github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/vfs/osfs/osfs.go (about) 1 // 2 // Copyright 2020 The AVFS authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 // Package osfs implements a file system using functions from os and path/filepath packages. 18 // 19 // Most functions are just calls to the original ones from os and filepath packages. 20 package osfs 21 22 import ( 23 "io/fs" 24 "os" 25 "path/filepath" 26 "time" 27 28 "github.com/avfs/avfs" 29 "github.com/avfs/avfs/idm/osidm" 30 ) 31 32 // file system functions. 33 34 // Abs returns an absolute representation of path. 35 // If the path is not absolute it will be joined with the current 36 // working directory to turn it into an absolute path. The absolute 37 // path name for a given file is not guaranteed to be unique. 38 // Abs calls Clean on the result. 39 func (vfs *OsFS) Abs(path string) (string, error) { 40 return filepath.Abs(path) 41 } 42 43 // Base returns the last element of path. 44 // Trailing path separators are removed before extracting the last element. 45 // If the path is empty, Base returns ".". 46 // If the path consists entirely of separators, Base returns a single separator. 47 func (vfs *OsFS) Base(path string) string { 48 return filepath.Base(path) 49 } 50 51 // Chdir changes the current working directory to the named directory. 52 // If there is an error, it will be of type *PathError. 53 func (vfs *OsFS) Chdir(dir string) error { 54 return os.Chdir(dir) 55 } 56 57 // Chmod changes the mode of the named file to mode. 58 // If the file is a symbolic link, it changes the mode of the link's target. 59 // If there is an error, it will be of type *PathError. 60 // 61 // A different subset of the mode bits are used, depending on the 62 // operating system. 63 // 64 // On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and 65 // ModeSticky are used. 66 // 67 // On Windows, only the 0200 bit (owner writable) of mode is used; it 68 // controls whether the file's read-only attribute is set or cleared. 69 // The other bits are currently unused. For compatibility with Go 1.12 70 // and earlier, use a non-zero mode. Use mode 0400 for a read-only 71 // file and 0600 for a readable+writable file. 72 // 73 // On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive, 74 // and ModeTemporary are used. 75 func (vfs *OsFS) Chmod(name string, mode fs.FileMode) error { 76 return os.Chmod(name, mode) 77 } 78 79 // Chown changes the numeric uid and gid of the named file. 80 // If the file is a symbolic link, it changes the uid and gid of the link's target. 81 // A uid or gid of -1 means to not change that value. 82 // If there is an error, it will be of type *PathError. 83 // 84 // On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or 85 // EPLAN9 error, wrapped in *PathError. 86 func (vfs *OsFS) Chown(name string, uid, gid int) error { 87 const op = "chown" 88 89 if !vfs.HasFeature(avfs.FeatIdentityMgr) && vfs.OSType() != avfs.OsWindows { 90 return &fs.PathError{Op: op, Path: name, Err: avfs.ErrOpNotPermitted} 91 } 92 93 return os.Chown(name, uid, gid) 94 } 95 96 // Chtimes changes the access and modification times of the named 97 // file, similar to the Unix utime() or utimes() functions. 98 // 99 // The underlying filesystem may truncate or round the values to a 100 // less precise time unit. 101 // If there is an error, it will be of type *PathError. 102 func (vfs *OsFS) Chtimes(name string, atime, mtime time.Time) error { 103 return os.Chtimes(name, atime, mtime) 104 } 105 106 // Clean returns the shortest path name equivalent to path 107 // by purely lexical processing. It applies the following rules 108 // iteratively until no further processing can be done: 109 // 110 // 1. Replace multiple Separator elements with a single one. 111 // 2. Eliminate each . path name element (the current directory). 112 // 3. Eliminate each inner .. path name element (the parent directory) 113 // along with the non-.. element that precedes it. 114 // 4. Eliminate .. elements that begin a rooted path: 115 // that is, replace "/.." by "/" at the beginning of a path, 116 // assuming Separator is '/'. 117 // 118 // The returned path ends in a slash only if it represents a root directory, 119 // such as "/" on Unix or `C:\` on Windows. 120 // 121 // Finally, any occurrences of slash are replaced by Separator. 122 // 123 // If the result of this process is an empty string, Clean 124 // returns the string ".". 125 // 126 // See also Rob Pike, “Lexical File Names in Plan 9 or 127 // Getting Dot-Dot Right,” 128 // https://9p.io/sys/doc/lexnames.html 129 func (vfs *OsFS) Clean(path string) string { 130 return filepath.Clean(path) 131 } 132 133 // Create creates or truncates the named file. If the file already exists, 134 // it is truncated. If the file does not exist, it is created with mode 0666 135 // (before umask). If successful, methods on the returned File can 136 // be used for I/O; the associated file descriptor has mode O_RDWR. 137 // If there is an error, it will be of type *PathError. 138 func (vfs *OsFS) Create(name string) (avfs.File, error) { 139 return os.Create(name) 140 } 141 142 // CreateTemp creates a new temporary file in the directory dir, 143 // opens the file for reading and writing, and returns the resulting file. 144 // The filename is generated by taking pattern and adding a random string to the end. 145 // If pattern includes a "*", the random string replaces the last "*". 146 // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. 147 // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. 148 // The caller can use the file's Name method to find the pathname of the file. 149 // It is the caller's responsibility to remove the file when it is no longer needed. 150 func (vfs *OsFS) CreateTemp(dir, pattern string) (avfs.File, error) { 151 return os.CreateTemp(dir, pattern) 152 } 153 154 // Dir returns all but the last element of path, typically the path's directory. 155 // After dropping the final element, Dir calls Clean on the path and trailing 156 // slashes are removed. 157 // If the path is empty, Dir returns ".". 158 // If the path consists entirely of separators, Dir returns a single separator. 159 // The returned path does not end in a separator unless it is the root directory. 160 func (vfs *OsFS) Dir(path string) string { 161 return filepath.Dir(path) 162 } 163 164 // EvalSymlinks returns the path name after the evaluation of any symbolic 165 // links. 166 // If path is relative the result will be relative to the current directory, 167 // unless one of the components is an absolute symbolic link. 168 // EvalSymlinks calls Clean on the result. 169 func (vfs *OsFS) EvalSymlinks(path string) (string, error) { 170 return filepath.EvalSymlinks(path) 171 } 172 173 // FromSlash returns the result of replacing each slash ('/') character 174 // in path with a separator character. Multiple slashes are replaced 175 // by multiple separators. 176 func (vfs *OsFS) FromSlash(path string) string { 177 return filepath.FromSlash(path) 178 } 179 180 // Getwd returns a rooted path name corresponding to the 181 // current directory. If the current directory can be 182 // reached via multiple paths (due to symbolic links), 183 // Getwd may return any one of them. 184 func (vfs *OsFS) Getwd() (dir string, err error) { 185 return os.Getwd() 186 } 187 188 // Glob returns the names of all files matching pattern or nil 189 // if there is no matching file. The syntax of patterns is the same 190 // as in Match. The pattern may describe hierarchical names such as 191 // /usr/*/bin/ed (assuming the Separator is '/'). 192 // 193 // Glob ignores file system errors such as I/O errors reading directories. 194 // The only possible returned error is ErrBadPattern, when pattern 195 // is malformed. 196 func (vfs *OsFS) Glob(pattern string) (matches []string, err error) { 197 return filepath.Glob(pattern) 198 } 199 200 // IsAbs reports whether the path is absolute. 201 func (vfs *OsFS) IsAbs(path string) bool { 202 return filepath.IsAbs(path) 203 } 204 205 // IsPathSeparator reports whether c is a directory separator character. 206 func (vfs *OsFS) IsPathSeparator(c uint8) bool { 207 return os.IsPathSeparator(c) 208 } 209 210 // Join joins any number of path elements into a single path, 211 // separating them with an OS specific Separator. Empty elements 212 // are ignored. The result is Cleaned. However, if the argument 213 // list is empty or all its elements are empty, Join returns 214 // an empty string. 215 // On Windows, the result will only be a UNC path if the first 216 // non-empty element is a UNC path. 217 func (vfs *OsFS) Join(elem ...string) string { 218 return filepath.Join(elem...) 219 } 220 221 // Lchown changes the numeric uid and gid of the named file. 222 // If the file is a symbolic link, it changes the uid and gid of the link itself. 223 // If there is an error, it will be of type *PathError. 224 // 225 // On Windows, it always returns the syscall.EWINDOWS error, wrapped 226 // in *PathError. 227 func (vfs *OsFS) Lchown(name string, uid, gid int) error { 228 const op = "lchown" 229 230 if !vfs.HasFeature(avfs.FeatIdentityMgr) && vfs.OSType() != avfs.OsWindows { 231 return &os.PathError{Op: op, Path: name, Err: avfs.ErrOpNotPermitted} 232 } 233 234 return os.Lchown(name, uid, gid) 235 } 236 237 // Link creates newname as a hard link to the oldname file. 238 // If there is an error, it will be of type *LinkError. 239 func (vfs *OsFS) Link(oldname, newname string) error { 240 return os.Link(oldname, newname) 241 } 242 243 // Lstat returns a FileInfo describing the named file. 244 // If the file is a symbolic link, the returned FileInfo 245 // describes the symbolic link. Lstat makes no attempt to follow the link. 246 // If there is an error, it will be of type *PathError. 247 func (vfs *OsFS) Lstat(name string) (fs.FileInfo, error) { 248 return os.Lstat(name) 249 } 250 251 // Match reports whether name matches the shell file name pattern. 252 // The pattern syntax is: 253 // 254 // pattern: 255 // { term } 256 // term: 257 // '*' matches any sequence of non-Separator characters 258 // '?' matches any single non-Separator character 259 // '[' [ '^' ] { character-range } ']' 260 // character class (must be non-empty) 261 // c matches character c (c != '*', '?', '\\', '[') 262 // '\\' c matches character c 263 // 264 // character-range: 265 // c matches character c (c != '\\', '-', ']') 266 // '\\' c matches character c 267 // lo '-' hi matches character c for lo <= c <= hi 268 // 269 // Match requires pattern to match all of name, not just a substring. 270 // The only possible returned error is ErrBadPattern, when pattern 271 // is malformed. 272 // 273 // On Windows, escaping is disabled. Instead, '\\' is treated as 274 // path separator. 275 func (vfs *OsFS) Match(pattern, name string) (matched bool, err error) { 276 return filepath.Match(pattern, name) 277 } 278 279 // Mkdir creates a new directory with the specified name and permission 280 // bits (before umask). 281 // If there is an error, it will be of type *PathError. 282 func (vfs *OsFS) Mkdir(name string, perm fs.FileMode) error { 283 return os.Mkdir(name, perm) 284 } 285 286 // MkdirAll creates a directory named path, 287 // along with any necessary parents, and returns nil, 288 // or else returns an error. 289 // The permission bits perm (before umask) are used for all 290 // directories that MkdirAll creates. 291 // If path is already a directory, MkdirAll does nothing 292 // and returns nil. 293 func (vfs *OsFS) MkdirAll(path string, perm fs.FileMode) error { 294 return os.MkdirAll(path, perm) 295 } 296 297 // MkdirTemp creates a new temporary directory in the directory dir 298 // and returns the pathname of the new directory. 299 // The new directory's name is generated by adding a random string to the end of pattern. 300 // If pattern includes a "*", the random string replaces the last "*" instead. 301 // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. 302 // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. 303 // It is the caller's responsibility to remove the directory when it is no longer needed. 304 func (vfs *OsFS) MkdirTemp(dir, prefix string) (name string, err error) { 305 return os.MkdirTemp(dir, prefix) 306 } 307 308 // Open opens the named file for reading. If successful, methods on 309 // the returned file can be used for reading; the associated file 310 // descriptor has mode O_RDONLY. 311 // If there is an error, it will be of type *PathError. 312 func (vfs *OsFS) Open(name string) (avfs.File, error) { 313 return os.Open(name) 314 } 315 316 // OpenFile is the generalized open call; most users will use Open 317 // or Create instead. It opens the named file with specified flag 318 // (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag 319 // is passed, it is created with mode perm (before umask). If successful, 320 // methods on the returned File can be used for I/O. 321 // If there is an error, it will be of type *PathError. 322 func (vfs *OsFS) OpenFile(name string, flag int, perm fs.FileMode) (avfs.File, error) { 323 return os.OpenFile(name, flag, perm) 324 } 325 326 // OSType returns the operating system type of the file system. 327 func (vfs *OsFS) OSType() avfs.OSType { 328 return avfs.CurrentOSType() 329 } 330 331 // PathSeparator return the OS-specific path separator. 332 func (vfs *OsFS) PathSeparator() uint8 { 333 return os.PathSeparator 334 } 335 336 // ReadDir reads the named directory, 337 // returning all its directory entries sorted by filename. 338 // If an error occurs reading the directory, 339 // ReadDir returns the entries it was able to read before the error, 340 // along with the error. 341 func (vfs *OsFS) ReadDir(name string) ([]fs.DirEntry, error) { 342 return os.ReadDir(name) 343 } 344 345 // ReadFile reads the named file and returns the contents. 346 // A successful call returns err == nil, not err == EOF. 347 // Because ReadFile reads the whole file, it does not treat an EOF from Read 348 // as an error to be reported. 349 func (vfs *OsFS) ReadFile(filename string) ([]byte, error) { 350 return os.ReadFile(filename) 351 } 352 353 // Readlink returns the destination of the named symbolic link. 354 // If there is an error, it will be of type *PathError. 355 func (vfs *OsFS) Readlink(name string) (string, error) { 356 return os.Readlink(name) 357 } 358 359 // Rel returns a relative path that is lexically equivalent to targpath when 360 // joined to basepath with an intervening separator. That is, 361 // Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. 362 // On success, the returned path will always be relative to basepath, 363 // even if basepath and targpath share no elements. 364 // An error is returned if targpath can't be made relative to basepath or if 365 // knowing the current working directory would be necessary to compute it. 366 // Rel calls Clean on the result. 367 func (vfs *OsFS) Rel(basepath, targpath string) (string, error) { 368 return filepath.Rel(basepath, targpath) 369 } 370 371 // Remove removes the named file or directory. 372 // If there is an error, it will be of type *PathError. 373 func (vfs *OsFS) Remove(name string) error { 374 return os.Remove(name) 375 } 376 377 // RemoveAll removes path and any children it contains. 378 // It removes everything it can but returns the first error 379 // it encounters. If the path does not exist, RemoveAll 380 // returns nil (no error). 381 // If there is an error, it will be of type *PathError. 382 func (vfs *OsFS) RemoveAll(path string) error { 383 return os.RemoveAll(path) 384 } 385 386 // Rename renames (moves) oldpath to newpath. 387 // If newpath already exists and is not a directory, Rename replaces it. 388 // OS-specific restrictions may apply when oldpath and newpath are in different directories. 389 // If there is an error, it will be of type *LinkError. 390 func (vfs *OsFS) Rename(oldpath, newpath string) error { 391 return os.Rename(oldpath, newpath) 392 } 393 394 // SameFile reports whether fi1 and fi2 describe the same file. 395 // For example, on Unix this means that the device and inode fields 396 // of the two underlying structures are identical; on other systems 397 // the decision may be based on the path names. 398 // SameFile only applies to results returned by this package's Stat. 399 // It returns false in other cases. 400 func (vfs *OsFS) SameFile(fi1, fi2 fs.FileInfo) bool { 401 return os.SameFile(fi1, fi2) 402 } 403 404 // SetUMask sets the file mode creation mask. 405 func (vfs *OsFS) SetUMask(mask fs.FileMode) error { 406 return avfs.SetUMask(mask) 407 } 408 409 // SetUser sets the current user. 410 // If the user can't be changed an error is returned. 411 func (vfs *OsFS) SetUser(user avfs.UserReader) error { 412 if !vfs.HasFeature(avfs.FeatIdentityMgr) { 413 return avfs.ErrPermDenied 414 } 415 416 return osidm.SetUser(user) 417 } 418 419 // SetUserByName sets and returns the current user. 420 // If the user is not found, the returned error is of type UnknownUserError. 421 func (vfs *OsFS) SetUserByName(name string) error { 422 if !vfs.HasFeature(avfs.FeatIdentityMgr) { 423 return avfs.ErrPermDenied 424 } 425 426 return osidm.SetUserByName(name) 427 } 428 429 // Stat returns a FileInfo describing the named file. 430 // If there is an error, it will be of type *PathError. 431 func (vfs *OsFS) Stat(name string) (fs.FileInfo, error) { 432 return os.Stat(name) 433 } 434 435 // Split splits path immediately following the final Separator, 436 // separating it into a directory and file name component. 437 // If there is no Separator in path, Split returns an empty dir 438 // and file set to path. 439 // The returned values have the property that path = dir+file. 440 func (vfs *OsFS) Split(path string) (dir, file string) { 441 return filepath.Split(path) 442 } 443 444 // Sub returns an FS corresponding to the subtree rooted at dir. 445 func (vfs *OsFS) Sub(dir string) (avfs.VFS, error) { 446 const op = "sub" 447 448 return nil, &fs.PathError{Op: op, Path: dir, Err: vfs.permDeniedError} 449 } 450 451 // Symlink creates newname as a symbolic link to oldname. 452 // On Windows, a symlink to a non-existent oldname creates a file symlink; 453 // if oldname is later created as a directory the symlink will not work. 454 // If there is an error, it will be of type *LinkError. 455 func (vfs *OsFS) Symlink(oldname, newname string) error { 456 return os.Symlink(oldname, newname) 457 } 458 459 // TempDir returns the default directory to use for temporary files. 460 // 461 // On Unix systems, it returns $TMPDIR if non-empty, else /tmp. 462 // On Windows, it uses GetTempPath, returning the first non-empty 463 // value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory. 464 // On Plan 9, it returns /tmp. 465 // 466 // The directory is neither guaranteed to exist nor have accessible 467 // permissions. 468 func (vfs *OsFS) TempDir() string { 469 return os.TempDir() 470 } 471 472 // ToSlash returns the result of replacing each separator character 473 // in path with a slash ('/') character. Multiple separators are 474 // replaced by multiple slashes. 475 func (vfs *OsFS) ToSlash(path string) string { 476 return filepath.ToSlash(path) 477 } 478 479 // Truncate changes the size of the named file. 480 // If the file is a symbolic link, it changes the size of the link's target. 481 func (vfs *OsFS) Truncate(name string, size int64) error { 482 return os.Truncate(name, size) 483 } 484 485 // UMask returns the file mode creation mask. 486 func (vfs *OsFS) UMask() fs.FileMode { 487 return avfs.UMask() 488 } 489 490 // User returns the current user. 491 func (vfs *OsFS) User() avfs.UserReader { 492 return osidm.User() 493 } 494 495 // WalkDir walks the file tree rooted at root, calling fn for each file or 496 // directory in the tree, including root. 497 // 498 // All errors that arise visiting files and directories are filtered by fn: 499 // see the fs.WalkDirFunc documentation for details. 500 // 501 // The files are walked in lexical order, which makes the output deterministic 502 // but requires WalkDir to read an entire directory into memory before proceeding 503 // to walk that directory. 504 // 505 // WalkDir does not follow symbolic links. 506 func (vfs *OsFS) WalkDir(root string, fn fs.WalkDirFunc) error { 507 return filepath.WalkDir(root, fn) 508 } 509 510 // WriteFile writes data to the named file, creating it if necessary. 511 // If the file does not exist, WriteFile creates it with permissions perm (before umask); 512 // otherwise WriteFile truncates it before writing, without changing permissions. 513 func (vfs *OsFS) WriteFile(filename string, data []byte, perm fs.FileMode) error { 514 return os.WriteFile(filename, data, perm) 515 }