mountfs

package
v0.56.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package mountfs is the filesystem behind `filex mount` — a remote filex server, seen as a folder on the machine that runs the command.

Why this exists when NFS and SFTP already do

Both of those need something on the other end that filex does not control: NFSv3 has no encryption and belongs on a LAN, and an SFTP mount needs sshfs or WinFsp installed and configured. `filex mount` is the one that works from anywhere over the same HTTPS the web app uses, with the same session token, through whatever proxy or tunnel is already between the two — because it is just the REST API with a filesystem in front of it.

What it is not

It is not a sync. Nothing is copied to the machine except a bounded read cache; closing the mount leaves nothing behind. `filex sync` is still the answer for "keep a folder on my laptop", and this is the answer for "open one file out of a hundred thousand without downloading the other 99,999".

The shape, and the reason for it

Reads are cached in fixed blocks. A mounted filesystem gets asked for 128 KiB at arbitrary offsets by whatever program the user opened, and answering each one with its own HTTPS request would make a video unplayable; a block cache turns a sequential read into a handful of large requests and a random seek into one.

Writes are spooled whole and uploaded on close. That is not laziness: the REST API takes a complete file, there is no ranged write on the wire, and a partial upload committed under the real name would replace a good file with a torn one. The cost is that a 4 GB write needs 4 GB of scratch space, which is bounded and stated rather than discovered.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrMountFailed — the driver refused. Almost always a mountpoint that is
	// in use, or a drive letter that is taken.
	ErrMountFailed = errors.New(
		"the filesystem driver refused to mount (is the drive letter or folder already in use?)")
	// ErrMountTimeout — it neither failed nor came up. On Windows this is
	// usually WinFsp missing: the DLL loads, the mount never registers.
	ErrMountTimeout = errors.New(
		"the mount did not come up (on Windows, check that WinFsp is installed - https://winfsp.dev)")
	// ErrUnmountFailed — the driver would not let go.
	ErrUnmountFailed = errors.New("could not unmount")
)

Mount failures, shared so a caller can tell them apart on any platform.

View Source
var ErrTooLarge = errors.New("mountfs: file is larger than the write spool allows")

ErrTooLarge is a file bigger than the write spool allows.

View Source
var ErrUnsupported = errors.New(
	"filex mount needs a filesystem driver, and on this platform there is none " +
		"it can use. Linux needs nothing (FUSE is in the kernel) and Windows " +
		"needs WinFsp, a free separate install - https://winfsp.dev. macOS is " +
		"not supported: it needs macFUSE, whose licence forbids a commercial " +
		"program from installing it and whose Go binding needs a C toolchain " +
		"filex deliberately does not use. There, use `filex sync` for a folder, " +
		"or the desktop app")

ErrUnsupported is what Mount returns where there is no FUSE driver.

⚠ Declared HERE rather than in the platform file so a caller can test for it on every OS — a Linux build that cannot see the sentinel would print a raw error instead of the sentence that tells somebody what to install.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Client talks to the remote filex. Required.
	Client *cliclient.Client
	// Remote is what to mount: "" for every storage the account can see,
	// "docs://" for one storage, "docs://projects/acme" for a subtree.
	Remote string
	// ReadOnly refuses every write. The safe default for a mount somebody
	// pointed at a production server to look at something.
	ReadOnly bool
	// BlockSize is the read granularity. 0 uses defaultBlockSize.
	BlockSize int64
	// CacheBlocks bounds the read cache, in blocks. 0 uses the default.
	CacheBlocks int
	// SpoolDir is where writes are spooled. Empty uses the OS temp dir.
	SpoolDir string
	// MaxSpool caps one write. 0 uses the default.
	MaxSpool int64
	// AttrTTL is how long a listing or a stat is trusted.
	//
	// ⚠ Not zero. A filesystem call that always goes to the network makes `ls`
	// on a large folder take seconds and a shell tab-completion feel broken;
	// but a long TTL means somebody else's upload is invisible for that long.
	// A few seconds is the honest middle, and it is a knob rather than a
	// constant because the right answer differs between a laptop and a build
	// machine.
	AttrTTL time.Duration
}

