blob: 0bac6a15a5c486396ff0031bb2df1ddc51e13d44 [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"
"path"
"strings"
"sync"
"unsafe"
"syscall/zx"
"syscall/zx/fdio"
"syscall/zx/fidl"
fidlIo "syscall/zx/io"
)
var (
Stdin = 0
Stdout = 1
Stderr = 2
)
const (
FsRightReadable = 0x00000001
FsRightWritable = 0x00000002
FsRightAdmin = 0x00000004
FsRightRight = 0x0000FFFF
FsFlagCreate = 0x00010000
FsFlagExclusive = 0x00020000
FsFlagTruncate = 0x00040000
FsFlagDirectory = 0x00080000
FsFlagAppend = 0x00100000
FsFlagNoRemote = 0x00200000
FsFlagPath = 0x00400000
FsFlagDescribe = 0x00800000
)
const (
O_RDONLY = 0x0
O_WRONLY = 0x1
O_RDWR = 0x2
O_PIPELINE = 0x80000000
// Flags which align with ZXIO_FS_*
O_ADMIN = FsRightAdmin
O_CREAT = FsFlagCreate
O_EXCL = FsFlagExclusive
O_TRUNC = FsFlagTruncate
O_DIRECTORY = FsFlagDirectory
O_APPEND = FsFlagAppend
O_NOREMOTE = FsFlagNoRemote
O_PATH = FsFlagPath
FdioAlignedFlags = O_ADMIN | O_CREAT | O_EXCL | O_TRUNC | O_DIRECTORY | O_APPEND |
O_NOREMOTE | O_PATH
// Flags which do not align with ZXIO_FS_*
O_NONBLOCK = 0x00000010
O_DSYNC = 0x00000020
O_SYNC = 0x00000040 | O_DSYNC
O_RSYNC = O_SYNC
O_NOFOLLOW = 0x00000080
O_CLOEXEC = 0x00000100
O_NOCTTY = 0x00000200
O_ASYNC = 0x00000400
O_DIRECT = 0x00000800
O_LARGEFILE = 0x00001000
O_NOATIME = 0x00002000
O_TMPFILE = 0x00004000
)
func FdioFlagsToZxio(flags uint32) (zflags uint32) {
perms := uint32(O_RDONLY | O_WRONLY | O_RDWR)
switch flags & perms {
case O_RDONLY:
zflags |= FsRightReadable
case O_WRONLY:
zflags |= FsRightWritable
case O_RDWR:
zflags |= FsRightReadable | FsRightWritable
}
if (flags & O_PIPELINE) == 0 {
zflags |= FsFlagDescribe
}
zflags |= (flags & FdioAlignedFlags)
return zflags
}
const (
S_IFMT = 0000170000
S_IFDIR = 0000040000
S_IFREG = 0000100000
S_IFIFO = 0000010000
)
// 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
)
const (
PathMax = 4096
)
type Stat_t struct {
Dev uint64
Ino uint64
Size uint64
CreateTime uint64
ModifyTime uint64
Mode uint64
}
func Getpid() (pid int) {
// TODO: is ProcHandle process-wide or OS-wide?
return int(zx.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) {
//zx.Sys_process_exit(int64(code))
//}
const ImplementsGetwd = true
func Getwd() (wd string, err error) {
cwdMu.Lock()
wd = cwdStr
cwdMu.Unlock()
return wd, nil
}
func Chdir(dir string) (err error) {
// TODO: if cgo enabled, change the working directory there too.
f, err := OpenPath(dir, 0, 0)
if err != nil {
return err
}
cwdMu.Lock()
var old fdio.FDIO
old, cwd = cwd, f
// cwd starts as a reference to root, which should remain open.
if old != nil && old != root {
defer old.Close()
}
if path.IsAbs(dir) {
cwdStr = path.Clean(dir)
} else {
cwdStr = path.Join(cwdStr, dir)
}
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) error {
return Unlink(path)
}
func Unlink(p string) error {
dir, relp := fdioPath(path.Clean(p))
return dir.Unlink(relp)
}
func makeAbs(paths ...string) []string {
cwdMu.Lock()
dir := cwdStr
cwdMu.Unlock()
for i := range paths {
if !path.IsAbs(paths[i]) {
paths[i] = path.Join(dir, paths[i])
}
}
return paths
}
func Rename(oldpath, newpath string) error {
abs := makeAbs(oldpath, newpath)
rootMu.Lock()
r := root
rootMu.Unlock()
return r.Rename(abs[0][1:], abs[1][1:])
}
func Link(oldpath, newpath string) error {
abs := makeAbs(oldpath, newpath)
rootMu.Lock()
r := root
rootMu.Unlock()
return r.Link(abs[0][1:], abs[1][1:])
}
func Fsync(fd int) error {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.Sync()
}
func Fchdir(fd int) (err error) {
return errors.New("Fchdir unimplemented")
}
func CloseOnExec(r int) {
panic("syscall.CloseOnExec TODO")
}
func SetNonblock(fd int, nonblocking bool) (err error) {
if nonblocking {
return errors.New("syscall.SetNonblock: non-blocking not supported on fuchsia")
}
return nil
}
var (
rootMu sync.Mutex
root fdio.FDIO
)
var (
cwdMu sync.Mutex
cwd fdio.FDIO
cwdStr string
)
var (
nsMu sync.Mutex
ns *fdio.NS
)
var (
stdioMu sync.Mutex
stdin fdio.FDIO
stdout fdio.FDIO
stderr fdio.FDIO
)
const fdsOff = 1e6 // move outside the fd range of unistd.c FDIO
var (
fdsMu sync.Mutex
fds []fdio.FDIO
)
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 = zx.EPIPE
var err error
nsMu.Lock()
ns, err = fdio.NewNSFromMap(zx.RootNSMap)
nsMu.Unlock()
if err != nil {
println("syscall: failed to create namespace: ", err.Error())
}
rootMu.Lock()
root, err = ns.OpenRoot()
rootMu.Unlock()
if err != nil {
println("syscall: failed to create root directory from namespace: ", err.Error())
}
cwdMu.Lock()
var found bool
cwdStr, found = Getenv("PWD")
if !found {
cwdStr = "/"
} else {
// Ensure the incoming cwd is absolute and cleaned
cwdStr = path.Join("/", cwdStr)
}
cwd = root
if cwdStr != "/" {
cwd, err = OpenPath(cwdStr, 0, 0)
}
cwdMu.Unlock()
if err != nil {
println("syscall: failed to create fdio cwd: ", err.Error())
}
initStdio := func(i int) (fdio.FDIO, error) {
switch zx.StdioHandleTypes[i] {
case 0:
return nil, EINVAL
case fdio.HandleTypeRemote, fdio.HandleTypeSocket, fdio.HandleTypeLogger, fdio.HandleTypeFileDescriptor:
info, err := zx.StdioHandles[i].GetInfoHandleBasic()
if err != nil {
println("syscall: GetInfoHandleBasic failed", err);
return nil, EINVAL
}
switch info.Type {
case zx.ObjectTypeChannel:
obj := (*fidlIo.NodeInterface)(&fidl.ChannelProxy{Channel: zx.Channel(zx.StdioHandles[i])})
return &fdio.File{Node: fdio.Node{obj}}, nil
case zx.ObjectTypeSocket:
return fdio.NewPipe(zx.Socket(zx.StdioHandles[i])), nil
case zx.ObjectTypeLog:
return fdio.NewLogger(zx.Log(zx.StdioHandles[i])), nil
default:
println("syscall: unknown object type for stdio: ", info.Type)
return nil, EINVAL
}
default:
println("syscall: unknown handle type for stdio: " + itoa(zx.StdioHandleTypes[i]))
return nil, EINVAL
}
}
stdioMu.Lock()
stdin, err = initStdio(0)
if err != nil {
println("syscall: failed to create fdio stdin: ", err.Error())
}
stdout, err = initStdio(1)
if err != nil {
println("syscall: failed to create fdio stdout: ", err.Error())
}
stderr, err = initStdio(2)
if err != nil {
println("syscall: failed to create fdio 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()
off, err = f.Seek(offset, whence)
if err != nil {
if status, isStatus := err.(zx.Error); isStatus {
if status.Status == zx.ErrNotSupported {
if _, isPipe := f.(*fdio.Pipe); isPipe {
return off, ESPIPE
}
}
}
}
return off, err
}
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 fdioPath(p string) (fdio.FDIO, string) {
if path.IsAbs(p) {
rootMu.Lock()
r := root
rootMu.Unlock()
return r, p[1:]
}
cwdMu.Lock()
c := cwd
cwdMu.Unlock()
return c, p
}
func OpenPath(p string, mode int, perm uint32) (f fdio.FDIO, err error) {
if strings.Contains(p, "\x00") {
return nil, EINVAL
}
dir, relp := fdioPath(path.Clean(p))
return dir.Open(relp, FdioFlagsToZxio(uint32(mode)), perm)
}
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 OpenFDIO(f), nil
}
func OpenAt(fdParent int, path string, mode int, perm uint32) (fd int, err error) {
parent := FDIOForFD(fdParent)
if parent == nil {
return -1, EBADF
}
f, err := parent.Open(path, FdioFlagsToZxio(uint32(mode)), perm)
if err != nil {
return -1, err
}
return OpenFDIO(f), nil
}
func FDIOForFD(fd int) fdio.FDIO {
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 OpenFDIO(f fdio.FDIO) (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, max uint64, in []byte, handles []zx.Handle) ([]byte, []zx.Handle, error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
return f.Ioctl(op, max, in, handles)
}
func Fstat(fd int, stat *Stat_t) (err error) {
fdsMu.Lock()
f := fds[fd-fdsOff]
fdsMu.Unlock()
attr, err := f.GetAttr()
if err != nil {
return err
}
stat.Dev = uint64(attr.Mode)
stat.Ino = attr.Id
stat.Size = attr.ContentSize
stat.CreateTime = attr.CreationTime
stat.ModifyTime = attr.ModificationTime
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()
return f.Truncate(uint64(length))
}
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
}
defer f.Close()
return f.SetAttr(fidlIo.NodeAttributeFlagModificationTime, fidlIo.NodeAttributes{
ModificationTime: uint64(TimespecToNsec(ts[1])),
})
}
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()
dirent, err := f.ReadDirents(uint64(len(buf)))
if err != nil {
return 0, err
}
return copy(buf, dirent), nil
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}
func direntReclen(buf []byte) (uint64, bool) {
if namelen, ok := direntNamlen(buf); ok {
return namelen + uint64(direntSize), true
} else {
return 0, false
}
}
func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Size), unsafe.Sizeof(Dirent{}.Size))
}
type Dirent struct {
Ino uint64
Size uint8
Type uint8
Name [1]byte
}
const direntSize = int(unsafe.Offsetof(Dirent{}.Name))