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
12package syscall
13
14import (
15	"errors"
16	"internal/oserror"
17	"runtime"
18	"unsafe"
19)
20
21const ImplementsGetwd = true
22const 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) ...
31type ErrorString string
32
33func (e ErrorString) Error() string { return string(e) }
34
35// NewError converts s to an ErrorString, which satisfies the Error interface.
36func NewError(s string) error { return ErrorString(s) }
37
38func (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.
54func 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.
64func 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
75func (e ErrorString) Temporary() bool {
76	return e == EINTR || e == EMFILE || e.Timeout()
77}
78
79func (e ErrorString) Timeout() bool {
80	return e == EBUSY || e == ETIMEDOUT
81}
82
83var emptystring string
84
85// A Note is a string describing a process note.
86// It implements the os.Signal interface.
87type Note string
88
89func (n Note) Signal() {}
90
91func (n Note) String() string {
92	return string(n)
93}
94
95var (
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].
103var SocketDisableIPv6 bool
104
105func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err ErrorString)
106func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err ErrorString)
107func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
108func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
109
110//go:nosplit
111func 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
119func 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
128func 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
137func 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
159func Getpid() (pid int) {
160	n, _ := readnum("#c/pid")
161	return int(n)
162}
163
164func Getppid() (ppid int) {
165	n, _ := readnum("#c/ppid")
166	return int(n)
167}
168
169func Read(fd int, p []byte) (n int, err error) {
170	return Pread(fd, p, -1)
171}
172
173func 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
185var ioSync int64
186
187//sys	fd2path(fd int, buf []byte) (err error)
188
189func 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
201func 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.
216func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
217
218func 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
227func 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
251type Waitmsg struct {
252	Pid  int
253	Time [3]uint32
254	Msg  string
255}
256
257func (w Waitmsg) Exited() bool   { return true }
258func (w Waitmsg) Signaled() bool { return false }
259
260func (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
270func 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
307func 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
337func 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
347type Timespec struct {
348	Sec  int32
349	Nsec int32
350}
351
352type Timeval struct {
353	Sec  int32
354	Usec int32
355}
356
357func 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
364func 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
375func Gettimeofday(tv *Timeval) error {
376	nsec := nsec()
377	*tv = NsecToTimeval(nsec)
378	return nil
379}
380
381func Getegid() (egid int) { return -1 }
382func Geteuid() (euid int) { return -1 }
383func Getgid() (gid int)   { return -1 }
384func Getuid() (uid int)   { return -1 }
385
386func 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
392func 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
401func 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
410func 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
419func 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
428func 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
437func 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
446func 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