blob: ec0ddccaf2a62b10bdf219b7f91afe89fe14c318 [file] [log] [blame]
// Copyright 2016 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.
package syscall
import (
"errors"
"io"
"strings"
"sync"
"unsafe"
"syscall/mx"
"syscall/mx/mxio"
"syscall/mx/mxio/logger"
"syscall/mx/mxio/pipe"
"syscall/mx/mxio/rio"
)
var (
Stdin = 0
Stdout = 1
Stderr = 2
)
const (
O_RDONLY = 0x0
O_WRONLY = 0x1
O_RDWR = 0x2
O_CREAT = 00000100
O_EXCL = 00000200
O_NOCTTY = 00000400
O_TRUNC = 00001000
O_APPEND = 00002000
O_NONBLOCK = 00004000
O_DSYNC = 00010000
O_SYNC = 04010000
O_RSYNC = 04010000
O_DIRECTORY = 00200000
O_NOFOLLOW = 00400000
O_CLOEXEC = 02000000
O_NOREMOTE = 0100000000
)
const (
S_IFMT = 0000170000
S_IFDIR = 0000040000
S_IFREG = 0000100000
)
// TODO(crawshaw): generate a zerrors file once cgo is working
const (
EPERM = Errno(0x01)
ENOENT = Errno(0x02)
EINTR = Errno(0x04)
EIO = Errno(0x05)
EBADF = Errno(0x09)
EACCES = Errno(0x0d)
EEXIST = Errno(0x11)
ENOTDIR = Errno(0x14)
EISDIR = Errno(0x15)
EINVAL = Errno(0x16)
EMFILE = Errno(0x18)
ESPIPE = Errno(0x1d)
ENAMETOOLONG = Errno(0x24)
ENOTEMPTY = Errno(0x27)
ENOPROTOOPT = Errno(0x5c)
EOPNOTSUPP = Errno(0x5f)
ENOTSUP = EOPNOTSUPP
EADDRINUSE = Errno(0x62)
EADDRNOTAVAIL = Errno(0x63)
ETIMEDOUT = Errno(0x6e)
ECONNABORTED = Errno(0x67)
EHOSTUNREACH = Errno(0x71)
EFUCHSIA = Errno(0xfa)
)
var EPIPE error = Errno(0x20)
const (
SOMAXCONN = 0x80
)
type Stat_t struct {
Dev uint64
Ino uint64
Size uint64
CreateTime uint64
ModifyTime uint64
}
func Getpid() (pid int) {
// TODO: is ProcHandle process-wide or OS-wide?
return int(mx.ProcHandle)
}
func Getppid() (ppid int) {
return 0
}
func Getuid() (uid int) {
return 0
}
func Geteuid() (uid int) {
return 0
}
func Getgid() (uid int) {
return 0
}
func Getegid() (uid int) {
return 0
}
func Getgroups() (gids []int, err error) {
return nil, errors.New("Getgroups unimplemented")
}
func Exit(code int) {
mx.Sys_process_exit(code)
}
const ImplementsGetwd = false
func Getwd() (wd string, err error) {
return "", errors.New("Gwd unimplemented")
}
func Chdir(path string) (err error) {
// TODO: if cgo enabled, change the working directory there too.
f, err := OpenPath(path, 0, 0)
if err != nil {
return err
}
cwdMu.Lock()
cwd = f
cwdMu.Unlock()
return nil
}
func Mkdir(path string, mode uint32) (err error) {
f, err := OpenPath(path, O_CREAT|O_EXCL|O_RDWR, mode&0777|S_IFDIR)
if err != nil {
return err
}
f.Close()
return nil
}
func Rmdir(path string) (err error) {
return Unlink(path)
}
func Unlink(path string) (err error) {
dir, file := splitpath(path)
f, err := OpenPath(dir, 0, 0)
if err != nil {
return err
}
err = rio.UnlinkAt(f, file)
f.Close()
return err
}
func splitpath(path string) (dir, file string) {
i := len(path) - 1
for i >= 0 && path[i] != '/' {
i--
}
dir = path[:i+1]
file = path[i+1:]
return dir, file
}
func Rename(oldpath, newpath string) error {
return twoPathOp(oldpath, newpath, rio.OpRename)
}
func Link(oldpath, newpath string) error {
return twoPathOp(oldpath, newpath, rio.OpLink)
}
func twoPathOp(oldpath, newpath string, op uint32) (err error) {
if oldpath == "" || newpath == "" {
return EINVAL
}
olddir, oldfile := splitpath(oldpath)
newdir, newfile := splitpath(newpath)
old, err := OpenPath(olddir, O_DIRECTORY, 0)
if err != nil {
return err
}
defer old.Close()
new, err := OpenPath(newdir, O_DIRECTORY, 0)
if err != nil {
return err
}
defer new.Close()
token, err := new.Ioctl(mxio.IoctlVFSGetTokenFS, nil, nil)
if err != nil {
return err
}
buf := make([]byte, len(oldfile)+len(newfile)+2)
copy(buf, oldfile)
copy(buf[len(oldfile)+1:], newfile)
_, err = old.Misc(op, 0, buf, nil, token)
return err
}
func Fsync(fd int) (err error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
_, err = f.Misc(rio.OpSync, 0, nil, nil, nil)
return err
}
func Fchdir(fd int) (err error) {
return errors.New("Fchdir unimplemented")
}
func CloseOnExec(r int) {
panic("syscall.CloseOnExec TODO")
}
var (
rootMu sync.Mutex
root mxio.MXIO
)
var (
cwdMu sync.Mutex
cwd mxio.MXIO
)
var (
stdioMu sync.Mutex
stdin mxio.MXIO
stdout mxio.MXIO
stderr mxio.MXIO
)
const fdsOff = 1e6 // move outside the fd range of unistd.c MXIO
var (
fdsMu sync.Mutex
fds []mxio.MXIO
)
func Read(fd int, p []byte) (n int, err error) {
if fd == Stdin {
stdioMu.Lock()
n, err = stdin.Read(p)
stdioMu.Unlock()
} else {
if fd < fdsOff {
return 0, EINVAL
}
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
n, err = f.Read(p)
}
// An io.Reader can return n < len(p) and io.EOF.
// But this is the POSIX syscall read(3), which
// returns nil on eof.
if err == io.EOF {
err = nil
}
return n, err
}
func init() {
EPIPE = mx.EPIPE
var err error
rootMu.Lock()
root, err = rio.New([]mx.Handle{mx.RootHandle})
if err != nil {
println("syscall: failed to create mxio root: ", err.Error())
}
rootMu.Unlock()
cwdMu.Lock()
cwd, err = rio.New([]mx.Handle{mx.CwdHandle})
if err != nil {
println("syscall: failed to create mxio cwd: ", err.Error())
}
cwdMu.Unlock()
initStdio := func(i int) (mxio.MXIO, error) {
switch mx.StdioHandleTypes[i] {
case 0:
return nil, EINVAL
case mxio.HandleTypeRemote:
return rio.New(mx.StdioHandles[i][:])
case mxio.HandleTypePipe:
return pipe.New([]mx.Handle{mx.StdioHandles[i][0]})
case mxio.HandleTypeLogger:
return logger.New(mx.StdioHandles[i][0])
default:
println("syscall: unknown handle type for stdio: " + itoa(mx.StdioHandleTypes[i]))
return nil, EINVAL
}
}
stdioMu.Lock()
stdin, err = initStdio(0)
if err != nil {
println("syscall: failed to create mxio stdin: ", err.Error())
}
stdout, err = initStdio(1)
if err != nil {
println("syscall: failed to create mxio stdiout: ", err.Error())
}
stderr, err = initStdio(2)
if err != nil {
println("syscall: failed to create mxio stderr: ", err.Error())
}
stdioMu.Unlock()
}
func Write(fd int, p []byte) (n int, err error) {
switch fd {
case Stdout:
stdioMu.Lock()
n, err := stdout.Write(p)
stdioMu.Unlock()
return n, err
case Stderr:
stdioMu.Lock()
n, err := stderr.Write(p)
stdioMu.Unlock()
return n, err
default:
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.Write(p)
}
return 0, EINVAL
}
func Seek(fd int, offset int64, whence int) (off int64, err error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.Seek(offset, whence)
}
func Close(fd int) (err error) {
if fd < fdsOff {
return EINVAL
}
fdsMu.Lock()
if fd-fdsOff > len(fds) {
fdsMu.Unlock()
return EINVAL
}
f := fds[fd-fdsOff]
fds[fd-fdsOff] = nil
fdsMu.Unlock()
return f.Close()
}
func OpenPath(path string, mode int, perm uint32) (f mxio.MXIO, err error) {
if path == "" {
path = "."
}
if strings.Contains(path, "\x00") {
return nil, EINVAL
} else if path[0] != '/' {
cwdMu.Lock()
f, err = cwd.Open(path, int32(mode), perm)
cwdMu.Unlock()
} else {
rootMu.Lock()
f, err = root.Open(path, int32(mode), perm)
rootMu.Unlock()
}
return f, err
}
func Open(path string, mode int, perm uint32) (fd int, err error) {
if path == "" {
return -1, EINVAL
}
f, err := OpenPath(path, mode, perm)
if err != nil {
return -1, err
}
return OpenMXIO(f), nil
}
func OpenAt(fdParent int, path string, mode int, perm uint32) (fd int, err error) {
parent := MXIOForFD(fdParent)
if parent == nil {
return -1, EBADF
}
f, err := parent.Open(path, int32(mode), perm)
if err != nil {
return -1, err
}
return OpenMXIO(f), nil
}
func MXIOForFD(fd int) mxio.MXIO {
switch fd {
case Stdin:
return stdin
case Stdout:
return stdout
case Stderr:
return stderr
}
if fd < fdsOff {
return nil
}
fdsMu.Lock()
if fd-fdsOff > len(fds) {
fdsMu.Unlock()
return nil
}
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f
}
func OpenMXIO(f mxio.MXIO) (fd int) {
fdsMu.Lock()
i := -1
for i = 0; i < len(fds); i++ {
if fds[i] == nil {
fds[i] = f
break
}
}
if i == len(fds) {
fds = append(fds, f)
}
fdsMu.Unlock()
return i + fdsOff
}
func Ioctl(fd int, op uint32, in, out []byte) ([]mx.Handle, error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.Ioctl(op, in, out)
}
func IoctlSetHandle(fd int, op uint32, in mx.Handle) error {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.IoctlSetHandle(op, in)
}
func Fstat(fd int, stat *Stat_t) (err error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
attr, err := rio.Stat(f)
if err != nil {
return err
}
stat.Dev = uint64(attr.Mode)
stat.Ino = attr.Inode
stat.Size = attr.Size
stat.CreateTime = attr.CreateTime
stat.ModifyTime = attr.ModifyTime
return nil
}
func Stat(path string, stat *Stat_t) (err error) {
fd, err := Open(path, O_RDONLY, 0)
if err != nil {
return err
}
err = Fstat(fd, stat)
if err2 := Close(fd); err == nil {
err = err2
}
return err
}
func Lstat(path string, stat *Stat_t) (err error) {
// TODO: adjust when there are symlinks
return Stat(path, stat)
}
func Pread(fd int, p []byte, offset int64) (n int, err error) {
if fd == Stdout || fd == Stderr {
return 0, ESPIPE
}
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
// An io.Reader can return n < len(p) and io.EOF.
// But this is the POSIX syscall read(3), which
// returns nil on eof.
n, err = f.ReadAt(p, offset)
if err == io.EOF {
err = nil
}
return n, err
}
func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
if fd == Stdout || fd == Stderr {
return 0, ESPIPE
}
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.WriteAt(p, offset)
}
func Ftruncate(fd int, length int64) (err error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
_, err = f.Misc(rio.OpTruncate, length, nil, nil, nil)
if err != nil {
return err
}
return nil
}
func Truncate(path string, length int64) (err error) {
fd, err := Open(path, O_WRONLY, 0)
if err != nil {
return err
}
err = Ftruncate(fd, length)
if err2 := Close(fd); err == nil {
err = err2
}
return err
}
func Symlink(oldpath string, newpath string) (err error) {
return EOPNOTSUPP // no fuchsia support yet
}
func UtimesNano(path string, ts []Timespec) (err error) {
f, err := OpenPath(path, 0, 0)
if err != nil {
return err
}
vn := &mxio.Vnattr{
Valid: mxio.AttrMtime,
ModifyTime: uint64(TimespecToNsec(ts[1])),
}
_, err = f.Misc(rio.OpSetAttr, 0, (*[unsafe.Sizeof(mxio.Vnattr{})]byte)(unsafe.Pointer(vn))[:], nil, nil)
return err
}
func clen(n []byte) int {
for i := 0; i < len(n); i++ {
if n[i] == 0 {
return i
}
}
return len(n)
}
func ReadDirent(fd int, buf []byte) (n int, err error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.Misc(rio.OpReaddir, 0, nil, buf, nil)
}
func direntIno(buf []byte) (uint64, bool) {
// Inodes aren't exposed through Fuchsia; just lie.
// Use '1' instead of '0', since '0' means "entry not filled" on some operating systems.
return 1, true
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Size), unsafe.Sizeof(Dirent{}.Size))
}
func direntNamlen(buf []byte) (uint64, bool) {
reclen, ok := direntReclen(buf)
if !ok {
return 0, false
}
return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
}
type Dirent struct {
Size uint32
Type uint32
Name [1]byte
}