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