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