summaryrefslogtreecommitdiff
path: root/cmd/btrfs-mount
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
parente401e0b17034d59b48ad42c076705408415f00f7 (diff)
wip better fuse
Diffstat (limited to 'cmd/btrfs-mount')
-rw-r--r--cmd/btrfs-mount/main.go144
-rw-r--r--cmd/btrfs-mount/subvol.go169
2 files changed, 187 insertions, 126 deletions
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() {}