github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/vfs/orefafs/orefafs.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 orefafs implements an Afero like in memory file system. 18 // 19 // it supports several features : 20 // - can emulate Linux or Windows systems regardless of the host system 21 // - supports Hard links 22 package orefafs 23 24 import ( 25 "io/fs" 26 "os" 27 "strings" 28 "time" 29 30 "github.com/avfs/avfs" 31 ) 32 33 // Abs returns an absolute representation of path. 34 // If the path is not absolute it will be joined with the current 35 // working directory to turn it into an absolute path. The absolute 36 // path name for a given file is not guaranteed to be unique. 37 // Abs calls [Clean] on the result. 38 func (vfs *OrefaFS) Abs(path string) (string, error) { 39 if vfs.IsAbs(path) { 40 return vfs.Clean(path), nil 41 } 42 43 return vfs.Join(vfs.CurDir(), path), nil 44 } 45 46 // Base returns the last element of path. 47 // Trailing path separators are removed before extracting the last element. 48 // If the path is empty, Base returns ".". 49 // If the path consists entirely of separators, Base returns a single separator. 50 func (vfs *OrefaFS) Base(path string) string { 51 return avfs.Base(vfs, path) 52 } 53 54 // Chdir changes the current working directory to the named directory. 55 // If there is an error, it will be of type *PathError. 56 func (vfs *OrefaFS) Chdir(dir string) error { 57 const op = "chdir" 58 59 absPath, _ := vfs.Abs(dir) 60 61 vfs.mu.RLock() 62 nd, ok := vfs.nodes[absPath] 63 vfs.mu.RUnlock() 64 65 if !ok { 66 return &fs.PathError{Op: op, Path: dir, Err: vfs.err.NoSuchFile} 67 } 68 69 if !nd.mode.IsDir() { 70 err := vfs.err.NotADirectory 71 if vfs.OSType() == avfs.OsWindows { 72 err = avfs.ErrWinDirNameInvalid 73 } 74 75 return &fs.PathError{Op: op, Path: dir, Err: err} 76 } 77 78 _ = vfs.SetCurDir(absPath) 79 80 return nil 81 } 82 83 // Chmod changes the mode of the named file to mode. 84 // If the file is a symbolic link, it changes the mode of the link's target. 85 // If there is an error, it will be of type *PathError. 86 // 87 // A different subset of the mode bits are used, depending on the 88 // operating system. 89 // 90 // On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and 91 // ModeSticky are used. 92 // 93 // On Windows, only the 0200 bit (owner writable) of mode is used; it 94 // controls whether the file's read-only attribute is set or cleared. 95 // The other bits are currently unused. For compatibility with Go 1.12 96 // and earlier, use a non-zero mode. Use mode 0400 for a read-only 97 // file and 0600 for a readable+writable file. 98 // 99 // On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive, 100 // and ModeTemporary are used. 101 func (vfs *OrefaFS) Chmod(name string, mode fs.FileMode) error { 102 const op = "chmod" 103 104 absPath, _ := vfs.Abs(name) 105 106 vfs.mu.RLock() 107 nd, ok := vfs.nodes[absPath] 108 vfs.mu.RUnlock() 109 110 if !ok { 111 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchFile} 112 } 113 114 nd.mu.Lock() 115 nd.setMode(mode) 116 nd.mu.Unlock() 117 118 return nil 119 } 120 121 // Chown changes the numeric uid and gid of the named file. 122 // If the file is a symbolic link, it changes the uid and gid of the link's target. 123 // A uid or gid of -1 means to not change that value. 124 // If there is an error, it will be of type *PathError. 125 // 126 // On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or 127 // EPLAN9 error, wrapped in *PathError. 128 func (vfs *OrefaFS) Chown(name string, uid, gid int) error { 129 const op = "chown" 130 131 if vfs.OSType() == avfs.OsWindows { 132 return &fs.PathError{Op: op, Path: name, Err: vfs.err.OpNotPermitted} 133 } 134 135 absPath, _ := vfs.Abs(name) 136 137 vfs.mu.RLock() 138 nd, ok := vfs.nodes[absPath] 139 vfs.mu.RUnlock() 140 141 if !ok { 142 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchFile} 143 } 144 145 nd.mu.Lock() 146 nd.setOwner(uid, gid) 147 nd.mu.Unlock() 148 149 return nil 150 } 151 152 // Chtimes changes the access and modification times of the named 153 // file, similar to the Unix utime() or utimes() functions. 154 // 155 // The underlying file system may truncate or round the values to a 156 // less precise time unit. 157 // If there is an error, it will be of type *PathError. 158 func (vfs *OrefaFS) Chtimes(name string, atime, mtime time.Time) error { 159 const op = "chtimes" 160 161 absPath, _ := vfs.Abs(name) 162 163 vfs.mu.RLock() 164 nd, ok := vfs.nodes[absPath] 165 vfs.mu.RUnlock() 166 167 if !ok { 168 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchFile} 169 } 170 171 nd.mu.Lock() 172 nd.setModTime(mtime) 173 nd.mu.Unlock() 174 175 return nil 176 } 177 178 // Clean returns the shortest path name equivalent to path 179 // by purely lexical processing. It applies the following rules 180 // iteratively until no further processing can be done: 181 // 182 // 1. Replace multiple Separator elements with a single one. 183 // 2. Eliminate each . path name element (the current directory). 184 // 3. Eliminate each inner .. path name element (the parent directory) 185 // along with the non-.. element that precedes it. 186 // 4. Eliminate .. elements that begin a rooted path: 187 // that is, replace "/.." by "/" at the beginning of a path, 188 // assuming Separator is '/'. 189 // 190 // The returned path ends in a slash only if it represents a root directory, 191 // such as "/" on Unix or `C:\` on Windows. 192 // 193 // Finally, any occurrences of slash are replaced by Separator. 194 // 195 // If the result of this process is an empty string, Clean 196 // returns the string ".". 197 // 198 // See also Rob Pike, “Lexical File Names in Plan 9 or 199 // Getting Dot-Dot Right,” 200 // https://9p.io/sys/doc/lexnames.html 201 func (vfs *OrefaFS) Clean(path string) string { 202 return avfs.Clean(vfs, path) 203 } 204 205 // Create creates or truncates the named file. If the file already exists, 206 // it is truncated. If the file does not exist, it is created with mode 0666 207 // (before umask). If successful, methods on the returned DummyFile can 208 // be used for I/O; the associated file descriptor has mode O_RDWR. 209 // If there is an error, it will be of type *PathError. 210 func (vfs *OrefaFS) Create(name string) (avfs.File, error) { 211 return avfs.Create(vfs, name) 212 } 213 214 // CreateTemp creates a new temporary file in the directory dir, 215 // opens the file for reading and writing, and returns the resulting file. 216 // The filename is generated by taking pattern and adding a random string to the end. 217 // If pattern includes a "*", the random string replaces the last "*". 218 // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. 219 // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. 220 // The caller can use the file's Name method to find the pathname of the file. 221 // It is the caller's responsibility to remove the file when it is no longer needed. 222 func (vfs *OrefaFS) CreateTemp(dir, pattern string) (avfs.File, error) { 223 return avfs.CreateTemp(vfs, dir, pattern) 224 } 225 226 // Dir returns all but the last element of path, typically the path's directory. 227 // After dropping the final element, Dir calls Clean on the path and trailing 228 // slashes are removed. 229 // If the path is empty, Dir returns ".". 230 // If the path consists entirely of separators, Dir returns a single separator. 231 // The returned path does not end in a separator unless it is the root directory. 232 func (vfs *OrefaFS) Dir(path string) string { 233 return avfs.Dir(vfs, path) 234 } 235 236 // EvalSymlinks returns the path name after the evaluation of any symbolic 237 // links. 238 // If path is relative the result will be relative to the current directory, 239 // unless one of the components is an absolute symbolic link. 240 // EvalSymlinks calls Clean on the result. 241 func (vfs *OrefaFS) EvalSymlinks(path string) (string, error) { 242 op := "lstat" 243 if vfs.OSType() == avfs.OsWindows { 244 op = "CreateFile" 245 } 246 247 return "", &fs.PathError{Op: op, Path: path, Err: vfs.err.PermDenied} 248 } 249 250 // FromSlash returns the result of replacing each slash ('/') character 251 // in path with a separator character. Multiple slashes are replaced 252 // by multiple separators. 253 func (vfs *OrefaFS) FromSlash(path string) string { 254 return avfs.FromSlash(vfs, path) 255 } 256 257 // Getwd returns a rooted name link corresponding to the 258 // current directory. If the current directory can be 259 // reached via multiple paths (due to symbolic links), 260 // Getwd may return any one of them. 261 func (vfs *OrefaFS) Getwd() (dir string, err error) { 262 return vfs.CurDir(), nil 263 } 264 265 // Glob returns the names of all files matching pattern or nil 266 // if there is no matching file. The syntax of patterns is the same 267 // as in Match. The pattern may describe hierarchical names such as 268 // /usr/*/bin/ed (assuming the Separator is '/'). 269 // 270 // Glob ignores file system errors such as I/O errors reading directories. 271 // The only possible returned error is ErrBadPattern, when pattern 272 // is malformed. 273 func (vfs *OrefaFS) Glob(pattern string) (matches []string, err error) { 274 return avfs.Glob(vfs, pattern) 275 } 276 277 // IsAbs reports whether the path is absolute. 278 func (vfs *OrefaFS) IsAbs(path string) bool { 279 return avfs.IsAbs(vfs, path) 280 } 281 282 // IsPathSeparator reports whether c is a directory separator character. 283 func (vfs *OrefaFS) IsPathSeparator(c uint8) bool { 284 return avfs.IsPathSeparator(vfs, c) 285 } 286 287 // Join joins any number of path elements into a single path, 288 // separating them with an OS specific Separator. Empty elements 289 // are ignored. The result is Cleaned. However, if the argument 290 // list is empty or all its elements are empty, Join returns 291 // an empty string. 292 // On Windows, the result will only be a UNC path if the first 293 // non-empty element is a UNC path. 294 func (vfs *OrefaFS) Join(elem ...string) string { 295 return avfs.Join(vfs, elem...) 296 } 297 298 // Lchown changes the numeric uid and gid of the named file. 299 // If the file is a symbolic link, it changes the uid and gid of the link itself. 300 // If there is an error, it will be of type *PathError. 301 // 302 // On Windows, it always returns the syscall.EWINDOWS error, wrapped 303 // in *PathError. 304 func (vfs *OrefaFS) Lchown(name string, uid, gid int) error { 305 const op = "lchown" 306 307 if vfs.OSType() == avfs.OsWindows { 308 return &fs.PathError{Op: op, Path: name, Err: vfs.err.OpNotPermitted} 309 } 310 311 absPath, _ := vfs.Abs(name) 312 313 vfs.mu.RLock() 314 nd, ok := vfs.nodes[absPath] 315 vfs.mu.RUnlock() 316 317 if !ok { 318 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchFile} 319 } 320 321 nd.mu.Lock() 322 nd.setOwner(uid, gid) 323 nd.mu.Unlock() 324 325 return nil 326 } 327 328 // Link creates newname as a hard link to the oldname file. 329 // If there is an error, it will be of type *LinkError. 330 func (vfs *OrefaFS) Link(oldname, newname string) error { 331 const op = "link" 332 333 oAbsPath, _ := vfs.Abs(oldname) 334 nAbsPath, _ := vfs.Abs(newname) 335 336 nDirName, nFileName := avfs.SplitAbs(vfs, nAbsPath) 337 338 vfs.mu.RLock() 339 oChild, oChildOk := vfs.nodes[oAbsPath] 340 _, nChildOk := vfs.nodes[nAbsPath] 341 nParent, nParentOk := vfs.nodes[nDirName] 342 vfs.mu.RUnlock() 343 344 if !oChildOk { 345 err := vfs.err.NoSuchFile 346 347 if vfs.OSType() == avfs.OsWindows { 348 oDirName, _ := avfs.SplitAbs(vfs, oAbsPath) 349 350 vfs.mu.RLock() 351 _, oParentOk := vfs.nodes[oDirName] 352 vfs.mu.RUnlock() 353 354 if !oParentOk { 355 err = vfs.err.NoSuchDir 356 } 357 } 358 359 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: err} 360 } 361 362 if !nParentOk { 363 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: vfs.err.NoSuchFile} 364 } 365 366 oChild.mu.Lock() 367 defer oChild.mu.Unlock() 368 369 nParent.mu.Lock() 370 defer nParent.mu.Unlock() 371 372 if oChild.mode.IsDir() { 373 err := error(avfs.ErrOpNotPermitted) 374 if vfs.OSType() == avfs.OsWindows { 375 err = avfs.ErrWinAccessDenied 376 } 377 378 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: err} 379 } 380 381 if nChildOk { 382 err := vfs.err.FileExists 383 if vfs.OSType() == avfs.OsWindows { 384 err = avfs.ErrWinAlreadyExists 385 } 386 387 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: err} 388 } 389 390 vfs.mu.Lock() 391 vfs.nodes[nAbsPath] = oChild 392 vfs.mu.Unlock() 393 394 nParent.addChild(nFileName, oChild) 395 396 oChild.nlink++ 397 398 return nil 399 } 400 401 // Lstat returns a FileInfo describing the named file. 402 // If the file is a symbolic link, the returned FileInfo 403 // describes the symbolic link. Lstat makes no attempt to follow the link. 404 // If there is an error, it will be of type *PathError. 405 func (vfs *OrefaFS) Lstat(name string) (fs.FileInfo, error) { 406 op := "lstat" 407 if vfs.OSType() == avfs.OsWindows { 408 op = "CreateFile" 409 } 410 411 return vfs.stat(name, op) 412 } 413 414 // Match reports whether name matches the shell file name pattern. 415 // The pattern syntax is: 416 // 417 // pattern: 418 // { term } 419 // term: 420 // '*' matches any sequence of non-Separator characters 421 // '?' matches any single non-Separator character 422 // '[' [ '^' ] { character-range } ']' 423 // character class (must be non-empty) 424 // c matches character c (c != '*', '?', '\\', '[') 425 // '\\' c matches character c 426 // 427 // character-range: 428 // c matches character c (c != '\\', '-', ']') 429 // '\\' c matches character c 430 // lo '-' hi matches character c for lo <= c <= hi 431 // 432 // Match requires pattern to match all of name, not just a substring. 433 // The only possible returned error is ErrBadPattern, when pattern 434 // is malformed. 435 // 436 // On Windows, escaping is disabled. Instead, '\\' is treated as 437 // path separator. 438 func (vfs *OrefaFS) Match(pattern, name string) (matched bool, err error) { 439 return avfs.Match(vfs, pattern, name) 440 } 441 442 // Mkdir creates a new directory with the specified name and permission 443 // bits (before umask). 444 // If there is an error, it will be of type *PathError. 445 func (vfs *OrefaFS) Mkdir(name string, perm fs.FileMode) error { 446 const op = "mkdir" 447 448 if name == "" { 449 return &fs.PathError{Op: op, Path: "", Err: vfs.err.NoSuchDir} 450 } 451 452 absPath, _ := vfs.Abs(name) 453 dirName, fileName := avfs.SplitAbs(vfs, absPath) 454 455 vfs.mu.Lock() 456 defer vfs.mu.Unlock() 457 458 _, childOk := vfs.nodes[absPath] 459 parent, parentOk := vfs.nodes[dirName] 460 461 if childOk { 462 return &fs.PathError{Op: op, Path: name, Err: vfs.err.FileExists} 463 } 464 465 if !parentOk { 466 for !parentOk { 467 dirName, _ = avfs.SplitAbs(vfs, dirName) 468 parent, parentOk = vfs.nodes[dirName] 469 } 470 471 if parent.mode.IsDir() { 472 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchDir} 473 } 474 475 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NotADirectory} 476 } 477 478 if !parent.mode.IsDir() { 479 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NotADirectory} 480 } 481 482 vfs.createDir(parent, absPath, fileName, perm) 483 484 return nil 485 } 486 487 // MkdirAll creates a directory named name, 488 // along with any necessary parents, and returns nil, 489 // or else returns an error. 490 // The permission bits perm (before umask) are used for all 491 // directories that MkdirAll creates. 492 // If name is already a directory, MkdirAll does nothing 493 // and returns nil. 494 func (vfs *OrefaFS) MkdirAll(path string, perm fs.FileMode) error { 495 const op = "mkdir" 496 497 absPath, _ := vfs.Abs(path) 498 499 vfs.mu.Lock() 500 defer vfs.mu.Unlock() 501 502 child, childOk := vfs.nodes[absPath] 503 if childOk { 504 if child.mode.IsDir() { 505 return nil 506 } 507 508 return &fs.PathError{Op: op, Path: path, Err: vfs.err.NotADirectory} 509 } 510 511 var ( 512 ds []string 513 parent *node 514 ) 515 516 dirName := absPath 517 518 for { 519 nd, ok := vfs.nodes[dirName] 520 if ok { 521 parent = nd 522 if !parent.mode.IsDir() { 523 return &fs.PathError{Op: op, Path: dirName, Err: vfs.err.NotADirectory} 524 } 525 526 break 527 } 528 529 ds = append(ds, dirName) 530 531 dirName, _ = avfs.SplitAbs(vfs, dirName) 532 } 533 534 for _, absPath = range ds { 535 _, fileName := avfs.SplitAbs(vfs, absPath) 536 537 parent = vfs.createDir(parent, absPath, fileName, perm) 538 } 539 540 return nil 541 } 542 543 // MkdirTemp creates a new temporary directory in the directory dir 544 // and returns the pathname of the new directory. 545 // The new directory's name is generated by adding a random string to the end of pattern. 546 // If pattern includes a "*", the random string replaces the last "*" instead. 547 // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. 548 // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. 549 // It is the caller's responsibility to remove the directory when it is no longer needed. 550 func (vfs *OrefaFS) MkdirTemp(dir, pattern string) (string, error) { 551 return avfs.MkdirTemp(vfs, dir, pattern) 552 } 553 554 // Open opens the named file for reading. If successful, methods on 555 // the returned file can be used for reading; the associated file 556 // descriptor has mode O_RDONLY. 557 // If there is an error, it will be of type *PathError. 558 func (vfs *OrefaFS) Open(name string) (avfs.File, error) { 559 return vfs.OpenFile(name, os.O_RDONLY, 0) 560 } 561 562 // OpenFile is the generalized open call; most users will use Open 563 // or Create instead. It opens the named file with specified flag 564 // (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag 565 // is passed, it is created with mode perm (before umask). If successful, 566 // methods on the returned File can be used for I/O. 567 // If there is an error, it will be of type *PathError. 568 func (vfs *OrefaFS) OpenFile(name string, flag int, perm fs.FileMode) (avfs.File, error) { 569 const op = "open" 570 571 at := int64(0) 572 om := avfs.ToOpenMode(flag) 573 574 absPath, _ := vfs.Abs(name) 575 dirName, fileName := avfs.SplitAbs(vfs, absPath) 576 577 vfs.mu.RLock() 578 parent, parentOk := vfs.nodes[dirName] 579 child, childOk := vfs.nodes[absPath] 580 vfs.mu.RUnlock() 581 582 if !childOk { 583 if !parentOk { 584 return nil, &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchDir} 585 } 586 587 if !parent.mode.IsDir() { 588 return &OrefaFile{}, &fs.PathError{Op: op, Path: name, Err: vfs.err.NotADirectory} 589 } 590 591 if om&avfs.OpenCreate == 0 { 592 return &OrefaFile{}, &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchFile} 593 } 594 595 if om&avfs.OpenWrite == 0 { 596 return &OrefaFile{}, &fs.PathError{Op: op, Path: name, Err: vfs.err.PermDenied} 597 } 598 599 vfs.mu.Lock() 600 defer vfs.mu.Unlock() 601 602 // test for race conditions when opening file in exclusive mode. 603 _, childOk = vfs.nodes[absPath] 604 if childOk && om&avfs.OpenCreateExcl != 0 { 605 return &OrefaFile{}, &fs.PathError{Op: op, Path: name, Err: vfs.err.FileExists} 606 } 607 608 child = vfs.createFile(parent, absPath, fileName, perm) 609 } else { 610 if child.mode.IsDir() { 611 if om&avfs.OpenWrite != 0 { 612 return (*OrefaFile)(nil), &fs.PathError{Op: op, Path: name, Err: vfs.err.IsADirectory} 613 } 614 } else { 615 if om&avfs.OpenCreateExcl != 0 { 616 return &OrefaFile{}, &fs.PathError{Op: op, Path: name, Err: vfs.err.FileExists} 617 } 618 619 if om&avfs.OpenTruncate != 0 { 620 child.mu.Lock() 621 child.truncate(0) 622 child.mu.Unlock() 623 } 624 625 if om&avfs.OpenAppend != 0 { 626 at = child.Size() 627 } 628 } 629 } 630 631 f := &OrefaFile{ 632 vfs: vfs, 633 nd: child, 634 openMode: om, 635 name: name, 636 at: at, 637 } 638 639 return f, nil 640 } 641 642 // ReadDir reads the named directory, 643 // returning all its directory entries sorted by filename. 644 // If an error occurs reading the directory, 645 // ReadDir returns the entries it was able to read before the error, 646 // along with the error. 647 func (vfs *OrefaFS) ReadDir(name string) ([]fs.DirEntry, error) { 648 return avfs.ReadDir(vfs, name) 649 } 650 651 // ReadFile reads the named file and returns the contents. 652 // A successful call returns err == nil, not err == EOF. 653 // Because ReadFile reads the whole file, it does not treat an EOF from Read 654 // as an error to be reported. 655 func (vfs *OrefaFS) ReadFile(name string) ([]byte, error) { 656 return avfs.ReadFile(vfs, name) 657 } 658 659 // Readlink returns the destination of the named symbolic link. 660 // If there is an error, it will be of type *PathError. 661 func (vfs *OrefaFS) Readlink(name string) (string, error) { 662 const op = "readlink" 663 664 err := error(avfs.ErrPermDenied) 665 if vfs.OSType() == avfs.OsWindows { 666 err = avfs.ErrWinNotReparsePoint 667 } 668 669 return "", &fs.PathError{Op: op, Path: name, Err: err} 670 } 671 672 // Rel returns a relative path that is lexically equivalent to targpath when 673 // joined to basepath with an intervening separator. That is, 674 // Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. 675 // On success, the returned path will always be relative to basepath, 676 // even if basepath and targpath share no elements. 677 // An error is returned if targpath can't be made relative to basepath or if 678 // knowing the current working directory would be necessary to compute it. 679 // Rel calls Clean on the result. 680 func (vfs *OrefaFS) Rel(basepath, targpath string) (string, error) { 681 return avfs.Rel(vfs, basepath, targpath) 682 } 683 684 // Remove removes the named file or (empty) directory. 685 // If there is an error, it will be of type *PathError. 686 func (vfs *OrefaFS) Remove(name string) error { 687 const op = "remove" 688 689 absPath, _ := vfs.Abs(name) 690 dirName, fileName := avfs.SplitAbs(vfs, absPath) 691 692 vfs.mu.Lock() 693 defer vfs.mu.Unlock() 694 695 child, childOk := vfs.nodes[absPath] 696 parent, parentOk := vfs.nodes[dirName] 697 698 if !childOk || !parentOk { 699 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchFile} 700 } 701 702 parent.mu.Lock() 703 defer parent.mu.Unlock() 704 705 child.mu.Lock() 706 defer child.mu.Unlock() 707 708 if child.mode.IsDir() && len(child.children) != 0 { 709 return &fs.PathError{Op: op, Path: name, Err: vfs.err.DirNotEmpty} 710 } 711 712 child.remove() 713 714 delete(parent.children, fileName) 715 delete(vfs.nodes, absPath) 716 717 return nil 718 } 719 720 // RemoveAll removes path and any children it contains. 721 // It removes everything it can but returns the first error 722 // it encounters. If the path does not exist, RemoveAll 723 // returns nil (no error). 724 // If there is an error, it will be of type *PathError. 725 func (vfs *OrefaFS) RemoveAll(path string) error { 726 if path == "" { 727 // fail silently to retain compatibility with previous behavior of RemoveAll. 728 return nil 729 } 730 731 absPath, _ := vfs.Abs(path) 732 dirName, fileName := avfs.SplitAbs(vfs, absPath) 733 734 vfs.mu.Lock() 735 defer vfs.mu.Unlock() 736 737 child, childOk := vfs.nodes[absPath] 738 parent, parentOk := vfs.nodes[dirName] 739 740 if !childOk || !parentOk { 741 return nil 742 } 743 744 if child.mode.IsDir() { 745 vfs.removeAll(absPath, child) 746 } 747 748 child.remove() 749 750 delete(parent.children, fileName) 751 delete(vfs.nodes, absPath) 752 753 return nil 754 } 755 756 func (vfs *OrefaFS) removeAll(absPath string, rootNode *node) { 757 if rootNode.mode.IsDir() { 758 for fileName, nd := range rootNode.children { 759 path := absPath + string(vfs.PathSeparator()) + fileName 760 761 vfs.removeAll(path, nd) 762 } 763 } 764 765 rootNode.remove() 766 delete(vfs.nodes, absPath) 767 } 768 769 // Rename renames (moves) oldpath to newpath. 770 // If newpath already exists and is not a directory, Rename replaces it. 771 // OS-specific restrictions may apply when oldpath and newpath are in different directories. 772 // If there is an error, it will be of type *LinkError. 773 func (vfs *OrefaFS) Rename(oldname, newname string) error { 774 const op = "rename" 775 776 oAbsPath, _ := vfs.Abs(oldname) 777 nAbsPath, _ := vfs.Abs(newname) 778 779 if oAbsPath == nAbsPath { 780 return nil 781 } 782 783 oDirName, oFileName := avfs.SplitAbs(vfs, oAbsPath) 784 nDirName, nFileName := avfs.SplitAbs(vfs, nAbsPath) 785 786 vfs.mu.RLock() 787 oChild, oChildOk := vfs.nodes[oAbsPath] 788 oParent, oParentOk := vfs.nodes[oDirName] 789 nChild, nChildOk := vfs.nodes[nAbsPath] 790 nParent, nParentOk := vfs.nodes[nDirName] 791 vfs.mu.RUnlock() 792 793 if !oChildOk || !oParentOk || !nParentOk { 794 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: vfs.err.NoSuchFile} 795 } 796 797 if (oChild.mode.IsDir() && nChildOk) || (!oChild.mode.IsDir() && nChildOk && nChild.mode.IsDir()) { 798 err := vfs.err.FileExists 799 if vfs.OSType() == avfs.OsWindows { 800 err = avfs.ErrWinAccessDenied 801 } 802 803 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: err} 804 } 805 806 nParent.mu.Lock() 807 defer nParent.mu.Unlock() 808 809 if nParent != oParent { 810 oParent.mu.Lock() 811 defer oParent.mu.Unlock() 812 } 813 814 nParent.children[nFileName] = oChild 815 816 delete(oParent.children, oFileName) 817 818 vfs.mu.Lock() 819 defer vfs.mu.Unlock() 820 821 vfs.nodes[nAbsPath] = oChild 822 delete(vfs.nodes, oAbsPath) 823 824 if oChild.mode.IsDir() { 825 oRoot := oAbsPath + string(vfs.PathSeparator()) 826 827 for absPath, node := range vfs.nodes { 828 if strings.HasPrefix(absPath, oRoot) { 829 nPath := nAbsPath + absPath[len(oAbsPath):] 830 vfs.nodes[nPath] = node 831 832 delete(vfs.nodes, absPath) 833 } 834 } 835 } 836 837 return nil 838 } 839 840 // SameFile reports whether fi1 and fi2 describe the same file. 841 // For example, on Unix this means that the device and inode fields 842 // of the two underlying structures are identical; on other systems 843 // the decision may be based on the path names. 844 // SameFile only applies to results returned by this package's Stat. 845 // It returns false in other cases. 846 func (vfs *OrefaFS) SameFile(fi1, fi2 fs.FileInfo) bool { 847 fs1, ok1 := fi1.(*OrefaInfo) 848 if !ok1 { 849 return false 850 } 851 852 fs2, ok2 := fi2.(*OrefaInfo) 853 if !ok2 { 854 return false 855 } 856 857 return fs1.id == fs2.id 858 } 859 860 // SetUserByName sets the current user by name. 861 // If the user is not found, the returned error is of type UnknownUserError. 862 func (vfs *OrefaFS) SetUserByName(name string) error { 863 return avfs.SetUserByName(vfs, name) 864 } 865 866 // Split splits path immediately following the final Separator, 867 // separating it into a directory and file name component. 868 // If there is no Separator in path, Split returns an empty dir 869 // and file set to path. 870 // The returned values have the property that path = dir+file. 871 func (vfs *OrefaFS) Split(path string) (dir, file string) { 872 return avfs.Split(vfs, path) 873 } 874 875 // Stat returns a FileInfo describing the named file. 876 // If there is an error, it will be of type *PathError. 877 func (vfs *OrefaFS) Stat(path string) (fs.FileInfo, error) { 878 op := "stat" 879 if vfs.OSType() == avfs.OsWindows { 880 op = "CreateFile" 881 } 882 883 return vfs.stat(path, op) 884 } 885 886 // stat is the internal function used by Stat and Lstat. 887 func (vfs *OrefaFS) stat(path, op string) (fs.FileInfo, error) { 888 absPath, _ := vfs.Abs(path) 889 dirName, fileName := avfs.SplitAbs(vfs, absPath) 890 891 vfs.mu.RLock() 892 child, childOk := vfs.nodes[absPath] 893 vfs.mu.RUnlock() 894 895 if !childOk { 896 vfs.mu.RLock() 897 parent, parentOk := vfs.nodes[dirName] 898 vfs.mu.RUnlock() 899 900 if !parentOk { 901 return nil, &fs.PathError{Op: op, Path: path, Err: vfs.err.NoSuchDir} 902 } 903 904 if parent.mode.IsDir() { 905 return nil, &fs.PathError{Op: op, Path: path, Err: vfs.err.NoSuchFile} 906 } 907 908 return nil, &fs.PathError{Op: op, Path: path, Err: vfs.err.NotADirectory} 909 } 910 911 fst := child.fillStatFrom(fileName) 912 913 return fst, nil 914 } 915 916 // Sub returns an FS corresponding to the subtree rooted at dir. 917 func (vfs *OrefaFS) Sub(dir string) (avfs.VFS, error) { 918 const op = "sub" 919 920 return nil, &fs.PathError{Op: op, Path: dir, Err: vfs.err.PermDenied} 921 } 922 923 // Symlink creates newname as a symbolic link to oldname. 924 // If there is an error, it will be of type *LinkError. 925 func (vfs *OrefaFS) Symlink(oldname, newname string) error { 926 const op = "symlink" 927 928 err := error(avfs.ErrPermDenied) 929 if vfs.OSType() == avfs.OsWindows { 930 err = avfs.ErrWinPrivilegeNotHeld 931 } 932 933 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: err} 934 } 935 936 // TempDir returns the default directory to use for temporary files. 937 // 938 // On Unix systems, it returns $TMPDIR if non-empty, else /tmp. 939 // On Windows, it uses GetTempPath, returning the first non-empty 940 // value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory. 941 // On Plan 9, it returns /tmp. 942 // 943 // The directory is neither guaranteed to exist nor have accessible 944 // permissions. 945 func (vfs *OrefaFS) TempDir() string { 946 return avfs.TempDir(vfs) 947 } 948 949 // ToSlash returns the result of replacing each separator character 950 // in path with a slash ('/') character. Multiple separators are 951 // replaced by multiple slashes. 952 func (vfs *OrefaFS) ToSlash(path string) string { 953 return avfs.ToSlash(vfs, path) 954 } 955 956 // ToSysStat takes a value from fs.FileInfo.Sys() and returns a value that implements interface avfs.SysStater. 957 func (vfs *OrefaFS) ToSysStat(info fs.FileInfo) avfs.SysStater { 958 return info.Sys().(avfs.SysStater) //nolint:forcetypeassert // type assertion must be checked 959 } 960 961 // Truncate changes the size of the named file. 962 // If the file is a symbolic link, it changes the size of the link's target. 963 // If there is an error, it will be of type *PathError. 964 func (vfs *OrefaFS) Truncate(name string, size int64) error { 965 op := "truncate" 966 967 absPath, _ := vfs.Abs(name) 968 969 vfs.mu.RLock() 970 child, childOk := vfs.nodes[absPath] 971 vfs.mu.RUnlock() 972 973 if !childOk { 974 if vfs.OSType() == avfs.OsWindows { 975 op = "open" 976 } 977 978 return &fs.PathError{Op: op, Path: name, Err: vfs.err.NoSuchFile} 979 } 980 981 if child.mode.IsDir() { 982 if vfs.OSType() == avfs.OsWindows { 983 op = "open" 984 } 985 986 return &fs.PathError{Op: op, Path: name, Err: vfs.err.IsADirectory} 987 } 988 989 if size < 0 { 990 return &fs.PathError{Op: op, Path: name, Err: vfs.err.InvalidArgument} 991 } 992 993 child.mu.Lock() 994 child.truncate(size) 995 child.mu.Unlock() 996 997 return nil 998 } 999 1000 // WalkDir walks the file tree rooted at root, calling fn for each file or 1001 // directory in the tree, including root. 1002 // 1003 // All errors that arise visiting files and directories are filtered by fn: 1004 // see the fs.WalkDirFunc documentation for details. 1005 // 1006 // The files are walked in lexical order, which makes the output deterministic 1007 // but requires WalkDir to read an entire directory into memory before proceeding 1008 // to walk that directory. 1009 // 1010 // WalkDir does not follow symbolic links. 1011 func (vfs *OrefaFS) WalkDir(root string, fn fs.WalkDirFunc) error { 1012 return avfs.WalkDir(vfs, root, fn) 1013 } 1014 1015 // WriteFile writes data to the named file, creating it if necessary. 1016 // If the file does not exist, WriteFile creates it with permissions perm (before umask); 1017 // otherwise WriteFile truncates it before writing, without changing permissions. 1018 func (vfs *OrefaFS) WriteFile(name string, data []byte, perm fs.FileMode) error { 1019 return avfs.WriteFile(vfs, name, data, perm) 1020 }