| // 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 syscall | |
| import ( | |
| "io" | |
| "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 io.SeekCurrent: | |
| offset += f.offset | |
| case io.SeekEnd: | |
| 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 | |
| } | |
| if uid != -1 { | |
| ip.Uid = uint32(uid) | |
| } | |
| if gid != -1 { | |
| 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() | |
| fs.mu.Lock() | |
| defer fs.mu.Unlock() | |
| 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 | |
| } | |
| _, _, err = fs.dirlookup(dp, elem) | |
| if err == nil { | |
| return EEXIST | |
| } | |
| fs.dirlink(dp, elem, ip) | |
| return nil | |
| } | |
| func Rename(from, to string) error { | |
| fsinit() | |
| fs.mu.Lock() | |
| defer fs.mu.Unlock() | |
| 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{} | |
| func openRandom() (devFile, error) { | |
| return randomFile{}, nil | |
| } | |
| func (f randomFile) close() error { | |
| return nil | |
| } | |
| func (f randomFile) pread(b []byte, offset int64) (int, error) { | |
| if err := naclGetRandomBytes(b); err != nil { | |
| return 0, err | |
| } | |
| return len(b), nil | |
| } | |
| 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() | |
| defer fs.mu.Unlock() | |
| f, err := fs.open(name, O_CREATE|O_EXCL, mode) | |
| if err != nil { | |
| if mode&S_IFMT == S_IFDIR { | |
| ip, _, err := fs.namei(name, false) | |
| if err == nil && (ip.Mode&S_IFMT) == S_IFDIR { | |
| return nil // directory already exists | |
| } | |
| } | |
| 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 | |
| } |