summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-07-10 17:24:51 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-07-11 00:44:30 -0600
commitbde202f286461ab575dc7e3d83f996d9a5f4a6ec (patch)
tree64782c354c15f64a164996125a06c1bca30c9aa7
parentd2da99882ea49cc67780c0255bf624698898e7fe (diff)
Have a go at rearranging things in to a lib/btrfsprogs
-rw-r--r--cmd/btrfs-clear-bad-nodes/main.go84
-rw-r--r--cmd/btrfs-dump-tree/main.go76
-rw-r--r--cmd/btrfs-fsck/pass1.go6
-rw-r--r--cmd/btrfs-fsck/pass2.go4
-rw-r--r--cmd/btrfs-ls-files/main.go4
-rw-r--r--cmd/btrfs-ls-trees/main.go6
-rw-r--r--cmd/btrfs-mount/fuseutil.go56
-rw-r--r--cmd/btrfs-mount/main.go25
-rw-r--r--lib/btrfsprogs/btrfsinspect/mount.go (renamed from cmd/btrfs-mount/subvol_fuse.go)105
-rw-r--r--lib/btrfsprogs/btrfsinspect/print_tree.go (renamed from lib/btrfsmisc/print_tree.go)90
-rw-r--r--lib/btrfsprogs/btrfsrepair/clearnodes.go91
-rw-r--r--lib/btrfsprogs/btrfsutil/open.go (renamed from lib/btrfsmisc/open.go)2
-rw-r--r--lib/btrfsprogs/btrfsutil/scan.go (renamed from lib/btrfsmisc/fsck.go)2
-rw-r--r--lib/btrfsprogs/btrfsutil/walk.go (renamed from lib/btrfsmisc/walk.go)2
14 files changed, 281 insertions, 272 deletions
diff --git a/cmd/btrfs-clear-bad-nodes/main.go b/cmd/btrfs-clear-bad-nodes/main.go
index 88c148c..f4b25f1 100644
--- a/cmd/btrfs-clear-bad-nodes/main.go
+++ b/cmd/btrfs-clear-bad-nodes/main.go
@@ -5,14 +5,11 @@
package main
import (
- "errors"
"fmt"
"os"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsmisc"
- "git.lukeshu.com/btrfs-progs-ng/lib/util"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsrepair"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
)
func main() {
@@ -29,7 +26,7 @@ func Main(imgfilenames ...string) (err error) {
}
}
- fs, err := btrfsmisc.Open(os.O_RDWR, imgfilenames...)
+ fs, err := btrfsutil.Open(os.O_RDWR, imgfilenames...)
if err != nil {
return err
}
@@ -37,78 +34,5 @@ func Main(imgfilenames ...string) (err error) {
maybeSetErr(fs.Close())
}()
- var uuidsInited bool
- var metadataUUID, chunkTreeUUID btrfs.UUID
-
- var treeName string
- var treeID btrfs.ObjID
- btrfsmisc.WalkAllTrees(fs, btrfsmisc.WalkAllTreesHandler{
- PreTree: func(name string, id btrfs.ObjID) {
- treeName = name
- treeID = id
- },
- Err: func(err error) {
- fmt.Printf("error: %v\n", err)
- },
- UnsafeNodes: true,
- TreeWalkHandler: btrfs.TreeWalkHandler{
- Node: func(path btrfs.TreePath, node *util.Ref[btrfsvol.LogicalAddr, btrfs.Node], err error) error {
- if err == nil {
- if !uuidsInited {
- metadataUUID = node.Data.Head.MetadataUUID
- chunkTreeUUID = node.Data.Head.ChunkTreeUUID
- uuidsInited = true
- }
- return nil
- }
- if !errors.Is(err, btrfs.ErrNotANode) {
- err = btrfsmisc.WalkErr{
- TreeName: treeName,
- Path: path,
- Err: err,
- }
- fmt.Printf("error: %v\n", err)
- return nil
- }
- origErr := err
- if !uuidsInited {
- // TODO(lukeshu): Is there a better way to get the chunk
- // tree UUID?
- return fmt.Errorf("cannot repair node@%v: not (yet?) sure what the chunk tree UUID is", node.Addr)
- }
- node.Data = btrfs.Node{
- Size: node.Data.Size,
- ChecksumType: node.Data.ChecksumType,
- Head: btrfs.NodeHeader{
- //Checksum: filled below,
- MetadataUUID: metadataUUID,
- Addr: node.Addr,
- Flags: btrfs.NodeWritten,
- BackrefRev: btrfs.MixedBackrefRev,
- ChunkTreeUUID: chunkTreeUUID,
- Generation: 0,
- Owner: treeID,
- NumItems: 0,
- Level: path[len(path)-1].NodeLevel,
- },
- }
- node.Data.Head.Checksum, err = node.Data.CalculateChecksum()
- if err != nil {
- return btrfsmisc.WalkErr{
- TreeName: treeName,
- Path: path,
- Err: err,
- }
- }
- if err := node.Write(); err != nil {
- return err
- }
-
- fmt.Printf("fixed node@%v (err was %v)\n", node.Addr, origErr)
- return nil
- },
- },
- })
-
- return nil
+ return btrfsrepair.ClearBadNodes(fs)
}
diff --git a/cmd/btrfs-dump-tree/main.go b/cmd/btrfs-dump-tree/main.go
index 14bb201..cf29ea4 100644
--- a/cmd/btrfs-dump-tree/main.go
+++ b/cmd/btrfs-dump-tree/main.go
@@ -8,9 +8,8 @@ import (
"fmt"
"os"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsmisc"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
)
func main() {
@@ -29,7 +28,7 @@ func Main(imgfilename string) (err error) {
}
}
- fs, err := btrfsmisc.Open(os.O_RDONLY, imgfilename)
+ fs, err := btrfsutil.Open(os.O_RDONLY, imgfilename)
if err != nil {
return err
}
@@ -37,73 +36,6 @@ func Main(imgfilename string) (err error) {
maybeSetErr(fs.Close())
}()
- superblock, err := fs.Superblock()
- if err != nil {
- return err
- }
-
fmt.Printf("btrfs-progs v%v\n", version)
- if superblock.Data.RootTree != 0 {
- fmt.Printf("root tree\n")
- if err := btrfsmisc.PrintTree(fs, btrfs.ROOT_TREE_OBJECTID); err != nil {
- return err
- }
- }
- if superblock.Data.ChunkTree != 0 {
- fmt.Printf("chunk tree\n")
- if err := btrfsmisc.PrintTree(fs, btrfs.CHUNK_TREE_OBJECTID); err != nil {
- return err
- }
- }
- if superblock.Data.LogTree != 0 {
- fmt.Printf("log root tree\n")
- if err := btrfsmisc.PrintTree(fs, btrfs.TREE_LOG_OBJECTID); err != nil {
- return err
- }
- }
- if superblock.Data.BlockGroupRoot != 0 {
- fmt.Printf("block group tree\n")
- if err := btrfsmisc.PrintTree(fs, btrfs.BLOCK_GROUP_TREE_OBJECTID); err != nil {
- return err
- }
- }
- if err := fs.TreeWalk(btrfs.ROOT_TREE_OBJECTID, btrfs.TreeWalkHandler{
- Item: func(_ btrfs.TreePath, item btrfs.Item) error {
- if item.Head.Key.ItemType != btrfsitem.ROOT_ITEM_KEY {
- return nil
- }
- treeName, ok := map[btrfs.ObjID]string{
- btrfs.ROOT_TREE_OBJECTID: "root",
- btrfs.EXTENT_TREE_OBJECTID: "extent",
- btrfs.CHUNK_TREE_OBJECTID: "chunk",
- btrfs.DEV_TREE_OBJECTID: "device",
- btrfs.FS_TREE_OBJECTID: "fs",
- btrfs.ROOT_TREE_DIR_OBJECTID: "directory",
- btrfs.CSUM_TREE_OBJECTID: "checksum",
- btrfs.ORPHAN_OBJECTID: "orphan",
- btrfs.TREE_LOG_OBJECTID: "log",
- btrfs.TREE_LOG_FIXUP_OBJECTID: "log fixup",
- btrfs.TREE_RELOC_OBJECTID: "reloc",
- btrfs.DATA_RELOC_TREE_OBJECTID: "data reloc",
- btrfs.EXTENT_CSUM_OBJECTID: "extent checksum",
- btrfs.QUOTA_TREE_OBJECTID: "quota",
- btrfs.UUID_TREE_OBJECTID: "uuid",
- btrfs.FREE_SPACE_TREE_OBJECTID: "free space",
- btrfs.MULTIPLE_OBJECTIDS: "multiple",
- btrfs.BLOCK_GROUP_TREE_OBJECTID: "block group",
- }[item.Head.Key.ObjectID]
- if !ok {
- treeName = "file"
- }
- fmt.Printf("%v tree %v \n", treeName, btrfsmisc.FmtKey(item.Head.Key))
- return btrfsmisc.PrintTree(fs, item.Head.Key.ObjectID)
- },
- }); err != nil {
- return err
- }
- fmt.Printf("total bytes %v\n", superblock.Data.TotalBytes)
- fmt.Printf("bytes used %v\n", superblock.Data.BytesUsed)
- fmt.Printf("uuid %v\n", superblock.Data.FSUUID)
-
- return nil
+ return btrfsinspect.DumpTrees(fs)
}
diff --git a/cmd/btrfs-fsck/pass1.go b/cmd/btrfs-fsck/pass1.go
index 9296f0a..0f1ebbf 100644
--- a/cmd/btrfs-fsck/pass1.go
+++ b/cmd/btrfs-fsck/pass1.go
@@ -17,7 +17,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsmisc"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
"git.lukeshu.com/btrfs-progs-ng/lib/util"
)
@@ -26,7 +26,7 @@ func pass1(fs *btrfs.FS, superblock *util.Ref[btrfsvol.PhysicalAddr, btrfs.Super
fmt.Printf("Pass 1: ... walking fs\n")
visitedNodes := make(map[btrfsvol.LogicalAddr]struct{})
- btrfsmisc.WalkAllTrees(fs, btrfsmisc.WalkAllTreesHandler{
+ btrfsutil.WalkAllTrees(fs, btrfsutil.WalkAllTreesHandler{
TreeWalkHandler: btrfs.TreeWalkHandler{
Node: func(path btrfs.TreePath, node *util.Ref[btrfsvol.LogicalAddr, btrfs.Node], err error) error {
if err != nil {
@@ -246,7 +246,7 @@ func pass1ScanOneDev_real(dev *btrfs.Device, superblock btrfs.Superblock) (pass1
devSize, _ := dev.Size()
lastProgress := -1
- err := btrfsmisc.ScanForNodes(dev, superblock, func(nodeRef *util.Ref[btrfsvol.PhysicalAddr, btrfs.Node], err error) {
+ err := btrfsutil.ScanForNodes(dev, superblock, func(nodeRef *util.Ref[btrfsvol.PhysicalAddr, btrfs.Node], err error) {
if err != nil {
fmt.Printf("Pass 1: ... dev[%q] error: %v\n", dev.Name(), err)
return
diff --git a/cmd/btrfs-fsck/pass2.go b/cmd/btrfs-fsck/pass2.go
index 86eb5f0..9256220 100644
--- a/cmd/btrfs-fsck/pass2.go
+++ b/cmd/btrfs-fsck/pass2.go
@@ -9,7 +9,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsmisc"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
"git.lukeshu.com/btrfs-progs-ng/lib/util"
)
@@ -17,7 +17,7 @@ func pass2(fs *btrfs.FS, foundNodes map[btrfsvol.LogicalAddr]struct{}) {
fmt.Printf("\nPass 2: orphaned nodes\n")
visitedNodes := make(map[btrfsvol.LogicalAddr]struct{})
- btrfsmisc.WalkAllTrees(fs, btrfsmisc.WalkAllTreesHandler{
+ btrfsutil.WalkAllTrees(fs, btrfsutil.WalkAllTreesHandler{
TreeWalkHandler: btrfs.TreeWalkHandler{
Node: func(path btrfs.TreePath, node *util.Ref[btrfsvol.LogicalAddr, btrfs.Node], err error) error {
visitedNodes[node.Addr] = struct{}{}
diff --git a/cmd/btrfs-ls-files/main.go b/cmd/btrfs-ls-files/main.go
index 3fb540c..36d14ed 100644
--- a/cmd/btrfs-ls-files/main.go
+++ b/cmd/btrfs-ls-files/main.go
@@ -14,7 +14,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsmisc"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
"git.lukeshu.com/btrfs-progs-ng/lib/util"
)
@@ -32,7 +32,7 @@ func Main(imgfilenames ...string) (err error) {
}
}
- fs, err := btrfsmisc.Open(os.O_RDONLY, imgfilenames...)
+ fs, err := btrfsutil.Open(os.O_RDONLY, imgfilenames...)
if err != nil {
return err
}
diff --git a/cmd/btrfs-ls-trees/main.go b/cmd/btrfs-ls-trees/main.go
index 7063323..a0ae460 100644
--- a/cmd/btrfs-ls-trees/main.go
+++ b/cmd/btrfs-ls-trees/main.go
@@ -12,7 +12,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsmisc"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
"git.lukeshu.com/btrfs-progs-ng/lib/util"
)
@@ -30,7 +30,7 @@ func Main(imgfilenames ...string) (err error) {
}
}
- fs, err := btrfsmisc.Open(os.O_RDONLY, imgfilenames...)
+ fs, err := btrfsutil.Open(os.O_RDONLY, imgfilenames...)
if err != nil {
return err
}
@@ -40,7 +40,7 @@ func Main(imgfilenames ...string) (err error) {
var treeErrCnt int
var treeItemCnt map[btrfsitem.Type]int
- btrfsmisc.WalkAllTrees(fs, btrfsmisc.WalkAllTreesHandler{
+ btrfsutil.WalkAllTrees(fs, btrfsutil.WalkAllTreesHandler{
PreTree: func(name string, treeID btrfs.ObjID) {
treeErrCnt = 0
treeItemCnt = make(map[btrfsitem.Type]int)
diff --git a/cmd/btrfs-mount/fuseutil.go b/cmd/btrfs-mount/fuseutil.go
deleted file mode 100644
index 940e571..0000000
--- a/cmd/btrfs-mount/fuseutil.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package main
-
-import (
- "context"
- "sync/atomic"
-
- "github.com/datawire/dlib/dcontext"
- "github.com/datawire/dlib/dgroup"
- "github.com/datawire/dlib/dlog"
- "github.com/jacobsa/fuse"
-)
-
-func Mount(ctx context.Context, mountpoint string, server fuse.Server, cfg *fuse.MountConfig) error {
- grp := dgroup.NewGroup(ctx, dgroup.GroupConfig{
- // Allow mountHandle.Join() returning to cause the
- // "unmount" goroutine to quit.
- ShutdownOnNonError: true,
- })
- mounted := uint32(1)
- grp.Go("unmount", func(ctx context.Context) error {
- <-ctx.Done()
- var err error
- var gotNil bool
- // Keep retrying, because the FS might be busy.
- for atomic.LoadUint32(&mounted) != 0 {
- if _err := fuse.Unmount(mountpoint); _err == nil {
- gotNil = true
- } else if !gotNil {
- err = _err
- }
- }
- if gotNil {
- return nil
- }
- return err
- })
- grp.Go("mount", func(ctx context.Context) error {
- defer atomic.StoreUint32(&mounted, 0)
-
- cfg.OpContext = ctx
- cfg.ErrorLogger = dlog.StdLogger(ctx, dlog.LogLevelError)
- cfg.DebugLogger = dlog.StdLogger(ctx, dlog.LogLevelDebug)
-
- mountHandle, err := fuse.Mount(mountpoint, server, cfg)
- if err != nil {
- return err
- }
- dlog.Infof(ctx, "mounted %q", mountpoint)
- return mountHandle.Join(dcontext.HardContext(ctx))
- })
- return grp.Wait()
-}
diff --git a/cmd/btrfs-mount/main.go b/cmd/btrfs-mount/main.go
index 0183c02..7debb3a 100644
--- a/cmd/btrfs-mount/main.go
+++ b/cmd/btrfs-mount/main.go
@@ -8,14 +8,13 @@ import (
"context"
"fmt"
"os"
- "path/filepath"
"github.com/datawire/dlib/dgroup"
"github.com/datawire/dlib/dlog"
"github.com/sirupsen/logrus"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsmisc"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
)
func main() {
@@ -36,14 +35,6 @@ 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 {
@@ -51,7 +42,7 @@ func Main(ctx context.Context, mountpoint string, imgfilenames ...string) (err e
}
}
- fs, err := btrfsmisc.Open(os.O_RDONLY, imgfilenames...)
+ fs, err := btrfsutil.Open(os.O_RDONLY, imgfilenames...)
if err != nil {
return err
}
@@ -59,13 +50,5 @@ func Main(ctx context.Context, mountpoint string, imgfilenames ...string) (err e
maybeSetErr(fs.Close())
}()
- rootSubvol := &Subvolume{
- Subvolume: btrfs.Subvolume{
- FS: fs,
- TreeID: btrfs.FS_TREE_OBJECTID,
- },
- DeviceName: tryAbs(imgfilenames[0]),
- Mountpoint: mountpoint,
- }
- return rootSubvol.Run(ctx)
+ return btrfsinspect.MountRO(ctx, fs, mountpoint)
}
diff --git a/cmd/btrfs-mount/subvol_fuse.go b/lib/btrfsprogs/btrfsinspect/mount.go
index 8a910bf..641bc64 100644
--- a/cmd/btrfs-mount/subvol_fuse.go
+++ b/lib/btrfsprogs/btrfsinspect/mount.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
-package main
+package btrfsinspect
import (
"context"
@@ -14,7 +14,9 @@ import (
"sync/atomic"
"syscall"
+ "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"
@@ -25,6 +27,69 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/util"
)
+func MountRO(ctx context.Context, fs *btrfs.FS, mountpoint string) error {
+ pvs := fs.LV.PhysicalVolumes()
+ if len(pvs) < 1 {
+ return errors.New("no devices")
+ }
+
+ deviceName := pvs[util.SortedMapKeys(pvs)[0]].Name()
+ if abs, err := filepath.Abs(deviceName); err == nil {
+ deviceName = abs
+ }
+
+ rootSubvol := &subvolume{
+ Subvolume: btrfs.Subvolume{
+ FS: fs,
+ TreeID: btrfs.FS_TREE_OBJECTID,
+ },
+ DeviceName: deviceName,
+ Mountpoint: mountpoint,
+ }
+ return rootSubvol.Run(ctx)
+}
+
+func fuseMount(ctx context.Context, mountpoint string, server fuse.Server, cfg *fuse.MountConfig) error {
+ grp := dgroup.NewGroup(ctx, dgroup.GroupConfig{
+ // Allow mountHandle.Join() returning to cause the
+ // "unmount" goroutine to quit.
+ ShutdownOnNonError: true,
+ })
+ mounted := uint32(1)
+ grp.Go("unmount", func(ctx context.Context) error {
+ <-ctx.Done()
+ var err error
+ var gotNil bool
+ // Keep retrying, because the FS might be busy.
+ for atomic.LoadUint32(&mounted) != 0 {
+ if _err := fuse.Unmount(mountpoint); _err == nil {
+ gotNil = true
+ } else if !gotNil {
+ err = _err
+ }
+ }
+ if gotNil {
+ return nil
+ }
+ return err
+ })
+ grp.Go("mount", func(ctx context.Context) error {
+ defer atomic.StoreUint32(&mounted, 0)
+
+ cfg.OpContext = ctx
+ cfg.ErrorLogger = dlog.StdLogger(ctx, dlog.LogLevelError)
+ cfg.DebugLogger = dlog.StdLogger(ctx, dlog.LogLevelDebug)
+
+ mountHandle, err := fuse.Mount(mountpoint, server, cfg)
+ if err != nil {
+ return err
+ }
+ dlog.Infof(ctx, "mounted %q", mountpoint)
+ return mountHandle.Join(dcontext.HardContext(ctx))
+ })
+ return grp.Wait()
+}
+
type dirState struct {
Dir *btrfs.Dir
}
@@ -33,7 +98,7 @@ type fileState struct {
File *btrfs.File
}
-type Subvolume struct {
+type subvolume struct {
btrfs.Subvolume
DeviceName string
Mountpoint string
@@ -48,7 +113,7 @@ type Subvolume struct {
grp *dgroup.Group
}
-func (sv *Subvolume) Run(ctx context.Context) error {
+func (sv *subvolume) Run(ctx context.Context) error {
sv.grp = dgroup.NewGroup(ctx, dgroup.GroupConfig{})
sv.grp.Go("self", func(ctx context.Context) error {
cfg := &fuse.MountConfig{
@@ -61,12 +126,12 @@ func (sv *Subvolume) Run(ctx context.Context) error {
"allow_other": "",
},
}
- return Mount(ctx, sv.Mountpoint, fuseutil.NewFileSystemServer(sv), cfg)
+ return fuseMount(ctx, sv.Mountpoint, fuseutil.NewFileSystemServer(sv), cfg)
})
return sv.grp.Wait()
}
-func (sv *Subvolume) newHandle() fuseops.HandleID {
+func (sv *subvolume) newHandle() fuseops.HandleID {
return fuseops.HandleID(atomic.AddUint64(&sv.lastHandle, 1))
}
@@ -85,7 +150,7 @@ func inodeItemToFUSE(itemBody btrfsitem.Inode) fuseops.InodeAttributes {
}
}
-func (sv *Subvolume) LoadDir(inode btrfs.ObjID) (val *btrfs.Dir, err error) {
+func (sv *subvolume) LoadDir(inode btrfs.ObjID) (val *btrfs.Dir, err error) {
val, err = sv.Subvolume.LoadDir(inode)
if val != nil {
haveSubvolumes := false
@@ -115,7 +180,7 @@ func (sv *Subvolume) LoadDir(inode btrfs.ObjID) (val *btrfs.Dir, err error) {
sv.subvols[subMountpoint] = struct{}{}
workerName := fmt.Sprintf("%d-%s", val.Inode, filepath.Base(subMountpoint))
sv.grp.Go(workerName, func(ctx context.Context) error {
- subSv := &Subvolume{
+ subSv := &subvolume{
Subvolume: btrfs.Subvolume{
FS: sv.FS,
TreeID: entry.Location.ObjectID,
@@ -133,7 +198,7 @@ func (sv *Subvolume) LoadDir(inode btrfs.ObjID) (val *btrfs.Dir, err error) {
return
}
-func (sv *Subvolume) StatFS(_ context.Context, op *fuseops.StatFSOp) error {
+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 {
@@ -156,7 +221,7 @@ func (sv *Subvolume) StatFS(_ context.Context, op *fuseops.StatFSOp) error {
return nil
}
-func (sv *Subvolume) LookUpInode(_ context.Context, op *fuseops.LookUpInodeOp) error {
+func (sv *subvolume) LookUpInode(_ context.Context, op *fuseops.LookUpInodeOp) error {
if op.Parent == fuseops.RootInodeID {
parent, err := sv.GetRootInode()
if err != nil {
@@ -207,7 +272,7 @@ func (sv *Subvolume) LookUpInode(_ context.Context, op *fuseops.LookUpInodeOp) e
return nil
}
-func (sv *Subvolume) GetInodeAttributes(_ context.Context, op *fuseops.GetInodeAttributesOp) error {
+func (sv *subvolume) GetInodeAttributes(_ context.Context, op *fuseops.GetInodeAttributesOp) error {
if op.Inode == fuseops.RootInodeID {
inode, err := sv.GetRootInode()
if err != nil {
@@ -225,7 +290,7 @@ func (sv *Subvolume) GetInodeAttributes(_ context.Context, op *fuseops.GetInodeA
return nil
}
-func (sv *Subvolume) OpenDir(_ context.Context, op *fuseops.OpenDirOp) error {
+func (sv *subvolume) OpenDir(_ context.Context, op *fuseops.OpenDirOp) error {
if op.Inode == fuseops.RootInodeID {
inode, err := sv.GetRootInode()
if err != nil {
@@ -245,7 +310,7 @@ func (sv *Subvolume) OpenDir(_ context.Context, op *fuseops.OpenDirOp) error {
op.Handle = handle
return nil
}
-func (sv *Subvolume) ReadDir(_ context.Context, op *fuseops.ReadDirOp) error {
+func (sv *subvolume) ReadDir(_ context.Context, op *fuseops.ReadDirOp) error {
state, ok := sv.dirHandles.Load(op.Handle)
if !ok {
return syscall.EBADF
@@ -278,7 +343,7 @@ func (sv *Subvolume) ReadDir(_ context.Context, op *fuseops.ReadDirOp) error {
}
return nil
}
-func (sv *Subvolume) ReleaseDirHandle(_ context.Context, op *fuseops.ReleaseDirHandleOp) error {
+func (sv *subvolume) ReleaseDirHandle(_ context.Context, op *fuseops.ReleaseDirHandleOp) error {
_, ok := sv.dirHandles.LoadAndDelete(op.Handle)
if !ok {
return syscall.EBADF
@@ -286,7 +351,7 @@ func (sv *Subvolume) ReleaseDirHandle(_ context.Context, op *fuseops.ReleaseDirH
return nil
}
-func (sv *Subvolume) OpenFile(_ context.Context, op *fuseops.OpenFileOp) error {
+func (sv *subvolume) OpenFile(_ context.Context, op *fuseops.OpenFileOp) error {
file, err := sv.LoadFile(btrfs.ObjID(op.Inode))
if err != nil {
return err
@@ -299,7 +364,7 @@ func (sv *Subvolume) OpenFile(_ context.Context, op *fuseops.OpenFileOp) error {
op.KeepPageCache = true
return nil
}
-func (sv *Subvolume) ReadFile(_ context.Context, op *fuseops.ReadFileOp) error {
+func (sv *subvolume) ReadFile(_ context.Context, op *fuseops.ReadFileOp) error {
state, ok := sv.fileHandles.Load(op.Handle)
if !ok {
return syscall.EBADF
@@ -322,7 +387,7 @@ func (sv *Subvolume) ReadFile(_ context.Context, op *fuseops.ReadFileOp) error {
return err
}
-func (sv *Subvolume) ReleaseFileHandle(_ context.Context, op *fuseops.ReleaseFileHandleOp) error {
+func (sv *subvolume) ReleaseFileHandle(_ context.Context, op *fuseops.ReleaseFileHandleOp) error {
_, ok := sv.fileHandles.LoadAndDelete(op.Handle)
if !ok {
return syscall.EBADF
@@ -330,13 +395,13 @@ func (sv *Subvolume) ReleaseFileHandle(_ context.Context, op *fuseops.ReleaseFil
return nil
}
-func (sv *Subvolume) ReadSymlink(_ context.Context, op *fuseops.ReadSymlinkOp) error {
+func (sv *subvolume) ReadSymlink(_ context.Context, op *fuseops.ReadSymlinkOp) error {
return syscall.ENOSYS
}
-func (sv *Subvolume) GetXattr(_ context.Context, op *fuseops.GetXattrOp) error { return syscall.ENOSYS }
-func (sv *Subvolume) ListXattr(_ context.Context, op *fuseops.ListXattrOp) error {
+func (sv *subvolume) GetXattr(_ context.Context, op *fuseops.GetXattrOp) error { return syscall.ENOSYS }
+func (sv *subvolume) ListXattr(_ context.Context, op *fuseops.ListXattrOp) error {
return syscall.ENOSYS
}
-func (sv *Subvolume) Destroy() {}
+func (sv *subvolume) Destroy() {}
diff --git a/lib/btrfsmisc/print_tree.go b/lib/btrfsprogs/btrfsinspect/print_tree.go
index 0a00c70..72ff13e 100644
--- a/lib/btrfsmisc/print_tree.go
+++ b/lib/btrfsprogs/btrfsinspect/print_tree.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
-package btrfsmisc
+package btrfsinspect
import (
"fmt"
@@ -16,10 +16,80 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/util"
)
-// PrintTree mimics btrfs-progs
+func DumpTrees(fs *btrfs.FS) error {
+ superblock, err := fs.Superblock()
+ if err != nil {
+ return err
+ }
+
+ if superblock.Data.RootTree != 0 {
+ fmt.Printf("root tree\n")
+ if err := printTree(fs, btrfs.ROOT_TREE_OBJECTID); err != nil {
+ return err
+ }
+ }
+ if superblock.Data.ChunkTree != 0 {
+ fmt.Printf("chunk tree\n")
+ if err := printTree(fs, btrfs.CHUNK_TREE_OBJECTID); err != nil {
+ return err
+ }
+ }
+ if superblock.Data.LogTree != 0 {
+ fmt.Printf("log root tree\n")
+ if err := printTree(fs, btrfs.TREE_LOG_OBJECTID); err != nil {
+ return err
+ }
+ }
+ if superblock.Data.BlockGroupRoot != 0 {
+ fmt.Printf("block group tree\n")
+ if err := printTree(fs, btrfs.BLOCK_GROUP_TREE_OBJECTID); err != nil {
+ return err
+ }
+ }
+ if err := fs.TreeWalk(btrfs.ROOT_TREE_OBJECTID, btrfs.TreeWalkHandler{
+ Item: func(_ btrfs.TreePath, item btrfs.Item) error {
+ if item.Head.Key.ItemType != btrfsitem.ROOT_ITEM_KEY {
+ return nil
+ }
+ treeName, ok := map[btrfs.ObjID]string{
+ btrfs.ROOT_TREE_OBJECTID: "root",
+ btrfs.EXTENT_TREE_OBJECTID: "extent",
+ btrfs.CHUNK_TREE_OBJECTID: "chunk",
+ btrfs.DEV_TREE_OBJECTID: "device",
+ btrfs.FS_TREE_OBJECTID: "fs",
+ btrfs.ROOT_TREE_DIR_OBJECTID: "directory",
+ btrfs.CSUM_TREE_OBJECTID: "checksum",
+ btrfs.ORPHAN_OBJECTID: "orphan",
+ btrfs.TREE_LOG_OBJECTID: "log",
+ btrfs.TREE_LOG_FIXUP_OBJECTID: "log fixup",
+ btrfs.TREE_RELOC_OBJECTID: "reloc",
+ btrfs.DATA_RELOC_TREE_OBJECTID: "data reloc",
+ btrfs.EXTENT_CSUM_OBJECTID: "extent checksum",
+ btrfs.QUOTA_TREE_OBJECTID: "quota",
+ btrfs.UUID_TREE_OBJECTID: "uuid",
+ btrfs.FREE_SPACE_TREE_OBJECTID: "free space",
+ btrfs.MULTIPLE_OBJECTIDS: "multiple",
+ btrfs.BLOCK_GROUP_TREE_OBJECTID: "block group",
+ }[item.Head.Key.ObjectID]
+ if !ok {
+ treeName = "file"
+ }
+ fmt.Printf("%v tree %v \n", treeName, fmtKey(item.Head.Key))
+ return printTree(fs, item.Head.Key.ObjectID)
+ },
+ }); err != nil {
+ return err
+ }
+ fmt.Printf("total bytes %v\n", superblock.Data.TotalBytes)
+ fmt.Printf("bytes used %v\n", superblock.Data.BytesUsed)
+ fmt.Printf("uuid %v\n", superblock.Data.FSUUID)
+ return nil
+}
+
+// printTree mimics btrfs-progs
// kernel-shared/print-tree.c:btrfs_print_tree() and
// kernel-shared/print-tree.c:btrfs_print_leaf()
-func PrintTree(fs *btrfs.FS, treeID btrfs.ObjID) error {
+func printTree(fs *btrfs.FS, treeID btrfs.ObjID) error {
return fs.TreeWalk(treeID, btrfs.TreeWalkHandler{
Node: func(path btrfs.TreePath, nodeRef *util.Ref[btrfsvol.LogicalAddr, btrfs.Node], err error) error {
if err != nil {
@@ -32,7 +102,7 @@ func PrintTree(fs *btrfs.FS, treeID btrfs.ObjID) error {
},
PreKeyPointer: func(_ btrfs.TreePath, item btrfs.KeyPointer) error {
fmt.Printf("\t%v block %v gen %v\n",
- FmtKey(item.Key),
+ fmtKey(item.Key),
item.BlockPtr,
item.Generation)
return nil
@@ -41,12 +111,12 @@ func PrintTree(fs *btrfs.FS, treeID btrfs.ObjID) error {
i := path[len(path)-1].ItemIdx
fmt.Printf("\titem %v %v itemoff %v itemsize %v\n",
i,
- FmtKey(item.Head.Key),
+ fmtKey(item.Head.Key),
item.Head.DataOffset,
item.Head.DataSize)
switch body := item.Body.(type) {
case btrfsitem.FreeSpaceHeader:
- fmt.Printf("\t\tlocation %v\n", FmtKey(body.Location))
+ fmt.Printf("\t\tlocation %v\n", fmtKey(body.Location))
fmt.Printf("\t\tcache generation %v entries %v bitmaps %v\n",
body.Generation, body.NumEntries, body.NumBitmaps)
case btrfsitem.Inode:
@@ -69,7 +139,7 @@ func PrintTree(fs *btrfs.FS, treeID btrfs.ObjID) error {
case btrfsitem.DirEntries:
for _, dir := range body {
fmt.Printf("\t\tlocation %v type %v\n",
- FmtKey(dir.Location), dir.Type)
+ fmtKey(dir.Location), dir.Type)
fmt.Printf("\t\ttransid %v data_len %v name_len %v\n",
dir.TransID, dir.DataLen, dir.NameLen)
fmt.Printf("\t\tname: %s\n", dir.Name)
@@ -85,7 +155,7 @@ func PrintTree(fs *btrfs.FS, treeID btrfs.ObjID) error {
fmt.Printf("\t\tlast_snapshot %v flags %v refs %v\n",
body.LastSnapshot, body.Flags, body.Refs)
fmt.Printf("\t\tdrop_progress %v drop_level %v\n",
- FmtKey(body.DropProgress), body.DropLevel)
+ fmtKey(body.DropProgress), body.DropLevel)
fmt.Printf("\t\tlevel %v generation_v2 %v\n",
body.Level, body.GenerationV2)
if body.Generation == body.GenerationV2 {
@@ -116,7 +186,7 @@ func PrintTree(fs *btrfs.FS, treeID btrfs.ObjID) error {
body.Head.Refs, body.Head.Generation, body.Head.Flags)
if body.Head.Flags.Has(btrfsitem.EXTENT_FLAG_TREE_BLOCK) {
fmt.Printf("\t\ttree block %v level %v\n",
- FmtKey(body.Info.Key), body.Info.Level)
+ fmtKey(body.Info.Key), body.Info.Level)
}
printExtentInlineRefs(body.Refs)
case btrfsitem.Metadata:
@@ -340,7 +410,7 @@ func printExtentInlineRefs(refs []btrfsitem.ExtentInlineRef) {
}
// mimics print-tree.c:btrfs_print_key()
-func FmtKey(key btrfs.Key) string {
+func fmtKey(key btrfs.Key) string {
var out strings.Builder
fmt.Fprintf(&out, "key (%v %v", key.ObjectID.Format(key.ItemType), key.ItemType)
switch key.ItemType {
diff --git a/lib/btrfsprogs/btrfsrepair/clearnodes.go b/lib/btrfsprogs/btrfsrepair/clearnodes.go
new file mode 100644
index 0000000..595fef0
--- /dev/null
+++ b/lib/btrfsprogs/btrfsrepair/clearnodes.go
@@ -0,0 +1,91 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package btrfsrepair
+
+import (
+ "errors"
+ "fmt"
+
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
+ "git.lukeshu.com/btrfs-progs-ng/lib/util"
+)
+
+func ClearBadNodes(fs *btrfs.FS) error {
+ var uuidsInited bool
+ var metadataUUID, chunkTreeUUID btrfs.UUID
+
+ var treeName string
+ var treeID btrfs.ObjID
+ btrfsutil.WalkAllTrees(fs, btrfsutil.WalkAllTreesHandler{
+ PreTree: func(name string, id btrfs.ObjID) {
+ treeName = name
+ treeID = id
+ },
+ Err: func(err error) {
+ fmt.Printf("error: %v\n", err)
+ },
+ UnsafeNodes: true,
+ TreeWalkHandler: btrfs.TreeWalkHandler{
+ Node: func(path btrfs.TreePath, node *util.Ref[btrfsvol.LogicalAddr, btrfs.Node], err error) error {
+ if err == nil {
+ if !uuidsInited {
+ metadataUUID = node.Data.Head.MetadataUUID
+ chunkTreeUUID = node.Data.Head.ChunkTreeUUID
+ uuidsInited = true
+ }
+ return nil
+ }
+ if !errors.Is(err, btrfs.ErrNotANode) {
+ err = btrfsutil.WalkErr{
+ TreeName: treeName,
+ Path: path,
+ Err: err,
+ }
+ fmt.Printf("error: %v\n", err)
+ return nil
+ }
+ origErr := err
+ if !uuidsInited {
+ // TODO(lukeshu): Is there a better way to get the chunk
+ // tree UUID?
+ return fmt.Errorf("cannot repair node@%v: not (yet?) sure what the chunk tree UUID is", node.Addr)
+ }
+ node.Data = btrfs.Node{
+ Size: node.Data.Size,
+ ChecksumType: node.Data.ChecksumType,
+ Head: btrfs.NodeHeader{
+ //Checksum: filled below,
+ MetadataUUID: metadataUUID,
+ Addr: node.Addr,
+ Flags: btrfs.NodeWritten,
+ BackrefRev: btrfs.MixedBackrefRev,
+ ChunkTreeUUID: chunkTreeUUID,
+ Generation: 0,
+ Owner: treeID,
+ NumItems: 0,
+ Level: path[len(path)-1].NodeLevel,
+ },
+ }
+ node.Data.Head.Checksum, err = node.Data.CalculateChecksum()
+ if err != nil {
+ return btrfsutil.WalkErr{
+ TreeName: treeName,
+ Path: path,
+ Err: err,
+ }
+ }
+ if err := node.Write(); err != nil {
+ return err
+ }
+
+ fmt.Printf("fixed node@%v (err was %v)\n", node.Addr, origErr)
+ return nil
+ },
+ },
+ })
+ return nil
+}
diff --git a/lib/btrfsmisc/open.go b/lib/btrfsprogs/btrfsutil/open.go
index 8646d5a..cc081a6 100644
--- a/lib/btrfsmisc/open.go
+++ b/lib/btrfsprogs/btrfsutil/open.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
-package btrfsmisc
+package btrfsutil
import (
"fmt"
diff --git a/lib/btrfsmisc/fsck.go b/lib/btrfsprogs/btrfsutil/scan.go
index b0c2ad7..d83525c 100644
--- a/lib/btrfsmisc/fsck.go
+++ b/lib/btrfsprogs/btrfsutil/scan.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
-package btrfsmisc
+package btrfsutil
import (
"errors"
diff --git a/lib/btrfsmisc/walk.go b/lib/btrfsprogs/btrfsutil/walk.go
index 43275ba..0c54384 100644
--- a/lib/btrfsmisc/walk.go
+++ b/lib/btrfsprogs/btrfsutil/walk.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
-package btrfsmisc
+package btrfsutil
import (
"fmt"