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