Documentation
¶
Index ¶
- func SymlinkTarget(f *mfs.File) string
- type Config
- type Dir
- func (d *Dir) Create(ctx context.Context, name string, flags uint32, _ uint32, out *fuse.EntryOut) (*fs.Inode, fs.FileHandle, uint32, syscall.Errno)
- func (d *Dir) Getattr(_ context.Context, _ fs.FileHandle, out *fuse.AttrOut) syscall.Errno
- func (d *Dir) Getxattr(_ context.Context, attr string, dest []byte) (uint32, syscall.Errno)
- func (d *Dir) Listxattr(_ context.Context, dest []byte) (uint32, syscall.Errno)
- func (d *Dir) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno)
- func (d *Dir) Mkdir(ctx context.Context, name string, _ uint32, out *fuse.EntryOut) (*fs.Inode, syscall.Errno)
- func (d *Dir) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno)
- func (d *Dir) Rename(_ context.Context, oldName string, newParent fs.InodeEmbedder, newName string, ...) syscall.Errno
- func (d *Dir) Rmdir(ctx context.Context, name string) syscall.Errno
- func (d *Dir) Setattr(_ context.Context, _ fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
- func (d *Dir) Statfs(_ context.Context, out *fuse.StatfsOut) syscall.Errno
- func (d *Dir) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno)
- func (d *Dir) Unlink(_ context.Context, name string) syscall.Errno
- type FileHandle
- func (fh *FileHandle) Flush(_ context.Context) syscall.Errno
- func (fh *FileHandle) Fsync(_ context.Context, _ uint32) syscall.Errno
- func (fh *FileHandle) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
- func (fh *FileHandle) Release(_ context.Context) syscall.Errno
- func (fh *FileHandle) Write(_ context.Context, data []byte, off int64) (uint32, syscall.Errno)
- type FileInode
- func (fi *FileInode) Getattr(_ context.Context, _ fs.FileHandle, out *fuse.AttrOut) syscall.Errno
- func (fi *FileInode) Getxattr(_ context.Context, attr string, dest []byte) (uint32, syscall.Errno)
- func (fi *FileInode) Listxattr(_ context.Context, dest []byte) (uint32, syscall.Errno)
- func (fi *FileInode) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno)
- func (fi *FileInode) Setattr(_ context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
- type Symlink
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SymlinkTarget ¶
SymlinkTarget extracts the symlink target from an MFS file, or returns "" if the file is not a TSymlink node. MFS represents symlinks as *mfs.File, so the DAG node's UnixFS type must be checked.
Types ¶
type Config ¶
type Config struct {
StoreMtime bool // persist mtime on create and open-for-write
StoreMode bool // persist mode on chmod
DAG ipld.DAGService // required: read-only opens use it to bypass MFS desclock
// RepoPath is the on-disk path of the IPFS repo (e.g. ~/.ipfs).
// Statfs calls syscall.Statfs on this path so that the FUSE mount
// reports how much free space is left on the volume that stores
// MFS data. Without it tools like macOS Finder see zero free space
// and refuse to copy files.
RepoPath string
// Blksize is the preferred I/O size advertised via st_blksize on
// every stat. Callers should derive it from Import.UnixFSChunker via
// fusemnt.BlksizeFromChunker so the hint matches the chunker MFS
// will use for writes. If zero, NewDir writes fusemnt.DefaultBlksize
// into this field in place, so fillAttr on every inode can read
// cfg.Blksize without a nil-check on each stat.
Blksize uint32
}
Config controls write-side behavior for writable mounts.
type Dir ¶
Dir is the FUSE adapter for MFS directories.
func NewDir ¶
NewDir creates a Dir node backed by the given MFS directory. cfg.DAG is required: read-only file opens build a DagReader directly from it to avoid MFS's desclock (see FileInode.Open). Passing a nil DAG would silently re-introduce the rsync --inplace deadlock, so we fail loudly at construction time instead.
func (*Dir) Mkdir ¶
func (d *Dir) Mkdir(ctx context.Context, name string, _ uint32, out *fuse.EntryOut) (*fs.Inode, syscall.Errno)
Mkdir creates a new directory under d.
TODO: boxo's mfs.Directory.Mkdir(name string) accepts no mode argument, so the caller's mode is silently dropped here. Tools that mkdir then chown without a follow-up chmod (some tar/rsync flows) see the default 0755 instead of the requested mode. Fixing this requires a boxo MFS API change.
func (*Dir) Rename ¶
func (d *Dir) Rename(_ context.Context, oldName string, newParent fs.InodeEmbedder, newName string, _ uint32) syscall.Errno
Rename moves an entry across MFS directories.
TODO: this is not atomic. The source is unlinked before the destination is added, so any failure between the two steps loses the source entry. Making it atomic requires changes to MFS rename semantics (boxo/mfs does not currently expose an atomic rename).
func (*Dir) Setattr ¶
func (d *Dir) Setattr(_ context.Context, _ fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
Setattr handles chmod and mtime changes on directories. Tools like tar and rsync set directory timestamps after extraction.
Mode and mtime are stored as UnixFS optional metadata. The UnixFS spec supports all 12 permission bits, but boxo's MFS layer exposes only the lower 9 (ugo-rwx); setuid/setgid/sticky are silently dropped. FUSE mounts are always nosuid so these bits would have no execution effect anyway. See https://specs.ipfs.tech/unixfs/#dag-pb-optional-metadata
func (*Dir) Statfs ¶
Statfs reports disk-space statistics for the underlying filesystem. macOS Finder checks free space before copying; without this it reports "not enough free space" because go-fuse returns zeroed stats.
type FileHandle ¶
type FileHandle struct {
// contains filtered or unexported fields
}
FileHandle wraps an MFS file descriptor for FUSE operations. All methods are serialized by mu because the FUSE server dispatches each request in its own goroutine and the underlying DagModifier is not safe for concurrent use.
func (*FileHandle) Flush ¶
func (fh *FileHandle) Flush(_ context.Context) syscall.Errno
Flush persists buffered writes to the DAG and invalidates the kernel's cached attrs so the next stat sees the updated size.
We intentionally ignore ctx: the underlying MFS flush cannot be safely canceled mid-operation, and abandoning it would leak a background goroutine that races with the subsequent Release.
Cache invalidation happens here (in addition to Release) because the kernel calls Flush synchronously inside close() but sends Release asynchronously after close() returns. Without this, a stat() immediately after close() could see stale cached attrs.
func (*FileHandle) Fsync ¶
Fsync flushes the write buffer through the open file descriptor and invalidates the kernel's cached attrs and content for this inode. Editors (vim, emacs) and databases call fsync after writing to ensure data reaches persistent storage; a fresh reader on the same path must see the synced bytes immediately, not the size the kernel cached from the initial Create response.
func (*FileHandle) Read ¶
func (fh *FileHandle) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
func (*FileHandle) Release ¶
func (fh *FileHandle) Release(_ context.Context) syscall.Errno
Release closes the descriptor and invalidates the kernel's cached content and attrs so readers opening the same path see the new data. Invalidation happens here (not in Flush) because fd.Close commits the final DAG node; Flush alone may not have the final size yet.
type FileInode ¶
FileInode is the FUSE adapter for MFS file inodes.
func (*FileInode) Setattr ¶
func (fi *FileInode) Setattr(_ context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
Setattr handles chmod, mtime changes (touch), and ftruncate.
Mode and mtime are stored as UnixFS optional metadata. The UnixFS spec supports all 12 permission bits, but boxo's MFS layer exposes only the lower 9 (ugo-rwx); setuid/setgid/sticky are silently dropped. FUSE mounts are always nosuid so these bits would have no execution effect anyway. See https://specs.ipfs.tech/unixfs/#dag-pb-optional-metadata
With hanwen/go-fuse, the kernel passes the open file handle (fh) when the caller uses ftruncate(fd, size). This lets us truncate through the existing write descriptor without opening a second one. For truncate(path, size) without a handle, a temporary descriptor is opened; this may block if another writer holds MFS's desclock.
type Symlink ¶
type Symlink struct {
fs.Inode
Target string
MFSFile *mfs.File // backing MFS node for mtime persistence
Cfg *Config
}
Symlink is the FUSE adapter for UnixFS TSymlink nodes on writable mounts. Target is resolved once at Lookup/Create time and never changes (POSIX symlinks are immutable; changing the target requires unlink + symlink).
func (*Symlink) Setattr ¶
func (s *Symlink) Setattr(_ context.Context, _ fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
Setattr handles mtime changes on symlinks. Tools like rsync call lutimes on symlinks after creating them and treat ENOTSUP as an error. Every major FUSE filesystem (gocryptfs, rclone, sshfs, s3fs) implements Setattr on symlinks for this reason.
Mode is always 0777 per POSIX convention (access control uses the target's mode), so chmod requests are silently accepted but not stored.