diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-07-10 17:24:51 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-07-11 00:44:30 -0600 |
commit | bde202f286461ab575dc7e3d83f996d9a5f4a6ec (patch) | |
tree | 64782c354c15f64a164996125a06c1bca30c9aa7 | |
parent | d2da99882ea49cc67780c0255bf624698898e7fe (diff) |
Have a go at rearranging things in to a lib/btrfsprogs
-rw-r--r-- | cmd/btrfs-clear-bad-nodes/main.go | 84 | ||||
-rw-r--r-- | cmd/btrfs-dump-tree/main.go | 76 | ||||
-rw-r--r-- | cmd/btrfs-fsck/pass1.go | 6 | ||||
-rw-r--r-- | cmd/btrfs-fsck/pass2.go | 4 | ||||
-rw-r--r-- | cmd/btrfs-ls-files/main.go | 4 | ||||
-rw-r--r-- | cmd/btrfs-ls-trees/main.go | 6 | ||||
-rw-r--r-- | cmd/btrfs-mount/fuseutil.go | 56 | ||||
-rw-r--r-- | cmd/btrfs-mount/main.go | 25 | ||||
-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.go | 91 | ||||
-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" |