github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/file_anyos.go (about) 1 //go:build !baremetal && !js 2 3 // Portions copyright 2009 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 package os 8 9 import ( 10 "io" 11 "syscall" 12 ) 13 14 func init() { 15 // Mount the host filesystem at the root directory. This is what most 16 // programs will be expecting. 17 Mount("/", unixFilesystem{}) 18 } 19 20 // Stdin, Stdout, and Stderr are open Files pointing to the standard input, 21 // standard output, and standard error file descriptors. 22 var ( 23 Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin") 24 Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout") 25 Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr") 26 ) 27 28 // isOS indicates whether we're running on a real operating system with 29 // filesystem support. 30 const isOS = true 31 32 // Chdir changes the current working directory to the named directory. 33 // If there is an error, it will be of type *PathError. 34 func Chdir(dir string) error { 35 if e := syscall.Chdir(dir); e != nil { 36 return &PathError{Op: "chdir", Path: dir, Err: e} 37 } 38 return nil 39 } 40 41 // Rename renames (moves) oldpath to newpath. 42 // If newpath already exists and is not a directory, Rename replaces it. 43 // OS-specific restrictions may apply when oldpath and newpath are in different directories. 44 // If there is an error, it will be of type *LinkError. 45 func Rename(oldpath, newpath string) error { 46 return rename(oldpath, newpath) 47 } 48 49 // unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations 50 // are relative to the current working directory. 51 type unixFilesystem struct { 52 } 53 54 func (fs unixFilesystem) Mkdir(path string, perm FileMode) error { 55 return handleSyscallError(syscall.Mkdir(path, uint32(perm))) 56 } 57 58 func (fs unixFilesystem) Remove(path string) error { 59 // System call interface forces us to know 60 // whether name is a file or directory. 61 // Try both: it is cheaper on average than 62 // doing a Stat plus the right one. 63 e := handleSyscallError(syscall.Unlink(path)) 64 if e == nil { 65 return nil 66 } 67 e1 := handleSyscallError(syscall.Rmdir(path)) 68 if e1 == nil { 69 return nil 70 } 71 72 // Both failed: figure out which error to return. 73 // OS X and Linux differ on whether unlink(dir) 74 // returns EISDIR, so can't use that. However, 75 // both agree that rmdir(file) returns ENOTDIR, 76 // so we can use that to decide which error is real. 77 // Rmdir might also return ENOTDIR if given a bad 78 // file path, like /etc/passwd/foo, but in that case, 79 // both errors will be ENOTDIR, so it's okay to 80 // use the error from unlink. 81 if e1 != syscall.ENOTDIR { 82 e = e1 83 } 84 return &PathError{Op: "remove", Path: path, Err: e} 85 } 86 87 func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (uintptr, error) { 88 fp, err := syscall.Open(path, flag, uint32(perm)) 89 return uintptr(fp), handleSyscallError(err) 90 } 91 92 // unixFileHandle is a Unix file pointer with associated methods that implement 93 // the FileHandle interface. 94 type unixFileHandle uintptr 95 96 // Read reads up to len(b) bytes from the File. It returns the number of bytes 97 // read and any error encountered. At end of file, Read returns 0, io.EOF. 98 func (f unixFileHandle) Read(b []byte) (n int, err error) { 99 n, err = syscall.Read(syscallFd(f), b) 100 err = handleSyscallError(err) 101 if n == 0 && len(b) > 0 && err == nil { 102 err = io.EOF 103 } 104 return 105 } 106 107 // Write writes len(b) bytes to the File. It returns the number of bytes written 108 // and an error, if any. Write returns a non-nil error when n != len(b). 109 func (f unixFileHandle) Write(b []byte) (n int, err error) { 110 n, err = syscall.Write(syscallFd(f), b) 111 err = handleSyscallError(err) 112 return 113 } 114 115 // Close closes the File, rendering it unusable for I/O. 116 func (f unixFileHandle) Close() error { 117 return handleSyscallError(syscall.Close(syscallFd(f))) 118 } 119 120 func (f unixFileHandle) Fd() uintptr { 121 return uintptr(f) 122 } 123 124 // Chmod changes the mode of the named file to mode. 125 // If the file is a symbolic link, it changes the mode of the link's target. 126 // If there is an error, it will be of type *PathError. 127 // 128 // A different subset of the mode bits are used, depending on the 129 // operating system. 130 // 131 // On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and 132 // ModeSticky are used. 133 // 134 // On Windows, only the 0200 bit (owner writable) of mode is used; it 135 // controls whether the file's read-only attribute is set or cleared. 136 // The other bits are currently unused. For compatibility with Go 1.12 137 // and earlier, use a non-zero mode. Use mode 0400 for a read-only 138 // file and 0600 for a readable+writable file. 139 func Chmod(name string, mode FileMode) error { 140 longName := fixLongPath(name) 141 e := ignoringEINTR(func() error { 142 return syscall.Chmod(longName, syscallMode(mode)) 143 }) 144 if e != nil { 145 return &PathError{Op: "chmod", Path: name, Err: e} 146 } 147 return nil 148 } 149 150 // ignoringEINTR makes a function call and repeats it if it returns an 151 // EINTR error. This appears to be required even though we install all 152 // signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. 153 // Also #20400 and #36644 are issues in which a signal handler is 154 // installed without setting SA_RESTART. None of these are the common case, 155 // but there are enough of them that it seems that we can't avoid 156 // an EINTR loop. 157 func ignoringEINTR(fn func() error) error { 158 for { 159 err := fn() 160 if err != syscall.EINTR { 161 return err 162 } 163 } 164 } 165 166 // handleSyscallError converts syscall errors into regular os package errors. 167 // The err parameter must be either nil or of type syscall.Errno. 168 func handleSyscallError(err error) error { 169 if err == nil { 170 return nil 171 } 172 switch err.(syscall.Errno) { 173 case syscall.EEXIST: 174 return ErrExist 175 case syscall.ENOENT: 176 return ErrNotExist 177 default: 178 return err 179 } 180 } 181 182 // syscallMode returns the syscall-specific mode bits from Go's portable mode bits. 183 func syscallMode(i FileMode) (o uint32) { 184 o |= uint32(i.Perm()) 185 if i&ModeSetuid != 0 { 186 o |= syscall.S_ISUID 187 } 188 if i&ModeSetgid != 0 { 189 o |= syscall.S_ISGID 190 } 191 if i&ModeSticky != 0 { 192 o |= syscall.S_ISVTX 193 } 194 // No mapping for Go's ModeTemporary (plan9 only). 195 return 196 }