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  }