summaryrefslogtreecommitdiff
path: root/cmd/btrfs-mount/subvol.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-07-05 03:46:48 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-07-05 03:46:48 -0600
commitbfbb9057c859b6d019e1a330dc648fe58f0b9a7e (patch)
treebccaf1670e70703617efa225bda0e578188e0e08 /cmd/btrfs-mount/subvol.go
parente401e0b17034d59b48ad42c076705408415f00f7 (diff)
wip better fuse
Diffstat (limited to 'cmd/btrfs-mount/subvol.go')
-rw-r--r--cmd/btrfs-mount/subvol.go169
1 files changed, 169 insertions, 0 deletions
diff --git a/cmd/btrfs-mount/subvol.go b/cmd/btrfs-mount/subvol.go
new file mode 100644
index 0000000..7c6a7ac
--- /dev/null
+++ b/cmd/btrfs-mount/subvol.go
@@ -0,0 +1,169 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "github.com/datawire/dlib/dcontext"
+ "github.com/datawire/dlib/dlog"
+ "github.com/jacobsa/fuse"
+ "github.com/jacobsa/fuse/fuseops"
+ "github.com/jacobsa/fuse/fuseutil"
+
+ "lukeshu.com/btrfs-tools/pkg/btrfs"
+ "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem"
+ "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol"
+)
+
+type Subvolume struct {
+ FS *btrfs.FS
+ DeviceName string
+ Mountpoint string
+ TreeID btrfs.ObjID
+
+ fuseutil.NotImplementedFileSystem
+
+ rootOnce sync.Once
+ rootVal btrfsitem.Root
+ rootErr error
+}
+
+func (sv *Subvolume) Run(ctx context.Context) error {
+ mount, err := fuse.Mount(
+ sv.Mountpoint,
+ fuseutil.NewFileSystemServer(sv),
+ &fuse.MountConfig{
+ OpContext: ctx,
+ ErrorLogger: dlog.StdLogger(ctx, dlog.LogLevelError),
+ DebugLogger: dlog.StdLogger(ctx, dlog.LogLevelDebug),
+
+ FSName: sv.DeviceName,
+ Subtype: "btrfs",
+
+ ReadOnly: true,
+ })
+ if err != nil {
+ return err
+ }
+ return mount.Join(dcontext.HardContext(ctx))
+}
+
+func (sv *Subvolume) initRoot() {
+ sv.rootOnce.Do(func() {
+ sb, err := sv.FS.Superblock()
+ if err != nil {
+ sv.rootErr = err
+ return
+ }
+
+ root, err := sv.FS.TreeLookup(sb.Data.RootTree, btrfs.Key{
+ ObjectID: sv.TreeID,
+ ItemType: btrfsitem.ROOT_ITEM_KEY,
+ Offset: 0,
+ })
+ if err != nil {
+ sv.rootErr = err
+ return
+ }
+
+ rootBody, ok := root.Body.(btrfsitem.Root)
+ if !ok {
+ sv.rootErr = fmt.Errorf("FS_TREE_ ROOT_ITEM has malformed body")
+ return
+ }
+
+ sv.rootVal = rootBody
+ })
+}
+
+func (sv *Subvolume) getRootInode() (btrfs.ObjID, error) {
+ sv.initRoot()
+ return sv.rootVal.RootDirID, sv.rootErr
+}
+
+func (sv *Subvolume) getFSTree() (btrfsvol.LogicalAddr, error) {
+ sv.initRoot()
+ return sv.rootVal.ByteNr, sv.rootErr
+}
+
+func (sv *Subvolume) StatFS(_ context.Context, op *fuseops.StatFSOp) error {
+ // See linux.git/fs/btrfs/super.c:btrfs_statfs()
+ sb, err := sv.FS.Superblock()
+ if err != nil {
+ return err
+ }
+
+ op.IoSize = sb.Data.SectorSize
+ op.BlockSize = sb.Data.SectorSize
+ op.Blocks = sb.Data.TotalBytes / uint64(sb.Data.SectorSize) // TODO: adjust for RAID type
+ //op.BlocksFree = TODO
+
+ // btrfs doesn't have a fixed number of inodes
+ op.Inodes = 0
+ op.InodesFree = 0
+
+ // jacobsa/fuse doesn't expose namelen, instead hard-coding it
+ // to 255. Which is fine by us, because that's what it is for
+ // btrfs.
+
+ return nil
+}
+
+// func (sv *Subvolume) LookUpInode(_ context.Context, op *fuseops.LookUpInodeOp) error {}
+
+func (sv *Subvolume) GetInodeAttributes(ctx context.Context, op *fuseops.GetInodeAttributesOp) error {
+ if op.Inode == fuseops.RootInodeID {
+ inode, err := sv.getRootInode()
+ if err != nil {
+ return err
+ }
+ op.Inode = fuseops.InodeID(inode)
+ }
+
+ tree, err := sv.getFSTree()
+ if err != nil {
+ return err
+ }
+
+ item, err := sv.FS.TreeLookup(tree, btrfs.Key{
+ ObjectID: btrfs.ObjID(op.Inode),
+ ItemType: btrfsitem.INODE_ITEM_KEY,
+ Offset: 0,
+ })
+ if err != nil {
+ return err
+ }
+
+ itemBody, ok := item.Body.(btrfsitem.Inode)
+ if !ok {
+ return fmt.Errorf("malformed inode")
+ }
+
+ op.Attributes = fuseops.InodeAttributes{
+ Size: uint64(itemBody.Size),
+ Nlink: uint32(itemBody.NLink),
+ Mode: uint32(itemBody.Mode),
+ //RDev: itemBody.Rdev, // jacobsa/fuse doesn't expose rdev
+ Atime: itemBody.ATime.ToStd(),
+ Mtime: itemBody.MTime.ToStd(),
+ Ctime: itemBody.CTime.ToStd(),
+ //Crtime: itemBody.OTime,
+ Uid: uint32(itemBody.UID),
+ Gid: uint32(itemBody.GID),
+ }
+ return nil
+}
+
+// func (sv *Subvolume) ForgetInode(_ context.Context, op *fuseops.ForgetInodeOp) error {}
+// func (sv *Subvolume) BatchForget(_ context.Context, op *fuseops.BatchForgetOp) error {}
+// func (sv *Subvolume) OpenDir(_ context.Context, op *fuseops.OpenDirOp) error {}
+// func (sv *Subvolume) ReadDir(_ context.Context, op *fuseops.ReadDirOp) error {}
+// func (sv *Subvolume) ReleaseDirHandle(_ context.Context, op *fuseops.ReleaseDirHandleOp) error {}
+// func (sv *Subvolume) OpenFile(_ context.Context, op *fuseops.OpenFileOp) error {}
+// func (sv *Subvolume) ReadFile(_ context.Context, op *fuseops.ReadFileOp) error {}
+// func (sv *Subvolume) ReleaseFileHandle(_ context.Context, op *fuseops.ReleaseFileHandleOp) error {}
+// func (sv *Subvolume) ReadSymlink(_ context.Context, op *fuseops.ReadSymlinkOp) error {}
+// func (sv *Subvolume) GetXattr(_ context.Context, op *fuseops.GetXattrOp) error {}
+// func (sv *Subvolume) ListXattr(_ context.Context, op *fuseops.ListXattrOp) error {}
+// func (sv *Subvolume) Destroy() {}