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