From bfbb9057c859b6d019e1a330dc648fe58f0b9a7e Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Tue, 5 Jul 2022 03:46:48 -0600 Subject: wip better fuse --- cmd/btrfs-mount/subvol.go | 169 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 cmd/btrfs-mount/subvol.go (limited to 'cmd/btrfs-mount/subvol.go') 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() {} -- cgit v1.2.3-2-g168b