github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/vfs/rofs/rofs.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 rofs provides a read only file system on top of any other Avfs file system. 18 package rofs 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 *RoFS) Abs(path string) (string, error) { 36 return vfs.baseFS.Abs(path) 37 } 38 39 // Base returns the last element of path. 40 // Trailing path separators are removed before extracting the last element. 41 // If the path is empty, Base returns ".". 42 // If the path consists entirely of separators, Base returns a single separator. 43 func (vfs *RoFS) Base(path string) string { 44 return vfs.baseFS.Base(path) 45 } 46 47 // Chdir changes the current working directory to the named directory. 48 // If there is an error, it will be of type *PathError. 49 func (vfs *RoFS) Chdir(dir string) error { 50 return vfs.baseFS.Chdir(dir) 51 } 52 53 // Chmod changes the mode of the named file to mode. 54 // If the file is a symbolic link, it changes the mode of the link's target. 55 // If there is an error, it will be of type *PathError. 56 // 57 // A different subset of the mode bits are used, depending on the 58 // operating system. 59 // 60 // On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and 61 // ModeSticky are used. 62 // 63 // On Windows, only the 0200 bit (owner writable) of mode is used; it 64 // controls whether the file's read-only attribute is set or cleared. 65 // The other bits are currently unused. For compatibility with Go 1.12 66 // and earlier, use a non-zero mode. Use mode 0400 for a read-only 67 // file and 0600 for a readable+writable file. 68 // 69 // On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive, 70 // and ModeTemporary are used. 71 func (vfs *RoFS) Chmod(name string, mode fs.FileMode) error { 72 const op = "chmod" 73 74 return &fs.PathError{Op: op, Path: name, Err: vfs.errPermDenied} 75 } 76 77 // Chown changes the numeric uid and gid of the named file. 78 // If the file is a symbolic link, it changes the uid and gid of the link's target. 79 // A uid or gid of -1 means to not change that value. 80 // If there is an error, it will be of type *PathError. 81 // 82 // On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or 83 // EPLAN9 error, wrapped in *PathError. 84 func (vfs *RoFS) Chown(name string, uid, gid int) error { 85 const op = "chown" 86 87 return &fs.PathError{Op: op, Path: name, Err: vfs.errOpNotPermitted} 88 } 89 90 // Chtimes changes the access and modification times of the named 91 // file, similar to the Unix utime() or utimes() functions. 92 // 93 // The underlying file system may truncate or round the values to a 94 // less precise time unit. 95 // If there is an error, it will be of type *PathError. 96 func (vfs *RoFS) Chtimes(name string, atime, mtime time.Time) error { 97 const op = "chtimes" 98 99 return &fs.PathError{Op: op, Path: name, Err: vfs.errPermDenied} 100 } 101 102 // Clean returns the shortest path name equivalent to path 103 // by purely lexical processing. It applies the following rules 104 // iteratively until no further processing can be done: 105 // 106 // 1. Replace multiple Separator elements with a single one. 107 // 2. Eliminate each . path name element (the current directory). 108 // 3. Eliminate each inner .. path name element (the parent directory) 109 // along with the non-.. element that precedes it. 110 // 4. Eliminate .. elements that begin a rooted path: 111 // that is, replace "/.." by "/" at the beginning of a path, 112 // assuming Separator is '/'. 113 // 114 // The returned path ends in a slash only if it represents a root directory, 115 // such as "/" on Unix or `C:\` on Windows. 116 // 117 // Finally, any occurrences of slash are replaced by Separator. 118 // 119 // If the result of this process is an empty string, Clean 120 // returns the string ".". 121 // 122 // See also Rob Pike, “Lexical File Names in Plan 9 or 123 // Getting Dot-Dot Right,” 124 // https://9p.io/sys/doc/lexnames.html 125 func (vfs *RoFS) Clean(path string) string { 126 return vfs.baseFS.Clean(path) 127 } 128 129 // Create creates the named file with mode 0666 (before umask), truncating 130 // it if it already exists. If successful, methods on the returned 131 // File can be used for I/O; the associated file descriptor has mode 132 // O_RDWR. 133 // If there is an error, it will be of type *PathError. 134 func (vfs *RoFS) Create(name string) (avfs.File, error) { 135 const op = "open" 136 137 return &RoFile{}, &fs.PathError{Op: op, Path: name, Err: vfs.errPermDenied} 138 } 139 140 // CreateTemp creates a new temporary file in the directory dir, 141 // opens the file for reading and writing, and returns the resulting file. 142 // The filename is generated by taking pattern and adding a random string to the end. 143 // If pattern includes a "*", the random string replaces the last "*". 144 // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. 145 // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. 146 // The caller can use the file's Name method to find the pathname of the file. 147 // It is the caller's responsibility to remove the file when it is no longer needed. 148 func (vfs *RoFS) CreateTemp(dir, pattern string) (avfs.File, error) { 149 const op = "createtemp" 150 151 return &RoFile{}, &fs.PathError{Op: op, Path: dir, Err: vfs.errPermDenied} 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 *RoFS) Dir(path string) string { 161 return vfs.baseFS.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 *RoFS) EvalSymlinks(path string) (string, error) { 170 return vfs.baseFS.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 *RoFS) FromSlash(path string) string { 177 return vfs.baseFS.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 *RoFS) Getwd() (dir string, err error) { 185 return vfs.baseFS.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 *RoFS) Glob(pattern string) (matches []string, err error) { 197 return vfs.baseFS.Glob(pattern) 198 } 199 200 // Idm returns the identity manager of the file system. 201 // If the file system does not have an identity manager, avfs.DummyIdm is returned. 202 func (vfs *RoFS) Idm() avfs.IdentityMgr { 203 return avfs.NotImplementedIdm 204 } 205 206 // IsAbs reports whether the path is absolute. 207 func (vfs *RoFS) IsAbs(path string) bool { 208 return vfs.baseFS.IsAbs(path) 209 } 210 211 // IsPathSeparator reports whether c is a directory separator character. 212 func (vfs *RoFS) IsPathSeparator(c uint8) bool { 213 return vfs.baseFS.IsPathSeparator(c) 214 } 215 216 // Join joins any number of path elements into a single path, adding a 217 // separating slash if necessary. The result is Cleaned; in particular, 218 // all empty strings are ignored. 219 func (vfs *RoFS) Join(elem ...string) string { 220 return vfs.baseFS.Join(elem...) 221 } 222 223 // Lchown changes the numeric uid and gid of the named file. 224 // If the file is a symbolic link, it changes the uid and gid of the link itself. 225 // If there is an error, it will be of type *PathError. 226 // 227 // On Windows, it always returns the syscall.EWINDOWS error, wrapped 228 // in *PathError. 229 func (vfs *RoFS) Lchown(name string, uid, gid int) error { 230 const op = "lchown" 231 232 return &fs.PathError{Op: op, Path: name, Err: vfs.errOpNotPermitted} 233 } 234 235 // Link creates newname as a hard link to the oldname file. 236 // If there is an error, it will be of type *LinkError. 237 func (vfs *RoFS) Link(oldname, newname string) error { 238 const op = "link" 239 240 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: vfs.errPermDenied} 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 *RoFS) Lstat(name string) (fs.FileInfo, error) { 248 return vfs.baseFS.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 *RoFS) Match(pattern, name string) (matched bool, err error) { 276 return vfs.baseFS.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 *RoFS) Mkdir(name string, perm fs.FileMode) error { 283 const op = "mkdir" 284 285 return &fs.PathError{Op: op, Path: name, Err: vfs.errPermDenied} 286 } 287 288 // MkdirAll creates a directory named path, 289 // along with any necessary parents, and returns nil, 290 // or else returns an error. 291 // The permission bits perm (before umask) are used for all 292 // directories that MkdirAll creates. 293 // If path is already a directory, MkdirAll does nothing 294 // and returns nil. 295 func (vfs *RoFS) MkdirAll(path string, perm fs.FileMode) error { 296 const op = "mkdir" 297 298 return &fs.PathError{Op: op, Path: path, Err: vfs.errPermDenied} 299 } 300 301 // MkdirTemp creates a new temporary directory in the directory dir 302 // and returns the pathname of the new directory. 303 // The new directory's name is generated by adding a random string to the end of pattern. 304 // If pattern includes a "*", the random string replaces the last "*" instead. 305 // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. 306 // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. 307 // It is the caller's responsibility to remove the directory when it is no longer needed. 308 func (vfs *RoFS) MkdirTemp(dir, prefix string) (name string, err error) { 309 const op = "mkdirtemp" 310 311 return "", &fs.PathError{Op: op, Path: dir, Err: vfs.errPermDenied} 312 } 313 314 // Open opens the named file for reading. If successful, methods on 315 // the returned file can be used for reading; the associated file 316 // descriptor has mode O_RDONLY. 317 // If there is an error, it will be of type *PathError. 318 func (vfs *RoFS) Open(name string) (avfs.File, error) { 319 return vfs.OpenFile(name, os.O_RDONLY, 0) 320 } 321 322 // OpenFile is the generalized open call; most users will use Open 323 // or Create instead. It opens the named file with specified flag 324 // (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag 325 // is passed, it is created with mode perm (before umask). If successful, 326 // methods on the returned File can be used for I/O. 327 // If there is an error, it will be of type *PathError. 328 func (vfs *RoFS) OpenFile(name string, flag int, perm fs.FileMode) (avfs.File, error) { 329 const op = "open" 330 331 if flag != os.O_RDONLY { 332 return &RoFile{}, &fs.PathError{Op: op, Path: name, Err: vfs.errPermDenied} 333 } 334 335 fBase, err := vfs.baseFS.OpenFile(name, os.O_RDONLY, 0) 336 337 f := &RoFile{baseFile: fBase, vfs: vfs} 338 339 return f, err 340 } 341 342 // PathSeparator return the OS-specific path separator. 343 func (vfs *RoFS) PathSeparator() uint8 { 344 return vfs.baseFS.PathSeparator() 345 } 346 347 // ReadDir reads the named directory, 348 // returning all its directory entries sorted by filename. 349 // If an error occurs reading the directory, 350 // ReadDir returns the entries it was able to read before the error, 351 // along with the error. 352 func (vfs *RoFS) ReadDir(name string) ([]fs.DirEntry, error) { 353 return vfs.baseFS.ReadDir(name) 354 } 355 356 // ReadFile reads the file named by filename and returns the contents. 357 // A successful call returns err == nil, not err == EOF. Because ReadFile 358 // reads the whole file, it does not treat an EOF from Read as an error 359 // to be reported. 360 func (vfs *RoFS) ReadFile(filename string) ([]byte, error) { 361 return vfs.baseFS.ReadFile(filename) 362 } 363 364 // Readlink returns the destination of the named symbolic link. 365 // If there is an error, it will be of type *PathError. 366 func (vfs *RoFS) Readlink(name string) (string, error) { 367 return vfs.baseFS.Readlink(name) 368 } 369 370 // Rel returns a relative path that is lexically equivalent to targpath when 371 // joined to basepath with an intervening separator. That is, 372 // Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. 373 // On success, the returned path will always be relative to basepath, 374 // even if basepath and targpath share no elements. 375 // An error is returned if targpath can't be made relative to basepath or if 376 // knowing the current working directory would be necessary to compute it. 377 // Rel calls Clean on the result. 378 func (vfs *RoFS) Rel(basepath, targpath string) (string, error) { 379 return vfs.baseFS.Rel(basepath, targpath) 380 } 381 382 // Remove removes the named file or (empty) directory. 383 // If there is an error, it will be of type *PathError. 384 func (vfs *RoFS) Remove(name string) error { 385 const op = "remove" 386 387 return &fs.PathError{Op: op, Path: name, Err: vfs.errPermDenied} 388 } 389 390 // RemoveAll removes path and any children it contains. 391 // It removes everything it can but returns the first error 392 // it encounters. If the path does not exist, RemoveAll 393 // returns nil (no error). 394 // If there is an error, it will be of type *PathError. 395 func (vfs *RoFS) RemoveAll(path string) error { 396 const op = "removeall" 397 398 return &fs.PathError{Op: op, Path: path, Err: vfs.errPermDenied} 399 } 400 401 // Rename renames (moves) oldpath to newpath. 402 // If newpath already exists and is not a directory, Rename replaces it. 403 // OS-specific restrictions may apply when oldpath and newpath are in different directories. 404 // If there is an error, it will be of type *LinkError. 405 func (vfs *RoFS) Rename(oldname, newname string) error { 406 const op = "rename" 407 408 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: vfs.errPermDenied} 409 } 410 411 // SameFile reports whether fi1 and fi2 describe the same file. 412 // For example, on Unix this means that the device and inode fields 413 // of the two underlying structures are identical; on other systems 414 // the decision may be based on the path names. 415 // SameFile only applies to results returned by this package's Stat. 416 // It returns false in other cases. 417 func (vfs *RoFS) SameFile(fi1, fi2 fs.FileInfo) bool { 418 return vfs.baseFS.SameFile(fi1, fi2) 419 } 420 421 // SetIdm set the current identity manager. 422 // If the identity manager provider is nil, the idm dummyidm.NotImplementedIdm is set. 423 func (vfs *RoFS) SetIdm(idm avfs.IdentityMgr) error { 424 return avfs.ErrPermDenied 425 } 426 427 // SetUMask sets the file mode creation mask. 428 // Setting Umask is disabled for read only file systems. 429 func (vfs *RoFS) SetUMask(mask fs.FileMode) error { 430 return vfs.baseFS.SetUMask(mask) 431 } 432 433 // SetUser sets the current user. 434 // If the user can't be changed an error is returned. 435 func (vfs *RoFS) SetUser(user avfs.UserReader) error { 436 return avfs.ErrPermDenied 437 } 438 439 // SetUserByName sets the current user by name. 440 // If the user is not found, the returned error is of type UnknownUserError. 441 func (vfs *RoFS) SetUserByName(name string) error { 442 return avfs.ErrPermDenied 443 } 444 445 // Split splits path immediately following the final Separator, 446 // separating it into a directory and file name component. 447 // If there is no Separator in path, Split returns an empty dir 448 // and file set to path. 449 // The returned values have the property that path = dir+file. 450 func (vfs *RoFS) Split(path string) (dir, file string) { 451 return vfs.baseFS.Split(path) 452 } 453 454 // Stat returns a FileInfo describing the named file. 455 // If there is an error, it will be of type *PathError. 456 func (vfs *RoFS) Stat(name string) (fs.FileInfo, error) { 457 return vfs.baseFS.Stat(name) 458 } 459 460 // Sub returns an FS corresponding to the subtree rooted at dir. 461 func (vfs *RoFS) Sub(dir string) (avfs.VFS, error) { 462 return vfs.baseFS.Sub(dir) 463 } 464 465 // Symlink creates newname as a symbolic link to oldname. 466 // If there is an error, it will be of type *LinkError. 467 func (vfs *RoFS) Symlink(oldname, newname string) error { 468 const op = "symlink" 469 470 err := vfs.errPermDenied 471 if vfs.OSType() == avfs.OsWindows { 472 err = avfs.ErrWinPrivilegeNotHeld 473 } 474 475 return &os.LinkError{Op: op, Old: oldname, New: newname, Err: err} 476 } 477 478 // TempDir returns the default directory to use for temporary files. 479 // 480 // On Unix systems, it returns $TMPDIR if non-empty, else /tmp. 481 // On Windows, it uses GetTempPath, returning the first non-empty 482 // value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory. 483 // On Plan 9, it returns /tmp. 484 // 485 // The directory is neither guaranteed to exist nor have accessible 486 // permissions. 487 func (vfs *RoFS) TempDir() string { 488 return vfs.baseFS.TempDir() 489 } 490 491 // ToSlash returns the result of replacing each separator character 492 // in path with a slash ('/') character. Multiple separators are 493 // replaced by multiple slashes. 494 func (vfs *RoFS) ToSlash(path string) string { 495 return vfs.baseFS.ToSlash(path) 496 } 497 498 // ToSysStat takes a value from fs.FileInfo.Sys() and returns a value that implements interface avfs.SysStater. 499 func (vfs *RoFS) ToSysStat(info fs.FileInfo) avfs.SysStater { 500 return vfs.baseFS.ToSysStat(info) 501 } 502 503 // Truncate changes the size of the named file. 504 // If the file is a symbolic link, it changes the size of the link's target. 505 // If there is an error, it will be of type *PathError. 506 func (vfs *RoFS) Truncate(name string, size int64) error { 507 const op = "truncate" 508 509 return &fs.PathError{Op: op, Path: name, Err: vfs.errPermDenied} 510 } 511 512 // UMask returns the file mode creation mask. 513 func (vfs *RoFS) UMask() fs.FileMode { 514 return vfs.baseFS.UMask() 515 } 516 517 // User returns the current user. 518 func (vfs *RoFS) User() avfs.UserReader { 519 return vfs.baseFS.User() 520 } 521 522 // WalkDir walks the file tree rooted at root, calling fn for each file or 523 // directory in the tree, including root. 524 // 525 // All errors that arise visiting files and directories are filtered by fn: 526 // see the fs.WalkDirFunc documentation for details. 527 // 528 // The files are walked in lexical order, which makes the output deterministic 529 // but requires WalkDir to read an entire directory into memory before proceeding 530 // to walk that directory. 531 // 532 // WalkDir does not follow symbolic links. 533 func (vfs *RoFS) WalkDir(root string, fn fs.WalkDirFunc) error { 534 return vfs.baseFS.WalkDir(root, fn) 535 } 536 537 // WriteFile writes data to a file named by filename. 538 // If the file does not exist, WriteFile creates it with permissions perm; 539 // otherwise WriteFile truncates it before writing. 540 func (vfs *RoFS) WriteFile(filename string, data []byte, perm fs.FileMode) error { 541 const op = "open" 542 543 return &fs.PathError{Op: op, Path: filename, Err: vfs.errPermDenied} 544 }