summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/btrfs-mount/main.go190
-rw-r--r--go.mod4
-rw-r--r--go.sum8
3 files changed, 202 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() {}
diff --git a/go.mod b/go.mod
index 07ce874..ece061f 100644
--- a/go.mod
+++ b/go.mod
@@ -5,13 +5,17 @@ go 1.18
require (
github.com/datawire/dlib v1.3.0
github.com/davecgh/go-spew v1.1.1
+ github.com/jacobsa/fuse v0.0.0-20220702091825-13117049f383
+ github.com/sirupsen/logrus v1.6.0
github.com/stretchr/testify v1.7.1
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf
golang.org/x/text v0.3.7
)
require (
+ github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
+ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
diff --git a/go.sum b/go.sum
index 5d6b3e0..06b22dd 100644
--- a/go.sum
+++ b/go.sum
@@ -3,16 +3,21 @@ github.com/datawire/dlib v1.3.0/go.mod h1:NiGDmetmbkBvtznpWSx6C0vA0s0LK9aHna3LJD
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/jacobsa/fuse v0.0.0-20220702091825-13117049f383 h1:LmgK5WyqEu12BdEFkD5XxNxyK1SFk5Iz4TQsq96NxQM=
+github.com/jacobsa/fuse v0.0.0-20220702091825-13117049f383/go.mod h1:liOmRdJd8oTwHCQ5M9JemRE3CebdlYcZWLk+ZjQeuq0=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -22,8 +27,11 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw=
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220526153639-5463443f8c37 h1:lUkvobShwKsOesNfWWlCS5q7fnbG1MEliIzwu886fn8=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=