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  }