1 //go:build darwin || (linux && !baremetal && !wasm_unknown && !nintendoswitch) || wasip1 || wasip2
2 3 // target wasi sets GOOS=linux and thus the +linux build tag,
4 // even though it doesn't show up in "moxie info target -wasi"
5 6 // Portions copyright 2009 The Go Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style
8 // license that can be found in the LICENSE file.
9 10 package os
11 12 import (
13 "io"
14 "syscall"
15 )
16 17 const DevNull = "/dev/null"
18 19 type syscallFd = int
20 21 // fixLongPath is a noop on non-Windows platforms.
22 func fixLongPath(path string) string {
23 return path
24 }
25 26 func rename(oldname, newname string) error {
27 // TODO: import rest of upstream tests, handle fancy cases
28 err := syscall.Rename(oldname, newname)
29 if err != nil {
30 return &LinkError{"rename", oldname, newname, err}
31 }
32 return nil
33 }
34 35 // file is the real representation of *File.
36 // The extra level of indirection ensures that no clients of os
37 // can overwrite this data, which could cause the finalizer
38 // to close the wrong file descriptor.
39 type file struct {
40 handle FileHandle
41 name string
42 dirinfo *dirInfo // nil unless directory being read
43 appendMode bool
44 }
45 46 func (f *file) close() (err error) {
47 if f.dirinfo != nil {
48 f.dirinfo.close()
49 f.dirinfo = nil
50 }
51 return f.handle.Close()
52 }
53 54 func NewFile(fd uintptr, name string) *File {
55 return &File{&file{handle: unixFileHandle(fd), name: name}}
56 }
57 58 // Truncate changes the size of the named file.
59 // If the file is a symbolic link, it changes the size of the link's target.
60 // If there is an error, it will be of type *PathError.
61 func Truncate(name string, size int64) error {
62 e := ignoringEINTR(func() error {
63 return syscall.Truncate(name, size)
64 })
65 if e != nil {
66 return &PathError{Op: "truncate", Path: name, Err: e}
67 }
68 return nil
69 }
70 71 func Pipe() (r *File, w *File, err error) {
72 var p [2]int
73 err = handleSyscallError(pipe(p[:]))
74 if err != nil {
75 return
76 }
77 r = NewFile(uintptr(p[0]), "|0")
78 w = NewFile(uintptr(p[1]), "|1")
79 return
80 }
81 82 func tempDir() string {
83 dir := Getenv("TMPDIR")
84 if dir == "" {
85 dir = "/tmp"
86 }
87 return dir
88 }
89 90 // Link creates newname as a hard link to the oldname file.
91 // If there is an error, it will be of type *LinkError.
92 func Link(oldname, newname string) error {
93 e := ignoringEINTR(func() error {
94 return syscall.Link(oldname, newname)
95 })
96 97 if e != nil {
98 return &LinkError{"link", oldname, newname, e}
99 }
100 return nil
101 }
102 103 // Symlink creates newname as a symbolic link to oldname.
104 // On Windows, a symlink to a non-existent oldname creates a file symlink;
105 // if oldname is later created as a directory the symlink will not work.
106 // If there is an error, it will be of type *LinkError.
107 func Symlink(oldname, newname string) error {
108 e := ignoringEINTR(func() error {
109 return syscall.Symlink(oldname, newname)
110 })
111 if e != nil {
112 return &LinkError{"symlink", oldname, newname, e}
113 }
114 return nil
115 }
116 117 // Readlink returns the destination of the named symbolic link.
118 // If there is an error, it will be of type *PathError.
119 func Readlink(name string) (string, error) {
120 for len := 128; ; len *= 2 {
121 b := make([]byte, len)
122 var (
123 n int
124 e error
125 )
126 for {
127 n, e = fixCount(syscall.Readlink(name, b))
128 if e != syscall.EINTR {
129 break
130 }
131 }
132 if e != nil {
133 return "", &PathError{Op: "readlink", Path: name, Err: e}
134 }
135 if n < len {
136 return string(b[0:n]), nil
137 }
138 }
139 }
140 141 // Truncate changes the size of the file.
142 // It does not change the I/O offset.
143 // If there is an error, it will be of type *PathError.
144 // Alternatively just use 'raw' syscall by file name
145 func (f *File) Truncate(size int64) (err error) {
146 if f.handle == nil {
147 return ErrClosed
148 }
149 150 return Truncate(f.name, size)
151 }
152 153 func (f *File) chmod(mode FileMode) error {
154 if f.handle == nil {
155 return ErrClosed
156 }
157 158 longName := fixLongPath(f.name)
159 e := ignoringEINTR(func() error {
160 return syscall.Chmod(longName, syscallMode(mode))
161 })
162 if e != nil {
163 return &PathError{Op: "chmod", Path: f.name, Err: e}
164 }
165 return nil
166 }
167 168 func (f *File) chdir() error {
169 if f.handle == nil {
170 return ErrClosed
171 }
172 173 // TODO: use syscall.Fchdir instead
174 longName := fixLongPath(f.name)
175 e := ignoringEINTR(func() error {
176 return syscall.Chdir(longName)
177 })
178 if e != nil {
179 return &PathError{Op: "chdir", Path: f.name, Err: e}
180 }
181 return nil
182 }
183 184 // ReadAt reads up to len(b) bytes from the File starting at the given absolute offset.
185 // It returns the number of bytes read and any error encountered, possibly io.EOF.
186 // At end of file, Pread returns 0, io.EOF.
187 // TODO: move to file_anyos once ReadAt is implemented for windows
188 func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
189 n, err = syscall.Pread(syscallFd(f), b, offset)
190 err = handleSyscallError(err)
191 if n == 0 && len(b) > 0 && err == nil {
192 err = io.EOF
193 }
194 return
195 }
196 197 // WriteAt writes len(b) bytes to the File starting at byte offset off.
198 // It returns the number of bytes written and an error, if any.
199 // WriteAt returns a non-nil error when n != len(b).
200 //
201 // If file was opened with the O_APPEND flag, WriteAt returns an error.
202 //
203 // TODO: move to file_anyos once WriteAt is implemented for windows.
204 func (f unixFileHandle) WriteAt(b []byte, offset int64) (int, error) {
205 n, err := syscall.Pwrite(syscallFd(f), b, offset)
206 return n, handleSyscallError(err)
207 }
208 209 // Seek wraps syscall.Seek.
210 func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) {
211 newoffset, err := syscall.Seek(syscallFd(f), offset, whence)
212 return newoffset, handleSyscallError(err)
213 }
214 215 func (f unixFileHandle) Sync() error {
216 err := syscall.Fsync(syscallFd(f))
217 return handleSyscallError(err)
218 }
219 220 type unixDirent struct {
221 parent string
222 name string
223 typ FileMode
224 info FileInfo
225 }
226 227 func (d *unixDirent) Name() string { return d.name }
228 func (d *unixDirent) IsDir() bool { return d.typ.IsDir() }
229 func (d *unixDirent) Type() FileMode { return d.typ }
230 231 func (d *unixDirent) Info() (FileInfo, error) {
232 if d.info != nil {
233 return d.info, nil
234 }
235 return lstat(d.parent + "/" + d.name)
236 }
237 238 func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) {
239 ude := &unixDirent{
240 parent: parent,
241 name: name,
242 typ: typ,
243 }
244 if typ != ^FileMode(0) && !testingForceReadDirLstat {
245 return ude, nil
246 }
247 248 info, err := lstat(parent + "/" + name)
249 if err != nil {
250 return nil, err
251 }
252 253 ude.typ = info.Mode().Type()
254 ude.info = info
255 return ude, nil
256 }
257 258 // Since internal/poll is not available, we need to stub this out.
259 // Big go requires the option to add the fd to the polling system.
260 //
261 //go:linkname net_newUnixFile net.newUnixFile
262 func net_newUnixFile(fd int, name string) *File {
263 if fd < 0 {
264 panic("invalid FD")
265 }
266 267 // see src/os/file_unix.go:162 newFile for the original implementation.
268 // return newFile(fd, name, kindSock, true)
269 return NewFile(uintptr(fd), name)
270 }
271