Home | History | Annotate | Download | only in syscall
      1 // Copyright 2013 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // A simulated Unix-like file system for use within NaCl.
      6 //
      7 // The simulation is not particularly tied to NaCl other than the reuse
      8 // of NaCl's definition for the Stat_t structure.
      9 //
     10 // The file system need never be written to disk, so it is represented as
     11 // in-memory Go data structures, never in a serialized form.
     12 //
     13 // TODO: Perhaps support symlinks, although they muck everything up.
     14 
     15 package syscall
     16 
     17 import (
     18 	"sync"
     19 	"unsafe"
     20 )
     21 
     22 // Provided by package runtime.
     23 func now() (sec int64, nsec int32)
     24 
     25 // An fsys is a file system.
     26 // Since there is no I/O (everything is in memory),
     27 // the global lock mu protects the whole file system state,
     28 // and that's okay.
     29 type fsys struct {
     30 	mu   sync.Mutex
     31 	root *inode                    // root directory
     32 	cwd  *inode                    // process current directory
     33 	inum uint64                    // number of inodes created
     34 	dev  []func() (devFile, error) // table for opening devices
     35 }
     36 
     37 // A devFile is the implementation required of device files
     38 // like /dev/null or /dev/random.
     39 type devFile interface {
     40 	pread([]byte, int64) (int, error)
     41 	pwrite([]byte, int64) (int, error)
     42 }
     43 
     44 // An inode is a (possibly special) file in the file system.
     45 type inode struct {
     46 	Stat_t
     47 	data []byte
     48 	dir  []dirent
     49 }
     50 
     51 // A dirent describes a single directory entry.
     52 type dirent struct {
     53 	name  string
     54 	inode *inode
     55 }
     56 
     57 // An fsysFile is the fileImpl implementation backed by the file system.
     58 type fsysFile struct {
     59 	defaultFileImpl
     60 	fsys     *fsys
     61 	inode    *inode
     62 	openmode int
     63 	offset   int64
     64 	dev      devFile
     65 }
     66 
     67 // newFsys creates a new file system.
     68 func newFsys() *fsys {
     69 	fs := &fsys{}
     70 	fs.mu.Lock()
     71 	defer fs.mu.Unlock()
     72 	ip := fs.newInode()
     73 	ip.Mode = 0555 | S_IFDIR
     74 	fs.dirlink(ip, ".", ip)
     75 	fs.dirlink(ip, "..", ip)
     76 	fs.cwd = ip
     77 	fs.root = ip
     78 	return fs
     79 }
     80 
     81 var fs = newFsys()
     82 var fsinit = func() {}
     83 
     84 func init() {
     85 	// do not trigger loading of zipped file system here
     86 	oldFsinit := fsinit
     87 	defer func() { fsinit = oldFsinit }()
     88 	fsinit = func() {}
     89 	Mkdir("/dev", 0555)
     90 	Mkdir("/tmp", 0777)
     91 	mkdev("/dev/null", 0666, openNull)
     92 	mkdev("/dev/random", 0444, openRandom)
     93 	mkdev("/dev/urandom", 0444, openRandom)
     94 	mkdev("/dev/zero", 0666, openZero)
     95 	chdirEnv()
     96 }
     97 
     98 func chdirEnv() {
     99 	pwd, ok := Getenv("NACLPWD")
    100 	if ok {
    101 		chdir(pwd)
    102 	}
    103 }
    104 
    105 // Except where indicated otherwise, unexported methods on fsys
    106 // expect fs.mu to have been locked by the caller.
    107 
    108 // newInode creates a new inode.
    109 func (fs *fsys) newInode() *inode {
    110 	fs.inum++
    111 	ip := &inode{
    112 		Stat_t: Stat_t{
    113 			Ino:     fs.inum,
    114 			Blksize: 512,
    115 		},
    116 	}
    117 	return ip
    118 }
    119 
    120 // atime sets ip.Atime to the current time.
    121 func (fs *fsys) atime(ip *inode) {
    122 	sec, nsec := now()
    123 	ip.Atime, ip.AtimeNsec = sec, int64(nsec)
    124 }
    125 
    126 // mtime sets ip.Mtime to the current time.
    127 func (fs *fsys) mtime(ip *inode) {
    128 	sec, nsec := now()
    129 	ip.Mtime, ip.MtimeNsec = sec, int64(nsec)
    130 }
    131 
    132 // dirlookup looks for an entry in the directory dp with the given name.
    133 // It returns the directory entry and its index within the directory.
    134 func (fs *fsys) dirlookup(dp *inode, name string) (de *dirent, index int, err error) {
    135 	fs.atime(dp)
    136 	for i := range dp.dir {
    137 		de := &dp.dir[i]
    138 		if de.name == name {
    139 			fs.atime(de.inode)
    140 			return de, i, nil
    141 		}
    142 	}
    143 	return nil, 0, ENOENT
    144 }
    145 
    146 // dirlink adds to the directory dp an entry for name pointing at the inode ip.
    147 // If dp already contains an entry for name, that entry is overwritten.
    148 func (fs *fsys) dirlink(dp *inode, name string, ip *inode) {
    149 	fs.mtime(dp)
    150 	fs.atime(ip)
    151 	ip.Nlink++
    152 	for i := range dp.dir {
    153 		if dp.dir[i].name == name {
    154 			dp.dir[i] = dirent{name, ip}
    155 			return
    156 		}
    157 	}
    158 	dp.dir = append(dp.dir, dirent{name, ip})
    159 	dp.dirSize()
    160 }
    161 
    162 func (dp *inode) dirSize() {
    163 	dp.Size = int64(len(dp.dir)) * (8 + 8 + 2 + 256) // Dirent
    164 }
    165 
    166 // skipelem splits path into the first element and the remainder.
    167 // the returned first element contains no slashes, and the returned
    168 // remainder does not begin with a slash.
    169 func skipelem(path string) (elem, rest string) {
    170 	for len(path) > 0 && path[0] == '/' {
    171 		path = path[1:]
    172 	}
    173 	if len(path) == 0 {
    174 		return "", ""
    175 	}
    176 	i := 0
    177 	for i < len(path) && path[i] != '/' {
    178 		i++
    179 	}
    180 	elem, path = path[:i], path[i:]
    181 	for len(path) > 0 && path[0] == '/' {
    182 		path = path[1:]
    183 	}
    184 	return elem, path
    185 }
    186 
    187 // namei translates a file system path name into an inode.
    188 // If parent is false, the returned ip corresponds to the given name, and elem is the empty string.
    189 // If parent is true, the walk stops at the next-to-last element in the name,
    190 // so that ip is the parent directory and elem is the final element in the path.
    191 func (fs *fsys) namei(path string, parent bool) (ip *inode, elem string, err error) {
    192 	// Reject NUL in name.
    193 	for i := 0; i < len(path); i++ {
    194 		if path[i] == '\x00' {
    195 			return nil, "", EINVAL
    196 		}
    197 	}
    198 
    199 	// Reject empty name.
    200 	if path == "" {
    201 		return nil, "", EINVAL
    202 	}
    203 
    204 	if path[0] == '/' {
    205 		ip = fs.root
    206 	} else {
    207 		ip = fs.cwd
    208 	}
    209 
    210 	for len(path) > 0 && path[len(path)-1] == '/' {
    211 		path = path[:len(path)-1]
    212 	}
    213 
    214 	for {
    215 		elem, rest := skipelem(path)
    216 		if elem == "" {
    217 			if parent && ip.Mode&S_IFMT == S_IFDIR {
    218 				return ip, ".", nil
    219 			}
    220 			break
    221 		}
    222 		if ip.Mode&S_IFMT != S_IFDIR {
    223 			return nil, "", ENOTDIR
    224 		}
    225 		if len(elem) >= 256 {
    226 			return nil, "", ENAMETOOLONG
    227 		}
    228 		if parent && rest == "" {
    229 			// Stop one level early.
    230 			return ip, elem, nil
    231 		}
    232 		de, _, err := fs.dirlookup(ip, elem)
    233 		if err != nil {
    234 			return nil, "", err
    235 		}
    236 		ip = de.inode
    237 		path = rest
    238 	}
    239 	if parent {
    240 		return nil, "", ENOTDIR
    241 	}
    242 	return ip, "", nil
    243 }
    244 
    245 // open opens or creates a file with the given name, open mode,
    246 // and permission mode bits.
    247 func (fs *fsys) open(name string, openmode int, mode uint32) (fileImpl, error) {
    248 	dp, elem, err := fs.namei(name, true)
    249 	if err != nil {
    250 		return nil, err
    251 	}
    252 	var (
    253 		ip  *inode
    254 		dev devFile
    255 	)
    256 	de, _, err := fs.dirlookup(dp, elem)
    257 	if err != nil {
    258 		if openmode&O_CREATE == 0 {
    259 			return nil, err
    260 		}
    261 		ip = fs.newInode()
    262 		ip.Mode = mode
    263 		fs.dirlink(dp, elem, ip)
    264 		if ip.Mode&S_IFMT == S_IFDIR {
    265 			fs.dirlink(ip, ".", ip)
    266 			fs.dirlink(ip, "..", dp)
    267 		}
    268 	} else {
    269 		ip = de.inode
    270 		if openmode&(O_CREATE|O_EXCL) == O_CREATE|O_EXCL {
    271 			return nil, EEXIST
    272 		}
    273 		if openmode&O_TRUNC != 0 {
    274 			if ip.Mode&S_IFMT == S_IFDIR {
    275 				return nil, EISDIR
    276 			}
    277 			ip.data = nil
    278 		}
    279 		if ip.Mode&S_IFMT == S_IFCHR {
    280 			if ip.Rdev < 0 || ip.Rdev >= int64(len(fs.dev)) || fs.dev[ip.Rdev] == nil {
    281 				return nil, ENODEV
    282 			}
    283 			dev, err = fs.dev[ip.Rdev]()
    284 			if err != nil {
    285 				return nil, err
    286 			}
    287 		}
    288 	}
    289 
    290 	switch openmode & O_ACCMODE {
    291 	case O_WRONLY, O_RDWR:
    292 		if ip.Mode&S_IFMT == S_IFDIR {
    293 			return nil, EISDIR
    294 		}
    295 	}
    296 
    297 	switch ip.Mode & S_IFMT {
    298 	case S_IFDIR:
    299 		if openmode&O_ACCMODE != O_RDONLY {
    300 			return nil, EISDIR
    301 		}
    302 
    303 	case S_IFREG:
    304 		// ok
    305 
    306 	case S_IFCHR:
    307 		// handled above
    308 
    309 	default:
    310 		// TODO: some kind of special file
    311 		return nil, EPERM
    312 	}
    313 
    314 	f := &fsysFile{
    315 		fsys:     fs,
    316 		inode:    ip,
    317 		openmode: openmode,
    318 		dev:      dev,
    319 	}
    320 	if openmode&O_APPEND != 0 {
    321 		f.offset = ip.Size
    322 	}
    323 	return f, nil
    324 }
    325 
    326 // fsysFile methods to implement fileImpl.
    327 
    328 func (f *fsysFile) stat(st *Stat_t) error {
    329 	f.fsys.mu.Lock()
    330 	defer f.fsys.mu.Unlock()
    331 	*st = f.inode.Stat_t
    332 	return nil
    333 }
    334 
    335 func (f *fsysFile) read(b []byte) (int, error) {
    336 	f.fsys.mu.Lock()
    337 	defer f.fsys.mu.Unlock()
    338 	n, err := f.preadLocked(b, f.offset)
    339 	f.offset += int64(n)
    340 	return n, err
    341 }
    342 
    343 func ReadDirent(fd int, buf []byte) (int, error) {
    344 	f, err := fdToFsysFile(fd)
    345 	if err != nil {
    346 		return 0, err
    347 	}
    348 	f.fsys.mu.Lock()
    349 	defer f.fsys.mu.Unlock()
    350 	if f.inode.Mode&S_IFMT != S_IFDIR {
    351 		return 0, EINVAL
    352 	}
    353 	n, err := f.preadLocked(buf, f.offset)
    354 	f.offset += int64(n)
    355 	return n, err
    356 }
    357 
    358 func (f *fsysFile) write(b []byte) (int, error) {
    359 	f.fsys.mu.Lock()
    360 	defer f.fsys.mu.Unlock()
    361 	n, err := f.pwriteLocked(b, f.offset)
    362 	f.offset += int64(n)
    363 	return n, err
    364 }
    365 
    366 func (f *fsysFile) seek(offset int64, whence int) (int64, error) {
    367 	f.fsys.mu.Lock()
    368 	defer f.fsys.mu.Unlock()
    369 	switch whence {
    370 	case 1:
    371 		offset += f.offset
    372 	case 2:
    373 		offset += f.inode.Size
    374 	}
    375 	if offset < 0 {
    376 		return 0, EINVAL
    377 	}
    378 	if offset > f.inode.Size {
    379 		return 0, EINVAL
    380 	}
    381 	f.offset = offset
    382 	return offset, nil
    383 }
    384 
    385 func (f *fsysFile) pread(b []byte, offset int64) (int, error) {
    386 	f.fsys.mu.Lock()
    387 	defer f.fsys.mu.Unlock()
    388 	return f.preadLocked(b, offset)
    389 }
    390 
    391 func (f *fsysFile) pwrite(b []byte, offset int64) (int, error) {
    392 	f.fsys.mu.Lock()
    393 	defer f.fsys.mu.Unlock()
    394 	return f.pwriteLocked(b, offset)
    395 }
    396 
    397 func (f *fsysFile) preadLocked(b []byte, offset int64) (int, error) {
    398 	if f.openmode&O_ACCMODE == O_WRONLY {
    399 		return 0, EINVAL
    400 	}
    401 	if offset < 0 {
    402 		return 0, EINVAL
    403 	}
    404 	if f.dev != nil {
    405 		f.fsys.atime(f.inode)
    406 		f.fsys.mu.Unlock()
    407 		defer f.fsys.mu.Lock()
    408 		return f.dev.pread(b, offset)
    409 	}
    410 	if offset > f.inode.Size {
    411 		return 0, nil
    412 	}
    413 	if int64(len(b)) > f.inode.Size-offset {
    414 		b = b[:f.inode.Size-offset]
    415 	}
    416 
    417 	if f.inode.Mode&S_IFMT == S_IFDIR {
    418 		if offset%direntSize != 0 || len(b) != 0 && len(b) < direntSize {
    419 			return 0, EINVAL
    420 		}
    421 		fs.atime(f.inode)
    422 		n := 0
    423 		for len(b) >= direntSize {
    424 			src := f.inode.dir[int(offset/direntSize)]
    425 			dst := (*Dirent)(unsafe.Pointer(&b[0]))
    426 			dst.Ino = int64(src.inode.Ino)
    427 			dst.Off = offset
    428 			dst.Reclen = direntSize
    429 			for i := range dst.Name {
    430 				dst.Name[i] = 0
    431 			}
    432 			copy(dst.Name[:], src.name)
    433 			n += direntSize
    434 			offset += direntSize
    435 			b = b[direntSize:]
    436 		}
    437 		return n, nil
    438 	}
    439 
    440 	fs.atime(f.inode)
    441 	n := copy(b, f.inode.data[offset:])
    442 	return n, nil
    443 }
    444 
    445 func (f *fsysFile) pwriteLocked(b []byte, offset int64) (int, error) {
    446 	if f.openmode&O_ACCMODE == O_RDONLY {
    447 		return 0, EINVAL
    448 	}
    449 	if offset < 0 {
    450 		return 0, EINVAL
    451 	}
    452 	if f.dev != nil {
    453 		f.fsys.atime(f.inode)
    454 		f.fsys.mu.Unlock()
    455 		defer f.fsys.mu.Lock()
    456 		return f.dev.pwrite(b, offset)
    457 	}
    458 	if offset > f.inode.Size {
    459 		return 0, EINVAL
    460 	}
    461 	f.fsys.mtime(f.inode)
    462 	n := copy(f.inode.data[offset:], b)
    463 	if n < len(b) {
    464 		f.inode.data = append(f.inode.data, b[n:]...)
    465 		f.inode.Size = int64(len(f.inode.data))
    466 	}
    467 	return len(b), nil
    468 }
    469 
    470 // Standard Unix system calls.
    471 
    472 func Open(path string, openmode int, perm uint32) (fd int, err error) {
    473 	fsinit()
    474 	fs.mu.Lock()
    475 	defer fs.mu.Unlock()
    476 	f, err := fs.open(path, openmode, perm&0777|S_IFREG)
    477 	if err != nil {
    478 		return -1, err
    479 	}
    480 	return newFD(f), nil
    481 }
    482 
    483 func Mkdir(path string, perm uint32) error {
    484 	fs.mu.Lock()
    485 	defer fs.mu.Unlock()
    486 	_, err := fs.open(path, O_CREATE|O_EXCL, perm&0777|S_IFDIR)
    487 	return err
    488 }
    489 
    490 func Getcwd(buf []byte) (n int, err error) {
    491 	// Force package os to default to the old algorithm using .. and directory reads.
    492 	return 0, ENOSYS
    493 }
    494 
    495 func Stat(path string, st *Stat_t) error {
    496 	fsinit()
    497 	fs.mu.Lock()
    498 	defer fs.mu.Unlock()
    499 	ip, _, err := fs.namei(path, false)
    500 	if err != nil {
    501 		return err
    502 	}
    503 	*st = ip.Stat_t
    504 	return nil
    505 }
    506 
    507 func Lstat(path string, st *Stat_t) error {
    508 	return Stat(path, st)
    509 }
    510 
    511 func unlink(path string, isdir bool) error {
    512 	fsinit()
    513 	fs.mu.Lock()
    514 	defer fs.mu.Unlock()
    515 	dp, elem, err := fs.namei(path, true)
    516 	if err != nil {
    517 		return err
    518 	}
    519 	if elem == "." || elem == ".." {
    520 		return EINVAL
    521 	}
    522 	de, _, err := fs.dirlookup(dp, elem)
    523 	if err != nil {
    524 		return err
    525 	}
    526 	if isdir {
    527 		if de.inode.Mode&S_IFMT != S_IFDIR {
    528 			return ENOTDIR
    529 		}
    530 		if len(de.inode.dir) != 2 {
    531 			return ENOTEMPTY
    532 		}
    533 	} else {
    534 		if de.inode.Mode&S_IFMT == S_IFDIR {
    535 			return EISDIR
    536 		}
    537 	}
    538 	de.inode.Nlink--
    539 	*de = dp.dir[len(dp.dir)-1]
    540 	dp.dir = dp.dir[:len(dp.dir)-1]
    541 	dp.dirSize()
    542 	return nil
    543 }
    544 
    545 func Unlink(path string) error {
    546 	return unlink(path, false)
    547 }
    548 
    549 func Rmdir(path string) error {
    550 	return unlink(path, true)
    551 }
    552 
    553 func Chmod(path string, mode uint32) error {
    554 	fsinit()
    555 	fs.mu.Lock()
    556 	defer fs.mu.Unlock()
    557 	ip, _, err := fs.namei(path, false)
    558 	if err != nil {
    559 		return err
    560 	}
    561 	ip.Mode = ip.Mode&^0777 | mode&0777
    562 	return nil
    563 }
    564 
    565 func Fchmod(fd int, mode uint32) error {
    566 	f, err := fdToFsysFile(fd)
    567 	if err != nil {
    568 		return err
    569 	}
    570 	f.fsys.mu.Lock()
    571 	defer f.fsys.mu.Unlock()
    572 	f.inode.Mode = f.inode.Mode&^0777 | mode&0777
    573 	return nil
    574 }
    575 
    576 func Chown(path string, uid, gid int) error {
    577 	fsinit()
    578 	fs.mu.Lock()
    579 	defer fs.mu.Unlock()
    580 	ip, _, err := fs.namei(path, false)
    581 	if err != nil {
    582 		return err
    583 	}
    584 	ip.Uid = uint32(uid)
    585 	ip.Gid = uint32(gid)
    586 	return nil
    587 }
    588 
    589 func Fchown(fd int, uid, gid int) error {
    590 	fs.mu.Lock()
    591 	defer fs.mu.Unlock()
    592 	f, err := fdToFsysFile(fd)
    593 	if err != nil {
    594 		return err
    595 	}
    596 	f.fsys.mu.Lock()
    597 	defer f.fsys.mu.Unlock()
    598 	f.inode.Uid = uint32(uid)
    599 	f.inode.Gid = uint32(gid)
    600 	return nil
    601 }
    602 
    603 func Lchown(path string, uid, gid int) error {
    604 	return Chown(path, uid, gid)
    605 }
    606 
    607 func UtimesNano(path string, ts []Timespec) error {
    608 	if len(ts) != 2 {
    609 		return EINVAL
    610 	}
    611 	fsinit()
    612 	fs.mu.Lock()
    613 	defer fs.mu.Unlock()
    614 	ip, _, err := fs.namei(path, false)
    615 	if err != nil {
    616 		return err
    617 	}
    618 	ip.Atime = ts[0].Sec
    619 	ip.AtimeNsec = int64(ts[0].Nsec)
    620 	ip.Mtime = ts[1].Sec
    621 	ip.MtimeNsec = int64(ts[1].Nsec)
    622 	return nil
    623 }
    624 
    625 func Link(path, link string) error {
    626 	fsinit()
    627 	ip, _, err := fs.namei(path, false)
    628 	if err != nil {
    629 		return err
    630 	}
    631 	dp, elem, err := fs.namei(link, true)
    632 	if err != nil {
    633 		return err
    634 	}
    635 	if ip.Mode&S_IFMT == S_IFDIR {
    636 		return EPERM
    637 	}
    638 	fs.dirlink(dp, elem, ip)
    639 	return nil
    640 }
    641 
    642 func Rename(from, to string) error {
    643 	fsinit()
    644 	fdp, felem, err := fs.namei(from, true)
    645 	if err != nil {
    646 		return err
    647 	}
    648 	fde, _, err := fs.dirlookup(fdp, felem)
    649 	if err != nil {
    650 		return err
    651 	}
    652 	tdp, telem, err := fs.namei(to, true)
    653 	if err != nil {
    654 		return err
    655 	}
    656 	fs.dirlink(tdp, telem, fde.inode)
    657 	fde.inode.Nlink--
    658 	*fde = fdp.dir[len(fdp.dir)-1]
    659 	fdp.dir = fdp.dir[:len(fdp.dir)-1]
    660 	fdp.dirSize()
    661 	return nil
    662 }
    663 
    664 func (fs *fsys) truncate(ip *inode, length int64) error {
    665 	if length > 1e9 || ip.Mode&S_IFMT != S_IFREG {
    666 		return EINVAL
    667 	}
    668 	if length < int64(len(ip.data)) {
    669 		ip.data = ip.data[:length]
    670 	} else {
    671 		data := make([]byte, length)
    672 		copy(data, ip.data)
    673 		ip.data = data
    674 	}
    675 	ip.Size = int64(len(ip.data))
    676 	return nil
    677 }
    678 
    679 func Truncate(path string, length int64) error {
    680 	fsinit()
    681 	fs.mu.Lock()
    682 	defer fs.mu.Unlock()
    683 	ip, _, err := fs.namei(path, false)
    684 	if err != nil {
    685 		return err
    686 	}
    687 	return fs.truncate(ip, length)
    688 }
    689 
    690 func Ftruncate(fd int, length int64) error {
    691 	f, err := fdToFsysFile(fd)
    692 	if err != nil {
    693 		return err
    694 	}
    695 	f.fsys.mu.Lock()
    696 	defer f.fsys.mu.Unlock()
    697 	return f.fsys.truncate(f.inode, length)
    698 }
    699 
    700 func Chdir(path string) error {
    701 	fsinit()
    702 	return chdir(path)
    703 }
    704 
    705 func chdir(path string) error {
    706 	fs.mu.Lock()
    707 	defer fs.mu.Unlock()
    708 	ip, _, err := fs.namei(path, false)
    709 	if err != nil {
    710 		return err
    711 	}
    712 	fs.cwd = ip
    713 	return nil
    714 }
    715 
    716 func Fchdir(fd int) error {
    717 	f, err := fdToFsysFile(fd)
    718 	if err != nil {
    719 		return err
    720 	}
    721 	f.fsys.mu.Lock()
    722 	defer f.fsys.mu.Unlock()
    723 	if f.inode.Mode&S_IFMT != S_IFDIR {
    724 		return ENOTDIR
    725 	}
    726 	fs.cwd = f.inode
    727 	return nil
    728 }
    729 
    730 func Readlink(path string, buf []byte) (n int, err error) {
    731 	return 0, ENOSYS
    732 }
    733 
    734 func Symlink(path, link string) error {
    735 	return ENOSYS
    736 }
    737 
    738 func Fsync(fd int) error {
    739 	return nil
    740 }
    741 
    742 // Special devices.
    743 
    744 func mkdev(path string, mode uint32, open func() (devFile, error)) error {
    745 	f, err := fs.open(path, O_CREATE|O_RDONLY|O_EXCL, S_IFCHR|mode)
    746 	if err != nil {
    747 		return err
    748 	}
    749 	ip := f.(*fsysFile).inode
    750 	ip.Rdev = int64(len(fs.dev))
    751 	fs.dev = append(fs.dev, open)
    752 	return nil
    753 }
    754 
    755 type nullFile struct{}
    756 
    757 func openNull() (devFile, error)                               { return &nullFile{}, nil }
    758 func (f *nullFile) close() error                               { return nil }
    759 func (f *nullFile) pread(b []byte, offset int64) (int, error)  { return 0, nil }
    760 func (f *nullFile) pwrite(b []byte, offset int64) (int, error) { return len(b), nil }
    761 
    762 type zeroFile struct{}
    763 
    764 func openZero() (devFile, error)                               { return &zeroFile{}, nil }
    765 func (f *zeroFile) close() error                               { return nil }
    766 func (f *zeroFile) pwrite(b []byte, offset int64) (int, error) { return len(b), nil }
    767 
    768 func (f *zeroFile) pread(b []byte, offset int64) (int, error) {
    769 	for i := range b {
    770 		b[i] = 0
    771 	}
    772 	return len(b), nil
    773 }
    774 
    775 type randomFile struct{}
    776 
    777 func openRandom() (devFile, error) {
    778 	return randomFile{}, nil
    779 }
    780 
    781 func (f randomFile) close() error {
    782 	return nil
    783 }
    784 
    785 func (f randomFile) pread(b []byte, offset int64) (int, error) {
    786 	if err := naclGetRandomBytes(b); err != nil {
    787 		return 0, err
    788 	}
    789 	return len(b), nil
    790 }
    791 
    792 func (f randomFile) pwrite(b []byte, offset int64) (int, error) {
    793 	return 0, EPERM
    794 }
    795 
    796 func fdToFsysFile(fd int) (*fsysFile, error) {
    797 	f, err := fdToFile(fd)
    798 	if err != nil {
    799 		return nil, err
    800 	}
    801 	impl := f.impl
    802 	fsysf, ok := impl.(*fsysFile)
    803 	if !ok {
    804 		return nil, EINVAL
    805 	}
    806 	return fsysf, nil
    807 }
    808 
    809 // create creates a file in the file system with the given name, mode, time, and data.
    810 // It is meant to be called when initializing the file system image.
    811 func create(name string, mode uint32, sec int64, data []byte) error {
    812 	fs.mu.Lock()
    813 	defer fs.mu.Unlock()
    814 	f, err := fs.open(name, O_CREATE|O_EXCL, mode)
    815 	if err != nil {
    816 		if mode&S_IFMT == S_IFDIR {
    817 			ip, _, err := fs.namei(name, false)
    818 			if err == nil && (ip.Mode&S_IFMT) == S_IFDIR {
    819 				return nil // directory already exists
    820 			}
    821 		}
    822 		return err
    823 	}
    824 	ip := f.(*fsysFile).inode
    825 	ip.Atime = sec
    826 	ip.Mtime = sec
    827 	ip.Ctime = sec
    828 	if len(data) > 0 {
    829 		ip.Size = int64(len(data))
    830 		ip.data = data
    831 	}
    832 	return nil
    833 }
    834