diff options
Diffstat (limited to 'cmd/btrfs-mount')
-rw-r--r-- | cmd/btrfs-mount/main.go | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/cmd/btrfs-mount/main.go b/cmd/btrfs-mount/main.go new file mode 100644 index 0000000..8e07e77 --- /dev/null +++ b/cmd/btrfs-mount/main.go @@ -0,0 +1,190 @@ +package main + +import ( + "context" + "fmt" + "os" + "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" +) + +func main() { + ctx := context.Background() + logger := logrus.New() + logger.SetLevel(logrus.TraceLevel) + ctx = dlog.WithLogger(ctx, dlog.WrapLogrus(logger)) + + grp := dgroup.NewGroup(ctx, dgroup.GroupConfig{ + EnableSignalHandling: true, + }) + grp.Go("main", func(ctx context.Context) error { + return Main(ctx, os.Args[1], os.Args[2:]...) + }) + if err := grp.Wait(); err != nil { + fmt.Fprintf(os.Stderr, "%v: error: %v\n", os.Args[0], err) + os.Exit(1) + } +} + +func Main(ctx context.Context, mountpoint string, imgfilenames ...string) (err error) { + maybeSetErr := func(_err error) { + if _err != nil && err == nil { + err = _err + } + } + + fs, err := btrfsmisc.Open(os.O_RDONLY, imgfilenames...) + if err != nil { + return err + } + defer func() { + 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 { + <-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 + }) + 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() {} |