| // Copyright 2011 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. |
| |
| // Plan 9 system calls. |
| // This file is compiled as ordinary Go code, |
| // but it is also input to mksyscall, |
| // which parses the //sys lines and generates system call stubs. |
| // Note that sometimes we use a lowercase //sys name and |
| // wrap it in our own nicer implementation. |
| |
| package syscall |
| |
| import ( |
| "errors" |
| "internal/oserror" |
| "runtime" |
| "unsafe" |
| ) |
| |
| const ImplementsGetwd = true |
| const bitSize16 = 2 |
| |
| // ErrorString implements Error's String method by returning itself. |
| // |
| // ErrorString values can be tested against error values using errors.Is. |
| // For example: |
| // |
| // _, _, err := syscall.Syscall(...) |
| // if errors.Is(err, fs.ErrNotExist) ... |
| type ErrorString string |
| |
| func (e ErrorString) Error() string { return string(e) } |
| |
| // NewError converts s to an ErrorString, which satisfies the Error interface. |
| func NewError(s string) error { return ErrorString(s) } |
| |
| func (e ErrorString) Is(target error) bool { |
| switch target { |
| case oserror.ErrPermission: |
| return checkErrMessageContent(e, "permission denied") |
| case oserror.ErrExist: |
| return checkErrMessageContent(e, "exists", "is a directory") |
| case oserror.ErrNotExist: |
| return checkErrMessageContent(e, "does not exist", "not found", |
| "has been removed", "no parent") |
| case errors.ErrUnsupported: |
| return checkErrMessageContent(e, "not supported") |
| } |
| return false |
| } |
| |
| // checkErrMessageContent checks if err message contains one of msgs. |
| func checkErrMessageContent(e ErrorString, msgs ...string) bool { |
| for _, msg := range msgs { |
| if contains(string(e), msg) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // contains is a local version of strings.Contains. It knows len(sep) > 1. |
| func contains(s, sep string) bool { |
| n := len(sep) |
| c := sep[0] |
| for i := 0; i+n <= len(s); i++ { |
| if s[i] == c && s[i:i+n] == sep { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (e ErrorString) Temporary() bool { |
| return e == EINTR || e == EMFILE || e.Timeout() |
| } |
| |
| func (e ErrorString) Timeout() bool { |
| return e == EBUSY || e == ETIMEDOUT |
| } |
| |
| var emptystring string |
| |
| // A Note is a string describing a process note. |
| // It implements the os.Signal interface. |
| type Note string |
| |
| func (n Note) Signal() {} |
| |
| func (n Note) String() string { |
| return string(n) |
| } |
| |
| var ( |
| Stdin = 0 |
| Stdout = 1 |
| Stderr = 2 |
| ) |
| |
| // For testing: clients can set this flag to force |
| // creation of IPv6 sockets to return EAFNOSUPPORT. |
| var SocketDisableIPv6 bool |
| |
| func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err ErrorString) |
| func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err ErrorString) |
| func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) |
| func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) |
| |
| //go:nosplit |
| func atoi(b []byte) (n uint) { |
| n = 0 |
| for i := 0; i < len(b); i++ { |
| n = n*10 + uint(b[i]-'0') |
| } |
| return |
| } |
| |
| func cstring(s []byte) string { |
| for i := range s { |
| if s[i] == 0 { |
| return string(s[0:i]) |
| } |
| } |
| return string(s) |
| } |
| |
| func errstr() string { |
| var buf [ERRMAX]byte |
| |
| RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0) |
| |
| buf[len(buf)-1] = 0 |
| return cstring(buf[:]) |
| } |
| |
| func readnum(path string) (uint, error) { |
| var b [12]byte |
| |
| fd, e := Open(path, O_RDONLY) |
| if e != nil { |
| return 0, e |
| } |
| defer Close(fd) |
| |
| n, e := Pread(fd, b[:], 0) |
| |
| if e != nil { |
| return 0, e |
| } |
| |
| m := 0 |
| for ; m < n && b[m] == ' '; m++ { |
| } |
| |
| return atoi(b[m : n-1]), nil |
| } |
| |
| func Getpid() (pid int) { |
| n, _ := readnum("#c/pid") |
| return int(n) |
| } |
| |
| func Getppid() (ppid int) { |
| n, _ := readnum("#c/ppid") |
| return int(n) |
| } |
| |
| func Read(fd int, p []byte) (n int, err error) { |
| return Pread(fd, p, -1) |
| } |
| |
| func Write(fd int, p []byte) (n int, err error) { |
| if faketime && (fd == 1 || fd == 2) { |
| n = faketimeWrite(fd, p) |
| if n < 0 { |
| return 0, ErrorString("error") |
| } |
| return n, nil |
| } |
| |
| return Pwrite(fd, p, -1) |
| } |
| |
| var ioSync int64 |
| |
| //sys fd2path(fd int, buf []byte) (err error) |
| |
| func Fd2path(fd int) (path string, err error) { |
| var buf [512]byte |
| |
| e := fd2path(fd, buf[:]) |
| if e != nil { |
| return "", e |
| } |
| return cstring(buf[:]), nil |
| } |
| |
| //sys pipe(p *[2]int32) (err error) |
| |
| func Pipe(p []int) (err error) { |
| if len(p) != 2 { |
| return NewError("bad arg in system call") |
| } |
| var pp [2]int32 |
| err = pipe(&pp) |
| if err == nil { |
| p[0] = int(pp[0]) |
| p[1] = int(pp[1]) |
| } |
| return |
| } |
| |
| // Underlying system call writes to newoffset via pointer. |
| // Implemented in assembly to avoid allocation. |
| func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string) |
| |
| func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { |
| newoffset, e := seek(0, fd, offset, whence) |
| |
| if newoffset == -1 { |
| err = NewError(e) |
| } |
| return |
| } |
| |
| func Mkdir(path string, mode uint32) (err error) { |
| // If path exists and is not a directory, Create will fail silently. |
| // Work around this by rejecting Mkdir if path exists. |
| statbuf := make([]byte, bitSize16) |
| // Remove any trailing slashes from path, otherwise the Stat will |
| // fail with ENOTDIR. |
| n := len(path) |
| for n > 1 && path[n-1] == '/' { |
| n-- |
| } |
| _, err = Stat(path[0:n], statbuf) |
| if err == nil { |
| return EEXIST |
| } |
| |
| fd, err := Create(path, O_RDONLY, DMDIR|mode) |
| |
| if fd != -1 { |
| Close(fd) |
| } |
| |
| return |
| } |
| |
| type Waitmsg struct { |
| Pid int |
| Time [3]uint32 |
| Msg string |
| } |
| |
| func (w Waitmsg) Exited() bool { return true } |
| func (w Waitmsg) Signaled() bool { return false } |
| |
| func (w Waitmsg) ExitStatus() int { |
| if len(w.Msg) == 0 { |
| // a normal exit returns no message |
| return 0 |
| } |
| return 1 |
| } |
| |
| //sys await(s []byte) (n int, err error) |
| |
| func Await(w *Waitmsg) (err error) { |
| var buf [512]byte |
| var f [5][]byte |
| |
| n, err := await(buf[:]) |
| |
| if err != nil || w == nil { |
| return |
| } |
| |
| nf := 0 |
| p := 0 |
| for i := 0; i < n && nf < len(f)-1; i++ { |
| if buf[i] == ' ' { |
| f[nf] = buf[p:i] |
| p = i + 1 |
| nf++ |
| } |
| } |
| f[nf] = buf[p:] |
| nf++ |
| |
| if nf != len(f) { |
| return NewError("invalid wait message") |
| } |
| w.Pid = int(atoi(f[0])) |
| w.Time[0] = uint32(atoi(f[1])) |
| w.Time[1] = uint32(atoi(f[2])) |
| w.Time[2] = uint32(atoi(f[3])) |
| w.Msg = cstring(f[4]) |
| if w.Msg == "''" { |
| // await() returns '' for no error |
| w.Msg = "" |
| } |
| return |
| } |
| |
| func Unmount(name, old string) (err error) { |
| if fixwd(name, old) { |
| defer runtime.UnlockOSThread() |
| } |
| oldp, err := BytePtrFromString(old) |
| if err != nil { |
| return err |
| } |
| oldptr := uintptr(unsafe.Pointer(oldp)) |
| |
| var r0 uintptr |
| var e ErrorString |
| |
| // bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted. |
| if name == "" { |
| r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0) |
| } else { |
| namep, err := BytePtrFromString(name) |
| if err != nil { |
| return err |
| } |
| r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0) |
| } |
| |
| if int32(r0) == -1 { |
| err = e |
| } |
| return |
| } |
| |
| func Fchdir(fd int) (err error) { |
| path, err := Fd2path(fd) |
| |
| if err != nil { |
| return |
| } |
| |
| return Chdir(path) |
| } |
| |
| type Timespec struct { |
| Sec int32 |
| Nsec int32 |
| } |
| |
| type Timeval struct { |
| Sec int32 |
| Usec int32 |
| } |
| |
| func NsecToTimeval(nsec int64) (tv Timeval) { |
| nsec += 999 // round up to microsecond |
| tv.Usec = int32(nsec % 1e9 / 1e3) |
| tv.Sec = int32(nsec / 1e9) |
| return |
| } |
| |
| func nsec() int64 { |
| var scratch int64 |
| |
| r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0) |
| // TODO(aram): remove hack after I fix _nsec in the pc64 kernel. |
| if r0 == 0 { |
| return scratch |
| } |
| return int64(r0) |
| } |
| |
| func Gettimeofday(tv *Timeval) error { |
| nsec := nsec() |
| *tv = NsecToTimeval(nsec) |
| return nil |
| } |
| |
| func Getegid() (egid int) { return -1 } |
| func Geteuid() (euid int) { return -1 } |
| func Getgid() (gid int) { return -1 } |
| func Getuid() (uid int) { return -1 } |
| |
| func Getgroups() (gids []int, err error) { |
| return make([]int, 0), nil |
| } |
| |
| //sys open(path string, mode int) (fd int, err error) |
| |
| func Open(path string, mode int) (fd int, err error) { |
| if fixwd(path) { |
| defer runtime.UnlockOSThread() |
| } |
| return open(path, mode) |
| } |
| |
| //sys create(path string, mode int, perm uint32) (fd int, err error) |
| |
| func Create(path string, mode int, perm uint32) (fd int, err error) { |
| if fixwd(path) { |
| defer runtime.UnlockOSThread() |
| } |
| return create(path, mode, perm) |
| } |
| |
| //sys remove(path string) (err error) |
| |
| func Remove(path string) error { |
| if fixwd(path) { |
| defer runtime.UnlockOSThread() |
| } |
| return remove(path) |
| } |
| |
| //sys stat(path string, edir []byte) (n int, err error) |
| |
| func Stat(path string, edir []byte) (n int, err error) { |
| if fixwd(path) { |
| defer runtime.UnlockOSThread() |
| } |
| return stat(path, edir) |
| } |
| |
| //sys bind(name string, old string, flag int) (err error) |
| |
| func Bind(name string, old string, flag int) (err error) { |
| if fixwd(name, old) { |
| defer runtime.UnlockOSThread() |
| } |
| return bind(name, old, flag) |
| } |
| |
| //sys mount(fd int, afd int, old string, flag int, aname string) (err error) |
| |
| func Mount(fd int, afd int, old string, flag int, aname string) (err error) { |
| if fixwd(old) { |
| defer runtime.UnlockOSThread() |
| } |
| return mount(fd, afd, old, flag, aname) |
| } |
| |
| //sys wstat(path string, edir []byte) (err error) |
| |
| func Wstat(path string, edir []byte) (err error) { |
| if fixwd(path) { |
| defer runtime.UnlockOSThread() |
| } |
| return wstat(path, edir) |
| } |
| |
| //sys chdir(path string) (err error) |
| //sys Dup(oldfd int, newfd int) (fd int, err error) |
| //sys Pread(fd int, p []byte, offset int64) (n int, err error) |
| //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) |
| //sys Close(fd int) (err error) |
| //sys Fstat(fd int, edir []byte) (n int, err error) |
| //sys Fwstat(fd int, edir []byte) (err error) |