github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/src/syscall/fs_nacl.go (about)

     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  }