github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/file_unix.go (about) 1 //go:build darwin || (linux && !baremetal) || wasip1 2 3 // target wasi sets GOOS=linux and thus the +linux build tag, 4 // even though it doesn't show up in "tinygo 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 func Pipe() (r *File, w *File, err error) { 59 var p [2]int 60 err = handleSyscallError(syscall.Pipe2(p[:], syscall.O_CLOEXEC)) 61 if err != nil { 62 return 63 } 64 r = NewFile(uintptr(p[0]), "|0") 65 w = NewFile(uintptr(p[1]), "|1") 66 return 67 } 68 69 func tempDir() string { 70 dir := Getenv("TMPDIR") 71 if dir == "" { 72 dir = "/tmp" 73 } 74 return dir 75 } 76 77 // Symlink creates newname as a symbolic link to oldname. 78 // On Windows, a symlink to a non-existent oldname creates a file symlink; 79 // if oldname is later created as a directory the symlink will not work. 80 // If there is an error, it will be of type *LinkError. 81 func Symlink(oldname, newname string) error { 82 e := ignoringEINTR(func() error { 83 return syscall.Symlink(oldname, newname) 84 }) 85 if e != nil { 86 return &LinkError{"symlink", oldname, newname, e} 87 } 88 return nil 89 } 90 91 // Readlink returns the destination of the named symbolic link. 92 // If there is an error, it will be of type *PathError. 93 func Readlink(name string) (string, error) { 94 for len := 128; ; len *= 2 { 95 b := make([]byte, len) 96 var ( 97 n int 98 e error 99 ) 100 for { 101 n, e = fixCount(syscall.Readlink(name, b)) 102 if e != syscall.EINTR { 103 break 104 } 105 } 106 if e != nil { 107 return "", &PathError{Op: "readlink", Path: name, Err: e} 108 } 109 if n < len { 110 return string(b[0:n]), nil 111 } 112 } 113 } 114 115 // ReadAt reads up to len(b) bytes from the File starting at the given absolute offset. 116 // It returns the number of bytes read and any error encountered, possibly io.EOF. 117 // At end of file, Pread returns 0, io.EOF. 118 // TODO: move to file_anyos once ReadAt is implemented for windows 119 func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) { 120 n, err = syscall.Pread(syscallFd(f), b, offset) 121 err = handleSyscallError(err) 122 if n == 0 && len(b) > 0 && err == nil { 123 err = io.EOF 124 } 125 return 126 } 127 128 // WriteAt writes len(b) bytes to the File starting at byte offset off. 129 // It returns the number of bytes written and an error, if any. 130 // WriteAt returns a non-nil error when n != len(b). 131 // 132 // If file was opened with the O_APPEND flag, WriteAt returns an error. 133 // 134 // TODO: move to file_anyos once WriteAt is implemented for windows. 135 func (f unixFileHandle) WriteAt(b []byte, offset int64) (int, error) { 136 n, err := syscall.Pwrite(syscallFd(f), b, offset) 137 return n, handleSyscallError(err) 138 } 139 140 // Seek wraps syscall.Seek. 141 func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) { 142 newoffset, err := syscall.Seek(syscallFd(f), offset, whence) 143 return newoffset, handleSyscallError(err) 144 } 145 146 func (f unixFileHandle) Sync() error { 147 err := syscall.Fsync(syscallFd(f)) 148 return handleSyscallError(err) 149 } 150 151 type unixDirent struct { 152 parent string 153 name string 154 typ FileMode 155 info FileInfo 156 } 157 158 func (d *unixDirent) Name() string { return d.name } 159 func (d *unixDirent) IsDir() bool { return d.typ.IsDir() } 160 func (d *unixDirent) Type() FileMode { return d.typ } 161 162 func (d *unixDirent) Info() (FileInfo, error) { 163 if d.info != nil { 164 return d.info, nil 165 } 166 return lstat(d.parent + "/" + d.name) 167 } 168 169 func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) { 170 ude := &unixDirent{ 171 parent: parent, 172 name: name, 173 typ: typ, 174 } 175 if typ != ^FileMode(0) && !testingForceReadDirLstat { 176 return ude, nil 177 } 178 179 info, err := lstat(parent + "/" + name) 180 if err != nil { 181 return nil, err 182 } 183 184 ude.typ = info.Mode().Type() 185 ude.info = info 186 return ude, nil 187 }