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/main.go | 144 +++++---------------------------------- cmd/btrfs-mount/subvol.go | 169 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+), 126 deletions(-) create mode 100644 cmd/btrfs-mount/subvol.go (limited to 'cmd/btrfs-mount') diff --git a/cmd/btrfs-mount/main.go b/cmd/btrfs-mount/main.go index 8e07e77..e059351 100644 --- a/cmd/btrfs-mount/main.go +++ b/cmd/btrfs-mount/main.go @@ -7,16 +7,12 @@ import ( "path/filepath" "sync/atomic" - "github.com/datawire/dlib/dcontext" "github.com/datawire/dlib/dgroup" "github.com/datawire/dlib/dlog" "github.com/jacobsa/fuse" - "github.com/jacobsa/fuse/fuseops" - "github.com/jacobsa/fuse/fuseutil" "github.com/sirupsen/logrus" "lukeshu.com/btrfs-tools/pkg/btrfs" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" "lukeshu.com/btrfs-tools/pkg/btrfsmisc" ) @@ -38,6 +34,14 @@ func main() { } } +func tryAbs(rel string) string { + abs, err := filepath.Abs(rel) + if err != nil { + return rel + } + return abs +} + func Main(ctx context.Context, mountpoint string, imgfilenames ...string) (err error) { maybeSetErr := func(_err error) { if _err != nil && err == nil { @@ -53,138 +57,26 @@ func Main(ctx context.Context, mountpoint string, imgfilenames ...string) (err e maybeSetErr(fs.Close()) }() - fsAdapter := &FSAdapter{ - fs: fs, - } - - devname, err := filepath.Abs(imgfilenames[0]) - if err != nil { - devname = imgfilenames[0] - } - mount, err := fuse.Mount( - mountpoint, - fuseutil.NewFileSystemServer(fsAdapter), - &fuse.MountConfig{ - OpContext: ctx, - ErrorLogger: dlog.StdLogger(ctx, dlog.LogLevelError), - DebugLogger: dlog.StdLogger(ctx, dlog.LogLevelDebug), - - FSName: devname, - Subtype: "btrfs", - - ReadOnly: true, - }) - if err != nil { - return err - } - mounted := uint32(1) grp := dgroup.NewGroup(ctx, dgroup.GroupConfig{ ShutdownOnNonError: true, }) - grp.Go("send-unmount", func(ctx context.Context) error { + grp.Go("shutdown", func(ctx context.Context) error { <-ctx.Done() if atomic.LoadUint32(&mounted) == 0 { return nil } return fuse.Unmount(os.Args[1]) }) - grp.Go("recv-unmount", func(ctx context.Context) error { - ret := mount.Join(dcontext.HardContext(ctx)) - atomic.StoreUint32(&mounted, 0) - return ret + grp.Go("main", func(ctx context.Context) error { + defer atomic.StoreUint32(&mounted, 0) + rootSubvol := &Subvolume{ + FS: fs, + DeviceName: tryAbs(imgfilenames[0]), + Mountpoint: mountpoint, + TreeID: btrfs.FS_TREE_OBJECTID, + } + return rootSubvol.Run(ctx) }) return grp.Wait() } - -type FSAdapter struct { - fuseutil.NotImplementedFileSystem - - fs *btrfs.FS - - rootInodeOnce sync.Once - rootInodeVal btrfs.ObjID - rootInodeErr error - - inode2treeMu sync.Mutex - inode2tree map[btrfs.ObjID]btrfsvol.LogicalAddr -} - -func (a *FSAdapter) getRootInode() (btrfs.ObjID, error) { - a.rootInodeOnce.Do(func() { - a.rootInodeVal, a.rootInodeErr = func() (btrfs.ObjID, error) { - sb, err := a.fs.Superblock() - if err != nil { - return 0, err - } - - root, err := fs.TreeLookup(sb.Data.RootTree, btrfs.Key{ - ObjectID: btrfs.FS_TREE_OBJECTID, - ItemType: btrfsitem.ROOT_ITEM_KEY, - Offset: 0, - }) - if err != nil { - return 0, err - } - rootBody, ok := root.Body.(btrfsitem.Root) - if !ok { - return 0, fmt.Errorf("FS_TREE_ ROOT_ITEM has malformed body") - } - return rootBody.RootDirID, nil - }() - }) - return a.rootInodeVal, a.rootInodeErr -} - -func (a *FSAdapter) StatFS(_ context.Context, op *fuseops.StatFSOp) error { - // See linux.git/fs/btrfs/super.c:btrfs_statfs() - sb, err := a.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 (a *FSAdapter) LookUpInode(_ context.Context, op *fuseops.LookUpInodeOp) error {} - -func (a *FSAdapter) GetInodeAttributes(ctx context.Context, op *fuseops.GetInodeAttributesOp) error { - sb, err := a.fs.Superblock() - if err != nil { - return err - } - - if op.Inode == fuseops.RootInodeID { - io.Inode, err = a.getRootInode() - if err != nil { - return err - } - } - -} - -// func (a *FSAdapter) ForgetInode(_ context.Context, op *fuseops.ForgetInodeOp) error {} -// func (a *FSAdapter) BatchForget(_ context.Context, op *fuseops.BatchForgetOp) error {} -// func (a *FSAdapter) OpenDir(_ context.Context, op *fuseops.OpenDirOp) error {} -// func (a *FSAdapter) ReadDir(_ context.Context, op *fuseops.ReadDirOp) error {} -// func (a *FSAdapter) ReleaseDirHandle(_ context.Context, op *fuseops.ReleaseDirHandleOp) error {} -// func (a *FSAdapter) OpenFile(_ context.Context, op *fuseops.OpenFileOp) error {} -// func (a *FSAdapter) ReadFile(_ context.Context, op *fuseops.ReadFileOp) error {} -// func (a *FSAdapter) ReleaseFileHandle(_ context.Context, op *fuseops.ReleaseFileHandleOp) error {} -// func (a *FSAdapter) ReadSymlink(_ context.Context, op *fuseops.ReadSymlinkOp) error {} -// func (a *FSAdapter) GetXattr(_ context.Context, op *fuseops.GetXattrOp) error {} -// func (a *FSAdapter) ListXattr(_ context.Context, op *fuseops.ListXattrOp) error {} -// func (a *FSAdapter) Destroy() {} 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