Documentation
¶
Overview ¶
Package osfs serves a directory of the host as a github.com/go-filesystems/interface Filesystem.
Every other driver in the go-filesystems org decodes an on-disk format out of an image. This one has no format: it hands each call to the host kernel, relative to a directory opened once with os.OpenRoot. It exists so that a server written against the interface (go-fileshare/fileshare serves SMB, NFS, WebDAV, SFTP and S3 that way) can export a directory tree — a container volume, a home directory — exactly as it exports an image.
Confinement ¶
The point of the package is that a client cannot leave the tree. Every path operation goes through the os.Root, never through a path joined by hand, so the confinement is the kernel-backed one os.Root implements (openat and friends, resolved one component at a time), not a string check that a clever path could get past:
- ".." that climbs above the tree is refused;
- a symbolic link whose target lies outside the tree is refused when it is followed — whether a client created it through FS.Symlink or it was already on the host;
- a symbolic link inside the tree that stays inside the tree works.
Symlink stores its target as given (the interface requires it, and so does os.Root), so a client CAN create a link that points at /etc. What it cannot do is make the server follow it: ReadFile, OpenFile, WriteFile, ListDir, Truncate, Chmod, Chown, Chtimes, Link and the rest all refuse a path that leaves the tree through such a link. Stat and ReadLink describe the link itself, which is what a network client needs to resolve it on its own side.
Opening ¶
Open takes a path. OpenRoot takes an os.Root the caller already holds and serves the tree through it, taking ownership of it: a server that checks a share's path and then opens it keeps the directory it checked, even if the path is swapped for a symbolic link afterwards, because the kernel resolves every call against the Root's descriptor.
Paths ¶
Paths are slash-separated and relative to the root of the tree; a leading "/" is accepted and means the same thing, so "/a/b", "a/b" and "//a/b" name one file and "", "/" and "." name the root. That matches the other drivers of the org. It follows that there is no way to name a host absolute path on Unix: "/etc/passwd" is the file etc/passwd INSIDE the tree.
What each call means here ¶
- Stat does not follow a final symbolic link (it is lstat), so servers see S_IFLNK and can answer READLINK. Mode carries the POSIX type and permission bits. Inode is the host's inode number on Unix; on other systems it is 0, which consumers already treat as "unknown".
- ListDir returns the entries sorted by name, without "." and "..". FileType is the dirent d_type value (DT_REG 8, DT_DIR 4, DT_LNK 10, DT_FIFO 1, DT_CHR 2, DT_BLK 6, DT_SOCK 12, DT_UNKNOWN 0).
- DeleteFile removes any non-directory, including a symbolic link (never its target). DeleteDir removes an EMPTY directory, as rmdir(2) does: it is not recursive, because every protocol server that calls it expects rmdir semantics and a recursive delete of a host tree is not a thing to get by accident. Neither is idempotent: a missing path is an error wrapping io/fs.ErrNotExist.
- ReadFile holds a whole file in memory, so it refuses one larger than DefaultMaxReadFile (1 GiB; see MaxReadFile) with an error wrapping ErrTooLarge, before allocating anything: a client that may write can Truncate a sparse file to any size. It never preallocates more than 1 MiB from the size a file claims; the buffer grows with what is read.
- ReadFile, OpenFile, WriteFile and Truncate only ever touch regular files. They open with O_NONBLOCK on Unix and check the type of what they opened, so a FIFO in the tree is refused instead of hanging the server.
- OpenFile returns a filesystem.WritableFile when the FS is writable and the host lets the file be opened read-write; otherwise a plain filesystem.File. Reads and writes are positional (pread/pwrite on the *os.File); nothing is buffered. Sync is fsync(2) (F_FULLFSYNC on Darwin, FlushFileBuffers on Windows), so "the backing store" is the host filesystem the directory lives on.
Read-only ¶
With ReadOnly, every mutating method returns an error wrapping ErrReadOnly before it touches anything, and files are opened O_RDONLY.
Index ¶
- Constants
- Variables
- type FS
- func (fsys *FS) Chmod(p string, perm os.FileMode) error
- func (fsys *FS) Chown(p string, uid, gid uint32) error
- func (fsys *FS) Chtimes(p string, atime, mtime time.Time) error
- func (fsys *FS) Close() error
- func (fsys *FS) DeleteDir(p string) error
- func (fsys *FS) DeleteFile(p string) error
- func (fsys *FS) Link(oldPath, newPath string) error
- func (fsys *FS) ListDir(p string) ([]filesystem.DirEntry, error)
- func (fsys *FS) MkDir(p string, perm os.FileMode) error
- func (fsys *FS) OpenFile(p string) (filesystem.File, error)
- func (fsys *FS) ReadFile(p string) ([]byte, error)
- func (fsys *FS) ReadLink(p string) (string, error)
- func (fsys *FS) Rename(oldPath, newPath string) error
- func (fsys *FS) Stat(p string) (filesystem.Stat, error)
- func (fsys *FS) Symlink(target, linkPath string) error
- func (fsys *FS) Truncate(p string, size int64) error
- func (fsys *FS) Usage() (total, free uint64, err error)
- func (fsys *FS) WriteFile(p string, data []byte, perm os.FileMode) error
- type Option
Constants ¶
const DefaultMaxReadFile = 1 << 30
DefaultMaxReadFile is the largest file, in bytes, that FS.ReadFile reads unless MaxReadFile says otherwise: 1 GiB.
ReadFile holds the whole file in memory, and a client that may write can make a sparse file of any size with Truncate (NFS SETATTR, SMB SET_INFO): without a limit, one request makes the server read terabytes of zeros into memory. Anything larger belongs to FS.OpenFile, which reads it piece by piece.
Variables ¶
var ErrReadOnly = errors.New("osfs: read-only filesystem")
ErrReadOnly is wrapped by the error every mutating method returns when the FS was opened with ReadOnly.
var ErrTooLarge = errors.New("osfs: file too large to read whole")
ErrTooLarge is wrapped by the error ReadFile returns for a file larger than the limit set with MaxReadFile (DefaultMaxReadFile unless changed).
Functions ¶
This section is empty.
Types ¶
type FS ¶
type FS struct {
// contains filtered or unexported fields
}
FS is a host directory tree, confined with os.Root. It is safe for concurrent use.
func Open ¶
Open opens the directory dir, which must exist and be a directory, as a filesystem. Nothing outside dir is reachable through the result.
func OpenRoot ¶ added in v0.2.0
OpenRoot serves the directory r was opened on, through r itself. Every call goes through r, so the tree is the one r holds: a caller that checked a path and then opened it with os.OpenRoot keeps what it checked, even if a component of that path is swapped for a symbolic link afterwards.
On success the FS owns r: FS.Close closes it, and the caller must not. On error r is left open and still belongs to the caller.
An FS opened this way knows no path for its tree. Where FS.Usage needs one it derives it from r's descriptor instead (see Usage).
func (*FS) Chmod ¶
Chmod sets the permission bits of p (following a final in-tree link).
On Windows a final symbolic link is changed itself rather than followed, as os.Root does there (go.dev/issue/71492); either way nothing outside the tree is touched.
func (*FS) Chown ¶
Chown sets the owner and group of p (following a final in-tree link). The host decides whether the caller may; on Windows it always fails.
func (*FS) DeleteDir ¶
DeleteDir removes the EMPTY directory at p, as rmdir(2) does. It is not recursive: a non-empty directory is an error.
func (*FS) DeleteFile ¶
DeleteFile removes the non-directory at p. A symbolic link is removed itself; its target is not touched.
func (*FS) Link ¶
Link creates newPath as a hard link to oldPath, which must not be a directory. If oldPath is a symbolic link, the link itself is linked.
func (*FS) ListDir ¶
func (fsys *FS) ListDir(p string) ([]filesystem.DirEntry, error)
ListDir returns the entries of the directory at p, sorted by name, without "." and "..". See the package documentation for the FileType values.
func (*FS) OpenFile ¶
func (fsys *FS) OpenFile(p string) (filesystem.File, error)
OpenFile opens the regular file at p for positional access. The result is a filesystem.WritableFile unless the FS is read-only or the host refuses to open the file for writing (its permissions, a read-only mount), in which case it is a plain, read-only filesystem.File.
func (*FS) ReadFile ¶
ReadFile returns the whole content of the regular file at p. A file larger than the limit (DefaultMaxReadFile, or as set with MaxReadFile) is an error wrapping ErrTooLarge; so is one that grows past it while being read.
func (*FS) Stat ¶
func (fsys *FS) Stat(p string) (filesystem.Stat, error)
Stat describes the file at p. A final symbolic link is not followed.
func (*FS) Symlink ¶
Symlink creates the symbolic link linkPath with the literal target. The target is not checked — it may point anywhere — but it is never followed out of the tree: see the package documentation.
type Option ¶
type Option func(*options)
Option configures Open and OpenRoot.
func MaxReadFile ¶ added in v0.2.0
MaxReadFile sets the largest file, in bytes, that FS.ReadFile reads; a larger one is an error wrapping ErrTooLarge, returned before anything is allocated for it. n <= 0 removes the limit — memory is then the only bound, so do that only where every writer is trusted. The default is DefaultMaxReadFile.
func ReadOnly ¶
func ReadOnly() Option
ReadOnly makes every mutating method return an error wrapping ErrReadOnly, and opens every file O_RDONLY.