| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- // Copyright 2013 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // A simulated Unix-like file system for use within NaCl.
- //
- // The simulation is not particularly tied to NaCl other than the reuse
- // of NaCl's definition for the Stat_t structure.
- //
- // The file system need never be written to disk, so it is represented as
- // in-memory Go data structures, never in a serialized form.
- //
- // TODO: Perhaps support symlinks, although they muck everything up.
- package unix
- import (
- "sync"
- "unsafe"
- )
- // Provided by package runtime.
- func now() (sec int64, nsec int32)
- // An fsys is a file system.
- // Since there is no I/O (everything is in memory),
- // the global lock mu protects the whole file system state,
- // and that's okay.
- type fsys struct {
- mu sync.Mutex
- root *inode // root directory
- cwd *inode // process current directory
- inum uint64 // number of inodes created
- dev []func() (devFile, error) // table for opening devices
- }
- // A devFile is the implementation required of device files
- // like /dev/null or /dev/random.
- type devFile interface {
- pread([]byte, int64) (int, error)
- pwrite([]byte, int64) (int, error)
- }
- // An inode is a (possibly special) file in the file system.
- type inode struct {
- Stat_t
- data []byte
- dir []dirent
- }
- // A dirent describes a single directory entry.
- type dirent struct {
- name string
- inode *inode
- }
- // An fsysFile is the fileImpl implementation backed by the file system.
- type fsysFile struct {
- defaultFileImpl
- fsys *fsys
- inode *inode
- openmode int
- offset int64
- dev devFile
- }
- // newFsys creates a new file system.
- func newFsys() *fsys {
- fs := &fsys{}
- fs.mu.Lock()
- defer fs.mu.Unlock()
- ip := fs.newInode()
- ip.Mode = 0555 | S_IFDIR
- fs.dirlink(ip, ".", ip)
- fs.dirlink(ip, "..", ip)
- fs.cwd = ip
- fs.root = ip
- return fs
- }
- var fs = newFsys()
- var fsinit = func() {}
- func init() {
- // do not trigger loading of zipped file system here
- oldFsinit := fsinit
- defer func() { fsinit = oldFsinit }()
- fsinit = func() {}
- Mkdir("/dev", 0555)
- Mkdir("/tmp", 0777)
- mkdev("/dev/null", 0666, openNull)
- mkdev("/dev/random", 0444, openRandom)
- mkdev("/dev/urandom", 0444, openRandom)
- mkdev("/dev/zero", 0666, openZero)
- chdirEnv()
- }
- func chdirEnv() {
- pwd, ok := Getenv("NACLPWD")
- if ok {
- chdir(pwd)
- }
- }
- // Except where indicated otherwise, unexported methods on fsys
- // expect fs.mu to have been locked by the caller.
- // newInode creates a new inode.
- func (fs *fsys) newInode() *inode {
- fs.inum++
- ip := &inode{
- Stat_t: Stat_t{
- Ino: fs.inum,
- Blksize: 512,
- },
- }
- return ip
- }
- // atime sets ip.Atime to the current time.
- func (fs *fsys) atime(ip *inode) {
- sec, nsec := now()
- ip.Atime, ip.AtimeNsec = sec, int64(nsec)
- }
- // mtime sets ip.Mtime to the current time.
- func (fs *fsys) mtime(ip *inode) {
- sec, nsec := now()
- ip.Mtime, ip.MtimeNsec = sec, int64(nsec)
- }
- // dirlookup looks for an entry in the directory dp with the given name.
- // It returns the directory entry and its index within the directory.
- func (fs *fsys) dirlookup(dp *inode, name string) (de *dirent, index int, err error) {
- fs.atime(dp)
- for i := range dp.dir {
- de := &dp.dir[i]
- if de.name == name {
- fs.atime(de.inode)
- return de, i, nil
- }
- }
- return nil, 0, ENOENT
- }
- // dirlink adds to the directory dp an entry for name pointing at the inode ip.
- // If dp already contains an entry for name, that entry is overwritten.
- func (fs *fsys) dirlink(dp *inode, name string, ip *inode) {
- fs.mtime(dp)
- fs.atime(ip)
- ip.Nlink++
- for i := range dp.dir {
- if dp.dir[i].name == name {
- dp.dir[i] = dirent{name, ip}
- return
- }
- }
- dp.dir = append(dp.dir, dirent{name, ip})
- dp.dirSize()
- }
- func (dp *inode) dirSize() {
- dp.Size = int64(len(dp.dir)) * (8 + 8 + 2 + 256) // Dirent
- }
- // skipelem splits path into the first element and the remainder.
- // the returned first element contains no slashes, and the returned
- // remainder does not begin with a slash.
- func skipelem(path string) (elem, rest string) {
- for len(path) > 0 && path[0] == '/' {
- path = path[1:]
- }
- if len(path) == 0 {
- return "", ""
- }
- i := 0
- for i < len(path) && path[i] != '/' {
- i++
- }
- elem, path = path[:i], path[i:]
- for len(path) > 0 && path[0] == '/' {
- path = path[1:]
- }
- return elem, path
- }
- // namei translates a file system path name into an inode.
- // If parent is false, the returned ip corresponds to the given name, and elem is the empty string.
- // If parent is true, the walk stops at the next-to-last element in the name,
- // so that ip is the parent directory and elem is the final element in the path.
- func (fs *fsys) namei(path string, parent bool) (ip *inode, elem string, err error) {
- // Reject NUL in name.
- for i := 0; i < len(path); i++ {
- if path[i] == '\x00' {
- return nil, "", EINVAL
- }
- }
- // Reject empty name.
- if path == "" {
- return nil, "", EINVAL
- }
- if path[0] == '/' {
- ip = fs.root
- } else {
- ip = fs.cwd
- }
- for len(path) > 0 && path[len(path)-1] == '/' {
- path = path[:len(path)-1]
- }
- for {
- elem, rest := skipelem(path)
- if elem == "" {
- if parent && ip.Mode&S_IFMT == S_IFDIR {
- return ip, ".", nil
- }
- break
- }
- if ip.Mode&S_IFMT != S_IFDIR {
- return nil, "", ENOTDIR
- }
- if len(elem) >= 256 {
- return nil, "", ENAMETOOLONG
- }
- if parent && rest == "" {
- // Stop one level early.
- return ip, elem, nil
- }
- de, _, err := fs.dirlookup(ip, elem)
- if err != nil {
- return nil, "", err
- }
- ip = de.inode
- path = rest
- }
- if parent {
- return nil, "", ENOTDIR
- }
- return ip, "", nil
- }
- // open opens or creates a file with the given name, open mode,
- // and permission mode bits.
- func (fs *fsys) open(name string, openmode int, mode uint32) (fileImpl, error) {
- dp, elem, err := fs.namei(name, true)
- if err != nil {
- return nil, err
- }
- var (
- ip *inode
- dev devFile
- )
- de, _, err := fs.dirlookup(dp, elem)
- if err != nil {
- if openmode&O_CREATE == 0 {
- return nil, err
- }
- ip = fs.newInode()
- ip.Mode = mode
- fs.dirlink(dp, elem, ip)
- if ip.Mode&S_IFMT == S_IFDIR {
- fs.dirlink(ip, ".", ip)
- fs.dirlink(ip, "..", dp)
- }
- } else {
- ip = de.inode
- if openmode&(O_CREATE|O_EXCL) == O_CREATE|O_EXCL {
- return nil, EEXIST
- }
- if openmode&O_TRUNC != 0 {
- if ip.Mode&S_IFMT == S_IFDIR {
- return nil, EISDIR
- }
- ip.data = nil
- }
- if ip.Mode&S_IFMT == S_IFCHR {
- if ip.Rdev < 0 || ip.Rdev >= int64(len(fs.dev)) || fs.dev[ip.Rdev] == nil {
- return nil, ENODEV
- }
- dev, err = fs.dev[ip.Rdev]()
- if err != nil {
- return nil, err
- }
- }
- }
- switch openmode & O_ACCMODE {
- case O_WRONLY, O_RDWR:
- if ip.Mode&S_IFMT == S_IFDIR {
- return nil, EISDIR
- }
- }
- switch ip.Mode & S_IFMT {
- case S_IFDIR:
- if openmode&O_ACCMODE != O_RDONLY {
- return nil, EISDIR
- }
- case S_IFREG:
- // ok
- case S_IFCHR:
- // handled above
- default:
- // TODO: some kind of special file
- return nil, EPERM
- }
- f := &fsysFile{
- fsys: fs,
- inode: ip,
- openmode: openmode,
- dev: dev,
- }
- if openmode&O_APPEND != 0 {
- f.offset = ip.Size
- }
- return f, nil
- }
- // fsysFile methods to implement fileImpl.
- func (f *fsysFile) stat(st *Stat_t) error {
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- *st = f.inode.Stat_t
- return nil
- }
- func (f *fsysFile) read(b []byte) (int, error) {
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- n, err := f.preadLocked(b, f.offset)
- f.offset += int64(n)
- return n, err
- }
- func ReadDirent(fd int, buf []byte) (int, error) {
- f, err := fdToFsysFile(fd)
- if err != nil {
- return 0, err
- }
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- if f.inode.Mode&S_IFMT != S_IFDIR {
- return 0, EINVAL
- }
- n, err := f.preadLocked(buf, f.offset)
- f.offset += int64(n)
- return n, err
- }
- func (f *fsysFile) write(b []byte) (int, error) {
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- n, err := f.pwriteLocked(b, f.offset)
- f.offset += int64(n)
- return n, err
- }
- func (f *fsysFile) seek(offset int64, whence int) (int64, error) {
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- switch whence {
- case 1:
- offset += f.offset
- case 2:
- offset += f.inode.Size
- }
- if offset < 0 {
- return 0, EINVAL
- }
- if offset > f.inode.Size {
- return 0, EINVAL
- }
- f.offset = offset
- return offset, nil
- }
- func (f *fsysFile) pread(b []byte, offset int64) (int, error) {
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- return f.preadLocked(b, offset)
- }
- func (f *fsysFile) pwrite(b []byte, offset int64) (int, error) {
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- return f.pwriteLocked(b, offset)
- }
- func (f *fsysFile) preadLocked(b []byte, offset int64) (int, error) {
- if f.openmode&O_ACCMODE == O_WRONLY {
- return 0, EINVAL
- }
- if offset < 0 {
- return 0, EINVAL
- }
- if f.dev != nil {
- f.fsys.atime(f.inode)
- f.fsys.mu.Unlock()
- defer f.fsys.mu.Lock()
- return f.dev.pread(b, offset)
- }
- if offset > f.inode.Size {
- return 0, nil
- }
- if int64(len(b)) > f.inode.Size-offset {
- b = b[:f.inode.Size-offset]
- }
- if f.inode.Mode&S_IFMT == S_IFDIR {
- if offset%direntSize != 0 || len(b) != 0 && len(b) < direntSize {
- return 0, EINVAL
- }
- fs.atime(f.inode)
- n := 0
- for len(b) >= direntSize {
- src := f.inode.dir[int(offset/direntSize)]
- dst := (*Dirent)(unsafe.Pointer(&b[0]))
- dst.Ino = int64(src.inode.Ino)
- dst.Off = offset
- dst.Reclen = direntSize
- for i := range dst.Name {
- dst.Name[i] = 0
- }
- copy(dst.Name[:], src.name)
- n += direntSize
- offset += direntSize
- b = b[direntSize:]
- }
- return n, nil
- }
- fs.atime(f.inode)
- n := copy(b, f.inode.data[offset:])
- return n, nil
- }
- func (f *fsysFile) pwriteLocked(b []byte, offset int64) (int, error) {
- if f.openmode&O_ACCMODE == O_RDONLY {
- return 0, EINVAL
- }
- if offset < 0 {
- return 0, EINVAL
- }
- if f.dev != nil {
- f.fsys.atime(f.inode)
- f.fsys.mu.Unlock()
- defer f.fsys.mu.Lock()
- return f.dev.pwrite(b, offset)
- }
- if offset > f.inode.Size {
- return 0, EINVAL
- }
- f.fsys.mtime(f.inode)
- n := copy(f.inode.data[offset:], b)
- if n < len(b) {
- f.inode.data = append(f.inode.data, b[n:]...)
- f.inode.Size = int64(len(f.inode.data))
- }
- return len(b), nil
- }
- // Standard Unix system calls.
- func Open(path string, openmode int, perm uint32) (fd int, err error) {
- fsinit()
- fs.mu.Lock()
- defer fs.mu.Unlock()
- f, err := fs.open(path, openmode, perm&0777|S_IFREG)
- if err != nil {
- return -1, err
- }
- return newFD(f), nil
- }
- func Mkdir(path string, perm uint32) error {
- fs.mu.Lock()
- defer fs.mu.Unlock()
- _, err := fs.open(path, O_CREATE|O_EXCL, perm&0777|S_IFDIR)
- return err
- }
- func Getcwd(buf []byte) (n int, err error) {
- // Force package os to default to the old algorithm using .. and directory reads.
- return 0, ENOSYS
- }
- func Stat(path string, st *Stat_t) error {
- fsinit()
- fs.mu.Lock()
- defer fs.mu.Unlock()
- ip, _, err := fs.namei(path, false)
- if err != nil {
- return err
- }
- *st = ip.Stat_t
- return nil
- }
- func Lstat(path string, st *Stat_t) error {
- return Stat(path, st)
- }
- func unlink(path string, isdir bool) error {
- fsinit()
- fs.mu.Lock()
- defer fs.mu.Unlock()
- dp, elem, err := fs.namei(path, true)
- if err != nil {
- return err
- }
- if elem == "." || elem == ".." {
- return EINVAL
- }
- de, _, err := fs.dirlookup(dp, elem)
- if err != nil {
- return err
- }
- if isdir {
- if de.inode.Mode&S_IFMT != S_IFDIR {
- return ENOTDIR
- }
- if len(de.inode.dir) != 2 {
- return ENOTEMPTY
- }
- } else {
- if de.inode.Mode&S_IFMT == S_IFDIR {
- return EISDIR
- }
- }
- de.inode.Nlink--
- *de = dp.dir[len(dp.dir)-1]
- dp.dir = dp.dir[:len(dp.dir)-1]
- dp.dirSize()
- return nil
- }
- func Unlink(path string) error {
- return unlink(path, false)
- }
- func Rmdir(path string) error {
- return unlink(path, true)
- }
- func Chmod(path string, mode uint32) error {
- fsinit()
- fs.mu.Lock()
- defer fs.mu.Unlock()
- ip, _, err := fs.namei(path, false)
- if err != nil {
- return err
- }
- ip.Mode = ip.Mode&^0777 | mode&0777
- return nil
- }
- func Fchmod(fd int, mode uint32) error {
- f, err := fdToFsysFile(fd)
- if err != nil {
- return err
- }
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- f.inode.Mode = f.inode.Mode&^0777 | mode&0777
- return nil
- }
- func Chown(path string, uid, gid int) error {
- fsinit()
- fs.mu.Lock()
- defer fs.mu.Unlock()
- ip, _, err := fs.namei(path, false)
- if err != nil {
- return err
- }
- ip.Uid = uint32(uid)
- ip.Gid = uint32(gid)
- return nil
- }
- func Fchown(fd int, uid, gid int) error {
- fs.mu.Lock()
- defer fs.mu.Unlock()
- f, err := fdToFsysFile(fd)
- if err != nil {
- return err
- }
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- f.inode.Uid = uint32(uid)
- f.inode.Gid = uint32(gid)
- return nil
- }
- func Lchown(path string, uid, gid int) error {
- return Chown(path, uid, gid)
- }
- func UtimesNano(path string, ts []Timespec) error {
- if len(ts) != 2 {
- return EINVAL
- }
- fsinit()
- fs.mu.Lock()
- defer fs.mu.Unlock()
- ip, _, err := fs.namei(path, false)
- if err != nil {
- return err
- }
- ip.Atime = ts[0].Sec
- ip.AtimeNsec = int64(ts[0].Nsec)
- ip.Mtime = ts[1].Sec
- ip.MtimeNsec = int64(ts[1].Nsec)
- return nil
- }
- func Link(path, link string) error {
- fsinit()
- ip, _, err := fs.namei(path, false)
- if err != nil {
- return err
- }
- dp, elem, err := fs.namei(link, true)
- if err != nil {
- return err
- }
- if ip.Mode&S_IFMT == S_IFDIR {
- return EPERM
- }
- fs.dirlink(dp, elem, ip)
- return nil
- }
- func Rename(from, to string) error {
- fsinit()
- fdp, felem, err := fs.namei(from, true)
- if err != nil {
- return err
- }
- fde, _, err := fs.dirlookup(fdp, felem)
- if err != nil {
- return err
- }
- tdp, telem, err := fs.namei(to, true)
- if err != nil {
- return err
- }
- fs.dirlink(tdp, telem, fde.inode)
- fde.inode.Nlink--
- *fde = fdp.dir[len(fdp.dir)-1]
- fdp.dir = fdp.dir[:len(fdp.dir)-1]
- fdp.dirSize()
- return nil
- }
- func (fs *fsys) truncate(ip *inode, length int64) error {
- if length > 1e9 || ip.Mode&S_IFMT != S_IFREG {
- return EINVAL
- }
- if length < int64(len(ip.data)) {
- ip.data = ip.data[:length]
- } else {
- data := make([]byte, length)
- copy(data, ip.data)
- ip.data = data
- }
- ip.Size = int64(len(ip.data))
- return nil
- }
- func Truncate(path string, length int64) error {
- fsinit()
- fs.mu.Lock()
- defer fs.mu.Unlock()
- ip, _, err := fs.namei(path, false)
- if err != nil {
- return err
- }
- return fs.truncate(ip, length)
- }
- func Ftruncate(fd int, length int64) error {
- f, err := fdToFsysFile(fd)
- if err != nil {
- return err
- }
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- return f.fsys.truncate(f.inode, length)
- }
- func Chdir(path string) error {
- fsinit()
- return chdir(path)
- }
- func chdir(path string) error {
- fs.mu.Lock()
- defer fs.mu.Unlock()
- ip, _, err := fs.namei(path, false)
- if err != nil {
- return err
- }
- fs.cwd = ip
- return nil
- }
- func Fchdir(fd int) error {
- f, err := fdToFsysFile(fd)
- if err != nil {
- return err
- }
- f.fsys.mu.Lock()
- defer f.fsys.mu.Unlock()
- if f.inode.Mode&S_IFMT != S_IFDIR {
- return ENOTDIR
- }
- fs.cwd = f.inode
- return nil
- }
- func Readlink(path string, buf []byte) (n int, err error) {
- return 0, ENOSYS
- }
- func Symlink(path, link string) error {
- return ENOSYS
- }
- func Fsync(fd int) error {
- return nil
- }
- // Special devices.
- func mkdev(path string, mode uint32, open func() (devFile, error)) error {
- f, err := fs.open(path, O_CREATE|O_RDONLY|O_EXCL, S_IFCHR|mode)
- if err != nil {
- return err
- }
- ip := f.(*fsysFile).inode
- ip.Rdev = int64(len(fs.dev))
- fs.dev = append(fs.dev, open)
- return nil
- }
- type nullFile struct{}
- func openNull() (devFile, error) { return &nullFile{}, nil }
- func (f *nullFile) close() error { return nil }
- func (f *nullFile) pread(b []byte, offset int64) (int, error) { return 0, nil }
- func (f *nullFile) pwrite(b []byte, offset int64) (int, error) { return len(b), nil }
- type zeroFile struct{}
- func openZero() (devFile, error) { return &zeroFile{}, nil }
- func (f *zeroFile) close() error { return nil }
- func (f *zeroFile) pwrite(b []byte, offset int64) (int, error) { return len(b), nil }
- func (f *zeroFile) pread(b []byte, offset int64) (int, error) {
- for i := range b {
- b[i] = 0
- }
- return len(b), nil
- }
- type randomFile struct {
- naclFD int
- }
- func openRandom() (devFile, error) {
- fd, err := openNamedService("SecureRandom", O_RDONLY)
- if err != nil {
- return nil, err
- }
- return &randomFile{naclFD: fd}, nil
- }
- func (f *randomFile) close() error {
- naclClose(f.naclFD)
- f.naclFD = -1
- return nil
- }
- func (f *randomFile) pread(b []byte, offset int64) (int, error) {
- return naclRead(f.naclFD, b)
- }
- func (f *randomFile) pwrite(b []byte, offset int64) (int, error) {
- return 0, EPERM
- }
- func fdToFsysFile(fd int) (*fsysFile, error) {
- f, err := fdToFile(fd)
- if err != nil {
- return nil, err
- }
- impl := f.impl
- fsysf, ok := impl.(*fsysFile)
- if !ok {
- return nil, EINVAL
- }
- return fsysf, nil
- }
- // create creates a file in the file system with the given name, mode, time, and data.
- // It is meant to be called when initializing the file system image.
- func create(name string, mode uint32, sec int64, data []byte) error {
- fs.mu.Lock()
- fs.mu.Unlock()
- f, err := fs.open(name, O_CREATE|O_EXCL, mode)
- if err != nil {
- return err
- }
- ip := f.(*fsysFile).inode
- ip.Atime = sec
- ip.Mtime = sec
- ip.Ctime = sec
- if len(data) > 0 {
- ip.Size = int64(len(data))
- ip.data = data
- }
- return nil
- }
|