mirror of
https://github.com/golang/go
synced 2024-11-24 05:40:15 -07:00
044d2d5af6
And fix the nacl implementation. Fixes #24710 Change-Id: I31ffeea03a72dac5021ffb183fde31e9ffd060ad Reviewed-on: https://go-review.googlesource.com/106464 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
847 lines
17 KiB
Go
847 lines
17 KiB
Go
// 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
|
|
}
|