syscall_plan9.mx raw
1 // Copyright 2011 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 // Plan 9 system calls.
6 // This file is compiled as ordinary Go code,
7 // but it is also input to mksyscall,
8 // which parses the //sys lines and generates system call stubs.
9 // Note that sometimes we use a lowercase //sys name and
10 // wrap it in our own nicer implementation.
11
12 package syscall
13
14 import (
15 "errors"
16 "internal/oserror"
17 "runtime"
18 "unsafe"
19 )
20
21 const ImplementsGetwd = true
22 const bitSize16 = 2
23
24 // ErrorString implements Error's String method by returning itself.
25 //
26 // ErrorString values can be tested against error values using [errors.Is].
27 // For example:
28 //
29 // _, _, err := syscall.Syscall(...)
30 // if errors.Is(err, fs.ErrNotExist) ...
31 type ErrorString string
32
33 func (e ErrorString) Error() string { return string(e) }
34
35 // NewError converts s to an ErrorString, which satisfies the Error interface.
36 func NewError(s string) error { return ErrorString(s) }
37
38 func (e ErrorString) Is(target error) bool {
39 switch target {
40 case oserror.ErrPermission:
41 return checkErrMessageContent(e, "permission denied")
42 case oserror.ErrExist:
43 return checkErrMessageContent(e, "exists", "is a directory")
44 case oserror.ErrNotExist:
45 return checkErrMessageContent(e, "does not exist", "not found",
46 "has been removed", "no parent")
47 case errors.ErrUnsupported:
48 return checkErrMessageContent(e, "not supported")
49 }
50 return false
51 }
52
53 // checkErrMessageContent checks if err message contains one of msgs.
54 func checkErrMessageContent(e ErrorString, msgs ...string) bool {
55 for _, msg := range msgs {
56 if contains(string(e), msg) {
57 return true
58 }
59 }
60 return false
61 }
62
63 // contains is a local version of strings.Contains. It knows len(sep) > 1.
64 func contains(s, sep string) bool {
65 n := len(sep)
66 c := sep[0]
67 for i := 0; i+n <= len(s); i++ {
68 if s[i] == c && s[i:i+n] == sep {
69 return true
70 }
71 }
72 return false
73 }
74
75 func (e ErrorString) Temporary() bool {
76 return e == EINTR || e == EMFILE || e.Timeout()
77 }
78
79 func (e ErrorString) Timeout() bool {
80 return e == EBUSY || e == ETIMEDOUT
81 }
82
83 var emptystring string
84
85 // A Note is a string describing a process note.
86 // It implements the os.Signal interface.
87 type Note string
88
89 func (n Note) Signal() {}
90
91 func (n Note) String() string {
92 return string(n)
93 }
94
95 var (
96 Stdin = 0
97 Stdout = 1
98 Stderr = 2
99 )
100
101 // For testing: clients can set this flag to force
102 // creation of IPv6 sockets to return [EAFNOSUPPORT].
103 var SocketDisableIPv6 bool
104
105 func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err ErrorString)
106 func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err ErrorString)
107 func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
108 func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
109
110 //go:nosplit
111 func atoi(b []byte) (n uint) {
112 n = 0
113 for i := 0; i < len(b); i++ {
114 n = n*10 + uint(b[i]-'0')
115 }
116 return
117 }
118
119 func cstring(s []byte) string {
120 for i := range s {
121 if s[i] == 0 {
122 return string(s[0:i])
123 }
124 }
125 return string(s)
126 }
127
128 func errstr() string {
129 var buf [ERRMAX]byte
130
131 RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
132
133 buf[len(buf)-1] = 0
134 return cstring(buf[:])
135 }
136
137 func readnum(path string) (uint, error) {
138 var b [12]byte
139
140 fd, e := Open(path, O_RDONLY)
141 if e != nil {
142 return 0, e
143 }
144 defer Close(fd)
145
146 n, e := Pread(fd, b[:], 0)
147
148 if e != nil {
149 return 0, e
150 }
151
152 m := 0
153 for ; m < n && b[m] == ' '; m++ {
154 }
155
156 return atoi(b[m : n-1]), nil
157 }
158
159 func Getpid() (pid int) {
160 n, _ := readnum("#c/pid")
161 return int(n)
162 }
163
164 func Getppid() (ppid int) {
165 n, _ := readnum("#c/ppid")
166 return int(n)
167 }
168
169 func Read(fd int, p []byte) (n int, err error) {
170 return Pread(fd, p, -1)
171 }
172
173 func Write(fd int, p []byte) (n int, err error) {
174 if faketime && (fd == 1 || fd == 2) {
175 n = faketimeWrite(fd, p)
176 if n < 0 {
177 return 0, ErrorString("error")
178 }
179 return n, nil
180 }
181
182 return Pwrite(fd, p, -1)
183 }
184
185 var ioSync int64
186
187 //sys fd2path(fd int, buf []byte) (err error)
188
189 func Fd2path(fd int) (path string, err error) {
190 var buf [512]byte
191
192 e := fd2path(fd, buf[:])
193 if e != nil {
194 return "", e
195 }
196 return cstring(buf[:]), nil
197 }
198
199 //sys pipe(p *[2]int32) (err error)
200
201 func Pipe(p []int) (err error) {
202 if len(p) != 2 {
203 return NewError("bad arg in system call")
204 }
205 var pp [2]int32
206 err = pipe(&pp)
207 if err == nil {
208 p[0] = int(pp[0])
209 p[1] = int(pp[1])
210 }
211 return
212 }
213
214 // Underlying system call writes to newoffset via pointer.
215 // Implemented in assembly to avoid allocation.
216 func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
217
218 func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
219 newoffset, e := seek(0, fd, offset, whence)
220
221 if newoffset == -1 {
222 err = NewError(e)
223 }
224 return
225 }
226
227 func Mkdir(path string, mode uint32) (err error) {
228 // If path exists and is not a directory, Create will fail silently.
229 // Work around this by rejecting Mkdir if path exists.
230 statbuf := make([]byte, bitSize16)
231 // Remove any trailing slashes from path, otherwise the Stat will
232 // fail with ENOTDIR.
233 n := len(path)
234 for n > 1 && path[n-1] == '/' {
235 n--
236 }
237 _, err = Stat(path[0:n], statbuf)
238 if err == nil {
239 return EEXIST
240 }
241
242 fd, err := Create(path, O_RDONLY, DMDIR|mode)
243
244 if fd != -1 {
245 Close(fd)
246 }
247
248 return
249 }
250
251 type Waitmsg struct {
252 Pid int
253 Time [3]uint32
254 Msg string
255 }
256
257 func (w Waitmsg) Exited() bool { return true }
258 func (w Waitmsg) Signaled() bool { return false }
259
260 func (w Waitmsg) ExitStatus() int {
261 if len(w.Msg) == 0 {
262 // a normal exit returns no message
263 return 0
264 }
265 return 1
266 }
267
268 //sys await(s []byte) (n int, err error)
269
270 func Await(w *Waitmsg) (err error) {
271 var buf [512]byte
272 var f [5][]byte
273
274 n, err := await(buf[:])
275
276 if err != nil || w == nil {
277 return
278 }
279
280 nf := 0
281 p := 0
282 for i := 0; i < n && nf < len(f)-1; i++ {
283 if buf[i] == ' ' {
284 f[nf] = buf[p:i]
285 p = i + 1
286 nf++
287 }
288 }
289 f[nf] = buf[p:]
290 nf++
291
292 if nf != len(f) {
293 return NewError("invalid wait message")
294 }
295 w.Pid = int(atoi(f[0]))
296 w.Time[0] = uint32(atoi(f[1]))
297 w.Time[1] = uint32(atoi(f[2]))
298 w.Time[2] = uint32(atoi(f[3]))
299 w.Msg = cstring(f[4])
300 if w.Msg == "''" {
301 // await() returns '' for no error
302 w.Msg = ""
303 }
304 return
305 }
306
307 func Unmount(name, old string) (err error) {
308 if fixwd(name, old) {
309 defer runtime.UnlockOSThread()
310 }
311 oldp, err := BytePtrFromString(old)
312 if err != nil {
313 return err
314 }
315 oldptr := uintptr(unsafe.Pointer(oldp))
316
317 var r0 uintptr
318 var e ErrorString
319
320 // bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
321 if name == "" {
322 r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0)
323 } else {
324 namep, err := BytePtrFromString(name)
325 if err != nil {
326 return err
327 }
328 r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0)
329 }
330
331 if int32(r0) == -1 {
332 err = e
333 }
334 return
335 }
336
337 func Fchdir(fd int) (err error) {
338 path, err := Fd2path(fd)
339
340 if err != nil {
341 return
342 }
343
344 return Chdir(path)
345 }
346
347 type Timespec struct {
348 Sec int32
349 Nsec int32
350 }
351
352 type Timeval struct {
353 Sec int32
354 Usec int32
355 }
356
357 func NsecToTimeval(nsec int64) (tv Timeval) {
358 nsec += 999 // round up to microsecond
359 tv.Usec = int32(nsec % 1e9 / 1e3)
360 tv.Sec = int32(nsec / 1e9)
361 return
362 }
363
364 func nsec() int64 {
365 var scratch int64
366
367 r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
368 // TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
369 if r0 == 0 {
370 return scratch
371 }
372 return int64(r0)
373 }
374
375 func Gettimeofday(tv *Timeval) error {
376 nsec := nsec()
377 *tv = NsecToTimeval(nsec)
378 return nil
379 }
380
381 func Getegid() (egid int) { return -1 }
382 func Geteuid() (euid int) { return -1 }
383 func Getgid() (gid int) { return -1 }
384 func Getuid() (uid int) { return -1 }
385
386 func Getgroups() (gids []int, err error) {
387 return make([]int, 0), nil
388 }
389
390 //sys open(path string, mode int) (fd int, err error)
391
392 func Open(path string, mode int) (fd int, err error) {
393 if fixwd(path) {
394 defer runtime.UnlockOSThread()
395 }
396 return open(path, mode)
397 }
398
399 //sys create(path string, mode int, perm uint32) (fd int, err error)
400
401 func Create(path string, mode int, perm uint32) (fd int, err error) {
402 if fixwd(path) {
403 defer runtime.UnlockOSThread()
404 }
405 return create(path, mode, perm)
406 }
407
408 //sys remove(path string) (err error)
409
410 func Remove(path string) error {
411 if fixwd(path) {
412 defer runtime.UnlockOSThread()
413 }
414 return remove(path)
415 }
416
417 //sys stat(path string, edir []byte) (n int, err error)
418
419 func Stat(path string, edir []byte) (n int, err error) {
420 if fixwd(path) {
421 defer runtime.UnlockOSThread()
422 }
423 return stat(path, edir)
424 }
425
426 //sys bind(name string, old string, flag int) (err error)
427
428 func Bind(name string, old string, flag int) (err error) {
429 if fixwd(name, old) {
430 defer runtime.UnlockOSThread()
431 }
432 return bind(name, old, flag)
433 }
434
435 //sys mount(fd int, afd int, old string, flag int, aname string) (err error)
436
437 func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
438 if fixwd(old) {
439 defer runtime.UnlockOSThread()
440 }
441 return mount(fd, afd, old, flag, aname)
442 }
443
444 //sys wstat(path string, edir []byte) (err error)
445
446 func Wstat(path string, edir []byte) (err error) {
447 if fixwd(path) {
448 defer runtime.UnlockOSThread()
449 }
450 return wstat(path, edir)
451 }
452
453 //sys chdir(path string) (err error)
454 //sys Dup(oldfd int, newfd int) (fd int, err error)
455 //sys Pread(fd int, p []byte, offset int64) (n int, err error)
456 //sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
457 //sys Close(fd int) (err error)
458 //sys Fstat(fd int, edir []byte) (n int, err error)
459 //sys Fwstat(fd int, edir []byte) (err error)
460