github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/file.go (about) 1 // Portions copyright 2009 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 // This file was originally copied from Go, see: 6 // https://github.com/golang/go/blob/master/src/os/file.go 7 // 8 // Some of the code inherited from Go is not used anymore in Tinygo, but we keep 9 // changes to a minimum to help simplify bringing changes (e.g. the lstat global 10 // is not used here anymore, but we might need it if we add tests from Go in 11 // this package). 12 13 // Package os implements a subset of the Go "os" package. See 14 // https://godoc.org/os for details. 15 // 16 // Note that the current implementation is blocking. This limitation should be 17 // removed in a future version. 18 package os 19 20 import ( 21 "errors" 22 "io" 23 "io/fs" 24 "runtime" 25 "syscall" 26 ) 27 28 // Seek whence values. 29 // 30 // Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd. 31 const ( 32 SEEK_SET int = io.SeekStart 33 SEEK_CUR int = io.SeekCurrent 34 SEEK_END int = io.SeekEnd 35 ) 36 37 // lstat is overridden in tests. 38 var lstat = Lstat 39 40 // Mkdir creates a directory. If the operation fails, it will return an error of 41 // type *PathError. 42 func Mkdir(path string, perm FileMode) error { 43 fs, suffix := findMount(path) 44 if fs == nil { 45 return &PathError{Op: "mkdir", Path: path, Err: ErrNotExist} 46 } 47 err := fs.Mkdir(suffix, perm) 48 if err != nil { 49 return &PathError{Op: "mkdir", Path: path, Err: err} 50 } 51 return nil 52 } 53 54 // Many functions in package syscall return a count of -1 instead of 0. 55 // Using fixCount(call()) instead of call() corrects the count. 56 func fixCount(n int, err error) (int, error) { 57 if n < 0 { 58 n = 0 59 } 60 return n, err 61 } 62 63 // Remove removes a file or (empty) directory. If the operation fails, it will 64 // return an error of type *PathError. 65 func Remove(path string) error { 66 fs, suffix := findMount(path) 67 if fs == nil { 68 return &PathError{Op: "remove", Path: path, Err: ErrNotExist} 69 } 70 err := fs.Remove(suffix) 71 if err != nil { 72 return err 73 } 74 return nil 75 } 76 77 // Name returns the name of the file with which it was opened. 78 func (f *File) Name() string { 79 return f.name 80 } 81 82 // OpenFile opens the named file. If the operation fails, the returned error 83 // will be of type *PathError. 84 func OpenFile(name string, flag int, perm FileMode) (*File, error) { 85 fs, suffix := findMount(name) 86 if fs == nil { 87 return nil, &PathError{Op: "open", Path: name, Err: ErrNotExist} 88 } 89 handle, err := fs.OpenFile(suffix, flag, perm) 90 if err != nil { 91 return nil, &PathError{Op: "open", Path: name, Err: err} 92 } 93 f := NewFile(handle, name) 94 f.appendMode = (flag & O_APPEND) != 0 95 return f, nil 96 } 97 98 // Open opens the file named for reading. 99 func Open(name string) (*File, error) { 100 return OpenFile(name, O_RDONLY, 0) 101 } 102 103 // Create creates the named file, overwriting it if it already exists. 104 func Create(name string) (*File, error) { 105 return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) 106 } 107 108 // Read reads up to len(b) bytes from the File. It returns the number of bytes 109 // read and any error encountered. At end of file, Read returns 0, io.EOF. 110 func (f *File) Read(b []byte) (n int, err error) { 111 if f.handle == nil { 112 err = ErrClosed 113 } else { 114 n, err = f.handle.Read(b) 115 } 116 // TODO: want to always wrap, like upstream, but ReadFile() compares against exactly io.EOF? 117 if err != nil && err != io.EOF { 118 err = &PathError{Op: "read", Path: f.name, Err: err} 119 } 120 return 121 } 122 123 var errNegativeOffset = errors.New("negative offset") 124 125 // ReadAt reads up to len(b) bytes from the File at the given absolute offset. 126 // It returns the number of bytes read and any error encountered, possible io.EOF. 127 // At end of file, Read returns 0, io.EOF. 128 func (f *File) ReadAt(b []byte, offset int64) (n int, err error) { 129 if offset < 0 { 130 return 0, &PathError{Op: "readat", Path: f.name, Err: errNegativeOffset} 131 } 132 if f.handle == nil { 133 return 0, &PathError{Op: "readat", Path: f.name, Err: ErrClosed} 134 } 135 136 for len(b) > 0 { 137 m, e := f.handle.ReadAt(b, offset) 138 if e != nil { 139 // TODO: want to always wrap, like upstream, but TestReadAtEOF compares against exactly io.EOF? 140 if e != io.EOF { 141 err = &PathError{Op: "readat", Path: f.name, Err: e} 142 } else { 143 err = e 144 } 145 break 146 } 147 n += m 148 b = b[m:] 149 offset += int64(m) 150 } 151 152 return 153 } 154 155 // Write writes len(b) bytes to the File. It returns the number of bytes written 156 // and an error, if any. Write returns a non-nil error when n != len(b). 157 func (f *File) Write(b []byte) (n int, err error) { 158 if f.handle == nil { 159 err = ErrClosed 160 } else { 161 n, err = f.handle.Write(b) 162 } 163 if err != nil { 164 err = &PathError{Op: "write", Path: f.name, Err: err} 165 } 166 return 167 } 168 169 // WriteString is like Write, but writes the contents of string s rather than a 170 // slice of bytes. 171 func (f *File) WriteString(s string) (n int, err error) { 172 return f.Write([]byte(s)) 173 } 174 175 var errWriteAtInAppendMode = errors.New("os: invalid use of WriteAt on file opened with O_APPEND") 176 177 // WriteAt writes len(b) bytes to the File starting at byte offset off. 178 // It returns the number of bytes written and an error, if any. 179 // WriteAt returns a non-nil error when n != len(b). 180 // 181 // If file was opened with the O_APPEND flag, WriteAt returns an error. 182 func (f *File) WriteAt(b []byte, offset int64) (n int, err error) { 183 switch { 184 case offset < 0: 185 return 0, &PathError{Op: "writeat", Path: f.name, Err: errNegativeOffset} 186 case f.handle == nil: 187 return 0, &PathError{Op: "writeat", Path: f.name, Err: ErrClosed} 188 case f.appendMode: 189 // Go does not wrap this error but it would be more consistent 190 // if it did. 191 return 0, errWriteAtInAppendMode 192 } 193 for len(b) > 0 { 194 m, e := f.handle.WriteAt(b, offset) 195 if e != nil { 196 err = &PathError{Op: "writeat", Path: f.name, Err: e} 197 break 198 } 199 n += m 200 b = b[m:] 201 offset += int64(m) 202 } 203 return 204 } 205 206 // Close closes the File, rendering it unusable for I/O. 207 func (f *File) Close() (err error) { 208 if f.handle == nil { 209 err = ErrClosed 210 } else { 211 // Some platforms manage extra state other than the system handle which 212 // needs to be released when the file is closed. For example, darwin 213 // files have a DIR object holding a dup of the file descriptor, and 214 // linux files hold a buffer which needs to be released to a pool. 215 // 216 // These platform-specific logic is provided by the (*file).close method 217 // which is why we do not call the handle's Close method directly. 218 err = f.file.close() 219 if err == nil { 220 f.handle = nil 221 } 222 } 223 if err != nil { 224 err = &PathError{Op: "close", Path: f.name, Err: err} 225 } 226 return 227 } 228 229 // Seek sets the offset for the next Read or Write on file to offset, interpreted 230 // according to whence: 0 means relative to the origin of the file, 1 means 231 // relative to the current offset, and 2 means relative to the end. 232 // It returns the new offset and an error, if any. 233 // The behavior of Seek on a file opened with O_APPEND is not specified. 234 // 235 // If f is a directory, the behavior of Seek varies by operating 236 // system; you can seek to the beginning of the directory on Unix-like 237 // operating systems, but not on Windows. 238 func (f *File) Seek(offset int64, whence int) (ret int64, err error) { 239 if f.handle == nil { 240 err = ErrClosed 241 } else { 242 ret, err = f.handle.Seek(offset, whence) 243 } 244 if err != nil { 245 err = &PathError{Op: "seek", Path: f.name, Err: err} 246 } 247 return 248 } 249 250 func (f *File) SyscallConn() (conn syscall.RawConn, err error) { 251 if f.handle == nil { 252 err = ErrClosed 253 } else { 254 err = ErrNotImplemented 255 } 256 return 257 } 258 259 // fd is an internal interface that is used to try a type assertion in order to 260 // call the Fd() method of the underlying file handle if it is implemented. 261 type fd interface { 262 Fd() uintptr 263 } 264 265 // Fd returns the file handle referencing the open file. 266 func (f *File) Fd() uintptr { 267 handle, ok := f.handle.(fd) 268 if ok { 269 return handle.Fd() 270 } 271 return ^uintptr(0) 272 } 273 274 // Sync commits the current contents of the file to stable storage. 275 // Typically, this means flushing the file system's in-memory copy of recently 276 // written data to disk. 277 func (f *File) Sync() (err error) { 278 if f.handle == nil { 279 err = ErrClosed 280 } else { 281 err = f.handle.Sync() 282 } 283 return 284 } 285 286 // Truncate is a stub, not yet implemented 287 func (f *File) Truncate(size int64) (err error) { 288 if f.handle == nil { 289 err = ErrClosed 290 } else { 291 err = ErrNotImplemented 292 } 293 return &PathError{Op: "truncate", Path: f.name, Err: err} 294 } 295 296 // LinkError records an error during a link or symlink or rename system call and 297 // the paths that caused it. 298 type LinkError struct { 299 Op string 300 Old string 301 New string 302 Err error 303 } 304 305 func (e *LinkError) Error() string { 306 return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error() 307 } 308 309 func (e *LinkError) Unwrap() error { 310 return e.Err 311 } 312 313 const ( 314 O_RDONLY int = syscall.O_RDONLY 315 O_WRONLY int = syscall.O_WRONLY 316 O_RDWR int = syscall.O_RDWR 317 O_APPEND int = syscall.O_APPEND 318 O_CREATE int = syscall.O_CREAT 319 O_EXCL int = syscall.O_EXCL 320 O_SYNC int = syscall.O_SYNC 321 O_TRUNC int = syscall.O_TRUNC 322 ) 323 324 func Getwd() (string, error) { 325 return syscall.Getwd() 326 } 327 328 // TempDir returns the default directory to use for temporary files. 329 // 330 // On Unix systems, it returns $TMPDIR if non-empty, else /tmp. 331 // On Windows, it uses GetTempPath, returning the first non-empty 332 // value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory. 333 // 334 // The directory is neither guaranteed to exist nor have accessible 335 // permissions. 336 func TempDir() string { 337 return tempDir() 338 } 339 340 // UserHomeDir returns the current user's home directory. 341 // 342 // On Unix, including macOS, it returns the $HOME environment variable. 343 // On Windows, it returns %USERPROFILE%. 344 // On Plan 9, it returns the $home environment variable. 345 func UserHomeDir() (string, error) { 346 env, enverr := "HOME", "$HOME" 347 switch runtime.GOOS { 348 case "windows": 349 env, enverr = "USERPROFILE", "%userprofile%" 350 case "plan9": 351 env, enverr = "home", "$home" 352 } 353 if v := Getenv(env); v != "" { 354 return v, nil 355 } 356 // On some geese the home directory is not always defined. 357 switch runtime.GOOS { 358 case "android": 359 return "/sdcard", nil 360 case "ios": 361 return "/", nil 362 } 363 return "", errors.New(enverr + " is not defined") 364 } 365 366 type ( 367 FileMode = fs.FileMode 368 FileInfo = fs.FileInfo 369 ) 370 371 // The followings are copied from Go 1.16 or 1.17 official implementation: 372 // https://github.com/golang/go/blob/go1.16/src/os/file.go 373 374 // DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir. 375 // 376 // Note that DirFS("/prefix") only guarantees that the Open calls it makes to the 377 // operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the 378 // same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside 379 // the /prefix tree, then using DirFS does not stop the access any more than using 380 // os.Open does. DirFS is therefore not a general substitute for a chroot-style security 381 // mechanism when the directory tree contains arbitrary content. 382 func DirFS(dir string) fs.FS { 383 return dirFS(dir) 384 } 385 386 func containsAny(s, chars string) bool { 387 for i := 0; i < len(s); i++ { 388 for j := 0; j < len(chars); j++ { 389 if s[i] == chars[j] { 390 return true 391 } 392 } 393 } 394 return false 395 } 396 397 type dirFS string 398 399 func (dir dirFS) Open(name string) (fs.File, error) { 400 if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { 401 return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid} 402 } 403 f, err := Open(string(dir) + "/" + name) 404 if err != nil { 405 return nil, err // nil fs.File 406 } 407 return f, nil 408 } 409 410 func (dir dirFS) Stat(name string) (fs.FileInfo, error) { 411 if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { 412 return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid} 413 } 414 f, err := Stat(string(dir) + "/" + name) 415 if err != nil { 416 return nil, err 417 } 418 return f, nil 419 } 420 421 // ReadFile reads the named file and returns the contents. 422 // A successful call returns err == nil, not err == EOF. 423 // Because ReadFile reads the whole file, it does not treat an EOF from Read 424 // as an error to be reported. 425 func ReadFile(name string) ([]byte, error) { 426 f, err := Open(name) 427 if err != nil { 428 return nil, err 429 } 430 defer f.Close() 431 432 var size int 433 if info, err := f.Stat(); err == nil { 434 size64 := info.Size() 435 if int64(int(size64)) == size64 { 436 size = int(size64) 437 } 438 } 439 size++ // one byte for final read at EOF 440 441 // If a file claims a small size, read at least 512 bytes. 442 // In particular, files in Linux's /proc claim size 0 but 443 // then do not work right if read in small pieces, 444 // so an initial read of 1 byte would not work correctly. 445 if size < 512 { 446 size = 512 447 } 448 449 data := make([]byte, 0, size) 450 for { 451 if len(data) >= cap(data) { 452 d := append(data[:cap(data)], 0) 453 data = d[:len(data)] 454 } 455 n, err := f.Read(data[len(data):cap(data)]) 456 data = data[:len(data)+n] 457 if err != nil { 458 if err == io.EOF { 459 err = nil 460 } 461 return data, err 462 } 463 } 464 } 465 466 // WriteFile writes data to the named file, creating it if necessary. 467 // If the file does not exist, WriteFile creates it with permissions perm (before umask); 468 // otherwise WriteFile truncates it before writing, without changing permissions. 469 func WriteFile(name string, data []byte, perm FileMode) error { 470 f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm) 471 if err != nil { 472 return err 473 } 474 _, err = f.Write(data) 475 if err1 := f.Close(); err1 != nil && err == nil { 476 err = err1 477 } 478 return err 479 } 480 481 // The defined file mode bits are the most significant bits of the FileMode. 482 // The nine least-significant bits are the standard Unix rwxrwxrwx permissions. 483 // The values of these bits should be considered part of the public API and 484 // may be used in wire protocols or disk representations: they must not be 485 // changed, although new bits might be added. 486 const ( 487 // The single letters are the abbreviations 488 // used by the String method's formatting. 489 ModeDir = fs.ModeDir // d: is a directory 490 ModeAppend = fs.ModeAppend // a: append-only 491 ModeExclusive = fs.ModeExclusive // l: exclusive use 492 ModeTemporary = fs.ModeTemporary // T: temporary file; Plan 9 only 493 ModeSymlink = fs.ModeSymlink // L: symbolic link 494 ModeDevice = fs.ModeDevice // D: device file 495 ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO) 496 ModeSocket = fs.ModeSocket // S: Unix domain socket 497 ModeSetuid = fs.ModeSetuid // u: setuid 498 ModeSetgid = fs.ModeSetgid // g: setgid 499 ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set 500 ModeSticky = fs.ModeSticky // t: sticky 501 ModeIrregular = fs.ModeIrregular // ?: non-regular file; nothing else is known about this file 502 503 // Mask for the type bits. For regular files, none will be set. 504 ModeType = fs.ModeType 505 506 ModePerm = fs.ModePerm // Unix permission bits, 0o777 507 )