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