Config wires the filesystem to a remote server.

type ErrKind

type ErrKind int

ErrKind is what went wrong, in terms a filesystem can express.

const (
	// ErrNone is success.
	ErrNone ErrKind = iota
	// ErrIO is the fallback: something failed and nothing more precise fits.
	ErrIO
	// ErrNotFound — the path is not there.
	ErrNotFound
	// ErrDenied — the caller may not do this.
	ErrDenied
	// ErrExists — something is already there.
	ErrExists
	// ErrReadOnlyFS — the mount, or the storage behind it, refuses writes.
	ErrReadOnlyFS
	// ErrNoSpace — a quota or a disk said no. ⚠ Distinct from ErrDenied on
	// purpose: "you are out of space" is a thing the user can act on, and
	// every file manager on both platforms shows it differently.
	ErrNoSpace
	// ErrTooBig — the file is larger than the write spool allows.
	ErrTooBig
)

func Classify

func Classify(err error) ErrKind

Classify turns an error from the filex client into an ErrKind.

type FS

type FS struct {
	// contains filtered or unexported fields
}

FS is the remote filesystem.

func New

func New(cfg Config) (*FS, error)

New builds a filesystem.

func (*FS) Describe

func (f *FS) Describe() string

Describe is what the command prints when the mount comes up.

func (*FS) Forget

func (f *FS) Forget(p string)

Forget drops cached metadata for a path and its parent.

⚠ The PARENT too, and that is the half people forget: after a create or a delete the parent's LISTING is what is wrong, and a stale listing is how a file the user just made does not appear in `ls`.

func (*FS) MaxSpool

func (f *FS) MaxSpool() int64

MaxSpool is the write ceiling.

func (*FS) Mkdir

func (f *FS) Mkdir(ctx context.Context, p string) error

Mkdir creates a directory.

func (*FS) ReadAt

func (f *FS) ReadAt(ctx context.Context, mountPath string, dst []byte, off int64, size int64) (int, error)

ReadAt fills p from the remote file at off, through the block cache.

func (*FS) ReadDir

func (f *FS) ReadDir(ctx context.Context, p string) ([]*cliclient.Stat, error)

ReadDir lists a directory.

func (*FS) ReadOnly

func (f *FS) ReadOnly() bool

ReadOnly reports whether writes are refused.

func (*FS) Remove

func (f *FS) Remove(ctx context.Context, p string) error

Remove deletes a file or directory.

⚠ It goes to the filex TRASH, like every other surface — the server decides that, not this client. Worth knowing when a user "deletes" from a mount and expects the space back immediately.

func (*FS) Rename

func (f *FS) Rename(ctx context.Context, oldPath, newPath string) error

Rename moves a path.

func (*FS) SpoolPath

func (f *FS) SpoolPath() (*os.File, error)

SpoolPath returns a scratch file for a write in flight.

func (*FS) Stat

func (f *FS) Stat(ctx context.Context, p string) (*cliclient.Stat, error)

Stat returns metadata for a mount-relative path.

func (*FS) Upload

func (f *FS) Upload(ctx context.Context, mountPath, localPath string) error

Upload writes a whole local file to a remote path.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is a live mount.

func Mount

func Mount(fsys *FS, mountpoint string, debug bool) (*Server, error)

Mount attaches fsys at mountpoint and blocks until it is unmounted.

func (*Server) Mountpoint

func (s *Server) Mountpoint() string

Mountpoint is where it is attached.

func (*Server) Unmount

func (s *Server) Unmount() error

Unmount detaches it.

⚠ A mount left behind after the process dies is worse than no mount: every `ls` in that directory hangs until somebody runs `fusermount -u` by hand. The command that owns this always unmounts on the way out, including on a signal.

func (*Server) Wait

func (s *Server) Wait()

Wait blocks until the filesystem is unmounted.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL