From 839dfa5d0aeadee9cb0f8581341922138f9595f0 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 11 Jul 2022 21:10:54 -0600 Subject: Move all of the small-ish tools to be part of btrfs-rec --- cmd/btrfs-clear-bad-nodes/main.go | 38 ------- cmd/btrfs-dump-tree/main.go | 41 ------- cmd/btrfs-ls-files/main.go | 200 ---------------------------------- cmd/btrfs-ls-trees/main.go | 77 ------------- cmd/btrfs-mount/main.go | 54 --------- cmd/btrfs-rec/inspect_dumptrees.go | 31 ++++++ cmd/btrfs-rec/inspect_lsfiles.go | 187 +++++++++++++++++++++++++++++++ cmd/btrfs-rec/inspect_lstrees.go | 68 ++++++++++++ cmd/btrfs-rec/inspect_mount.go | 26 +++++ cmd/btrfs-rec/main.go | 2 +- cmd/btrfs-rec/repair_clearbadnodes.go | 28 +++++ 11 files changed, 341 insertions(+), 411 deletions(-) delete mode 100644 cmd/btrfs-clear-bad-nodes/main.go delete mode 100644 cmd/btrfs-dump-tree/main.go delete mode 100644 cmd/btrfs-ls-files/main.go delete mode 100644 cmd/btrfs-ls-trees/main.go delete mode 100644 cmd/btrfs-mount/main.go create mode 100644 cmd/btrfs-rec/inspect_dumptrees.go create mode 100644 cmd/btrfs-rec/inspect_lsfiles.go create mode 100644 cmd/btrfs-rec/inspect_lstrees.go create mode 100644 cmd/btrfs-rec/inspect_mount.go create mode 100644 cmd/btrfs-rec/repair_clearbadnodes.go (limited to 'cmd') diff --git a/cmd/btrfs-clear-bad-nodes/main.go b/cmd/btrfs-clear-bad-nodes/main.go deleted file mode 100644 index f44b1f7..0000000 --- a/cmd/btrfs-clear-bad-nodes/main.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package main - -import ( - "fmt" - "os" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsrepair" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" -) - -func main() { - if err := Main(os.Args[1:]...); err != nil { - fmt.Fprintf(os.Stderr, "%v: error: %v\n", os.Args[0], err) - os.Exit(1) - } -} - -func Main(imgfilenames ...string) (err error) { - maybeSetErr := func(_err error) { - if _err != nil && err == nil { - err = _err - } - } - - fs, err := btrfsutil.Open(os.O_RDWR, imgfilenames...) - if err != nil { - return err - } - defer func() { - maybeSetErr(fs.Close()) - }() - - return btrfsrepair.ClearBadNodes(os.Stdout, os.Stderr, fs) -} diff --git a/cmd/btrfs-dump-tree/main.go b/cmd/btrfs-dump-tree/main.go deleted file mode 100644 index 2770849..0000000 --- a/cmd/btrfs-dump-tree/main.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package main - -import ( - "fmt" - "os" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" -) - -func main() { - if err := Main(os.Args[1]); err != nil { - fmt.Fprintf(os.Stderr, "%v: error: %v\n", os.Args[0], err) - os.Exit(1) - } -} - -const version = "5.18.1" - -func Main(imgfilename string) (err error) { - maybeSetErr := func(_err error) { - if _err != nil && err == nil { - err = _err - } - } - - fs, err := btrfsutil.Open(os.O_RDONLY, imgfilename) - if err != nil { - return err - } - defer func() { - maybeSetErr(fs.Close()) - }() - - fmt.Printf("btrfs-progs v%v\n", version) - return btrfsinspect.DumpTrees(os.Stdout, os.Stderr, fs) -} diff --git a/cmd/btrfs-ls-files/main.go b/cmd/btrfs-ls-files/main.go deleted file mode 100644 index 0f27def..0000000 --- a/cmd/btrfs-ls-files/main.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package main - -import ( - "fmt" - "os" - "reflect" - "strings" - - "github.com/datawire/dlib/derror" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" - "git.lukeshu.com/btrfs-progs-ng/lib/util" -) - -func main() { - if err := Main(os.Args[1:]...); err != nil { - fmt.Fprintf(os.Stderr, "%v: error: %v\n", os.Args[0], err) - os.Exit(1) - } -} - -func Main(imgfilenames ...string) (err error) { - maybeSetErr := func(_err error) { - if _err != nil && err == nil { - err = _err - } - } - - fs, err := btrfsutil.Open(os.O_RDONLY, imgfilenames...) - if err != nil { - return err - } - defer func() { - maybeSetErr(fs.Close()) - }() - - printSubvol(fs, "", "", "/", btrfs.Key{ - ObjectID: btrfs.FS_TREE_OBJECTID, - ItemType: btrfsitem.ROOT_ITEM_KEY, - Offset: 0, - }) - - return nil -} - -const ( - tS = "    " - tl = "│   " - tT = "├── " - tL = "└── " -) - -func printSubvol(fs *btrfs.FS, prefix0, prefix1, name string, key btrfs.Key) { - root, err := fs.TreeLookup(btrfs.ROOT_TREE_OBJECTID, key) - if err != nil { - fmt.Printf("%s%q error: could not look up root %v: %v\n", prefix0, name, key, err) - return - } - rootBody := root.Body.(btrfsitem.Root) - - printDir(fs, root.Head.Key.ObjectID, prefix0, prefix1, name, rootBody.RootDirID) -} - -func printDir(fs *btrfs.FS, fsTree btrfs.ObjID, prefix0, prefix1, dirName string, dirInode btrfs.ObjID) { - var errs derror.MultiError - items, err := fs.TreeSearchAll(fsTree, func(key btrfs.Key) int { - return util.CmpUint(dirInode, key.ObjectID) - }) - if err != nil { - errs = append(errs, fmt.Errorf("read dir: %w", err)) - } - var dirInodeDat btrfsitem.Inode - var dirInodeDatOK bool - membersByIndex := make(map[uint64]btrfsitem.DirEntry) - membersByNameHash := make(map[uint64]btrfsitem.DirEntry) - for _, item := range items { - switch item.Head.Key.ItemType { - case btrfsitem.INODE_ITEM_KEY: - if dirInodeDatOK { - if !reflect.DeepEqual(dirInodeDat, item.Body.(btrfsitem.Inode)) { - errs = append(errs, fmt.Errorf("read dir: multiple inodes")) - } - continue - } - dirInodeDat = item.Body.(btrfsitem.Inode) - dirInodeDatOK = true - case btrfsitem.INODE_REF_KEY: - // TODO - case btrfsitem.DIR_ITEM_KEY: - entry := item.Body.(btrfsitem.DirEntry) - namehash := btrfsitem.NameHash(entry.Name) - if namehash != item.Head.Key.Offset { - errs = append(errs, fmt.Errorf("read dir: direntry crc32c mismatch: key=%#x crc32c(%q)=%#x", - item.Head.Key.Offset, entry.Name, namehash)) - continue - } - if other, exists := membersByNameHash[namehash]; exists { - if !reflect.DeepEqual(entry, other) { - if string(entry.Name) == string(other.Name) { - errs = append(errs, fmt.Errorf("read dir: multiple instances of direntry crc32c(%q)=%#x", - entry.Name, namehash)) - } else { - errs = append(errs, fmt.Errorf("read dir: multiple instances of direntry crc32c(%q|%q)=%#x", - other.Name, entry.Name, namehash)) - } - } - continue - } - membersByNameHash[btrfsitem.NameHash(entry.Name)] = entry - case btrfsitem.DIR_INDEX_KEY: - index := item.Head.Key.Offset - entry := item.Body.(btrfsitem.DirEntry) - if other, exists := membersByIndex[index]; exists { - if !reflect.DeepEqual(entry, other) { - errs = append(errs, fmt.Errorf("read dir: multiple instances of direntry index %v", index)) - } - continue - } - membersByIndex[index] = entry - //case btrfsitem.XATTR_ITEM_KEY: - default: - errs = append(errs, fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType)) - } - } - fmt.Printf("%s%q\t[ino=%d", - prefix0, dirName, dirInode) - if dirInodeDatOK { - fmt.Printf("\tuid=%d\tgid=%d\tsize=%d]\n", - dirInodeDat.UID, dirInodeDat.GID, dirInodeDat.Size) - } else { - err := fmt.Errorf("read dir: no inode data") - if len(items) == 0 && len(errs) == 1 { - err = errs[0] - errs = nil - } - fmt.Printf("]\terror: %v\n", err) - } - for i, index := range util.SortedMapKeys(membersByIndex) { - entry := membersByIndex[index] - namehash := btrfsitem.NameHash(entry.Name) - if other, ok := membersByNameHash[namehash]; ok { - if !reflect.DeepEqual(entry, other) { - errs = append(errs, fmt.Errorf("read dir: index=%d disagrees with crc32c(%q)=%#x", - index, entry.Name, namehash)) - } - delete(membersByNameHash, namehash) - } else { - errs = append(errs, fmt.Errorf("read dir: no DIR_ITEM crc32c(%q)=%#x for DIR_INDEX index=%d", - entry.Name, namehash, index)) - } - p0, p1 := tT, tl - if (i == len(membersByIndex)-1) && (len(membersByNameHash) == 0) && (len(errs) == 0) { - p0, p1 = tL, tS - } - printDirEntry(fs, fsTree, prefix1+p0, prefix1+p1, entry) - } - for _, namehash := range util.SortedMapKeys(membersByNameHash) { - entry := membersByNameHash[namehash] - errs = append(errs, fmt.Errorf("read dir: no DIR_INDEX for DIR_ITEM crc32c(%q)=%#x", - entry.Name, namehash)) - printDirEntry(fs, fsTree, prefix1+tT, prefix1+tl, entry) - } - for i, err := range errs { - p0, p1 := tT, tl - if i == len(errs)-1 { - p0, p1 = tL, tS - } - fmt.Printf("%serror: %s\n", prefix1+p0, strings.ReplaceAll(err.Error(), "\n", prefix1+p1+" \n")) - } -} - -func printDirEntry(fs *btrfs.FS, fsTree btrfs.ObjID, prefix0, prefix1 string, entry btrfsitem.DirEntry) { - if len(entry.Data) != 0 { - fmt.Printf("%s%q: error: TODO: I don't know how to handle dirent.data\n", - prefix0, entry.Name) - return - } - switch entry.Type { - case btrfsitem.FT_DIR: - switch entry.Location.ItemType { - case btrfsitem.INODE_ITEM_KEY: - printDir(fs, fsTree, prefix0, prefix1, string(entry.Name), entry.Location.ObjectID) - case btrfsitem.ROOT_ITEM_KEY: - key := entry.Location - key.Offset = 0 - printSubvol(fs, prefix0, prefix1, string(entry.Name), key) - default: - fmt.Printf("%s%q\t[location=%v type=%v] error: I'm not sure how to print a %v directory\n", - prefix0, entry.Name, entry.Location, entry.Type, entry.Location.ItemType) - } - default: - fmt.Printf("%s%q\t[location=%v type=%v]\n", prefix0, entry.Name, entry.Location, entry.Type) - } -} diff --git a/cmd/btrfs-ls-trees/main.go b/cmd/btrfs-ls-trees/main.go deleted file mode 100644 index a0ae460..0000000 --- a/cmd/btrfs-ls-trees/main.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package main - -import ( - "fmt" - "os" - "strconv" - "text/tabwriter" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" - "git.lukeshu.com/btrfs-progs-ng/lib/util" -) - -func main() { - if err := Main(os.Args[1:]...); err != nil { - fmt.Fprintf(os.Stderr, "%v: error: %v\n", os.Args[0], err) - os.Exit(1) - } -} - -func Main(imgfilenames ...string) (err error) { - maybeSetErr := func(_err error) { - if _err != nil && err == nil { - err = _err - } - } - - fs, err := btrfsutil.Open(os.O_RDONLY, imgfilenames...) - if err != nil { - return err - } - defer func() { - maybeSetErr(fs.Close()) - }() - - var treeErrCnt int - var treeItemCnt map[btrfsitem.Type]int - btrfsutil.WalkAllTrees(fs, btrfsutil.WalkAllTreesHandler{ - PreTree: func(name string, treeID btrfs.ObjID) { - treeErrCnt = 0 - treeItemCnt = make(map[btrfsitem.Type]int) - fmt.Printf("tree id=%v name=%q\n", treeID, name) - }, - Err: func(_ error) { - treeErrCnt++ - }, - TreeWalkHandler: btrfs.TreeWalkHandler{ - Item: func(_ btrfs.TreePath, item btrfs.Item) error { - typ := item.Head.Key.ItemType - treeItemCnt[typ] = treeItemCnt[typ] + 1 - return nil - }, - }, - PostTree: func(_ string, _ btrfs.ObjID) { - totalItems := 0 - for _, cnt := range treeItemCnt { - totalItems += cnt - } - numWidth := len(strconv.Itoa(util.Max(treeErrCnt, totalItems))) - - table := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0) - fmt.Fprintf(table, " errors\t% *s\n", numWidth, strconv.Itoa(treeErrCnt)) - for _, typ := range util.SortedMapKeys(treeItemCnt) { - fmt.Fprintf(table, " %v items\t% *s\n", typ, numWidth, strconv.Itoa(treeItemCnt[typ])) - } - fmt.Fprintf(table, " total items\t% *s\n", numWidth, strconv.Itoa(totalItems)) - table.Flush() - }, - }) - - return nil -} diff --git a/cmd/btrfs-mount/main.go b/cmd/btrfs-mount/main.go deleted file mode 100644 index 7debb3a..0000000 --- a/cmd/btrfs-mount/main.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package main - -import ( - "context" - "fmt" - "os" - - "github.com/datawire/dlib/dgroup" - "github.com/datawire/dlib/dlog" - "github.com/sirupsen/logrus" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" -) - -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 := btrfsutil.Open(os.O_RDONLY, imgfilenames...) - if err != nil { - return err - } - defer func() { - maybeSetErr(fs.Close()) - }() - - return btrfsinspect.MountRO(ctx, fs, mountpoint) -} diff --git a/cmd/btrfs-rec/inspect_dumptrees.go b/cmd/btrfs-rec/inspect_dumptrees.go new file mode 100644 index 0000000..5ddff60 --- /dev/null +++ b/cmd/btrfs-rec/inspect_dumptrees.go @@ -0,0 +1,31 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package main + +import ( + "fmt" + "os" + + "github.com/datawire/ocibuild/pkg/cliutil" + "github.com/spf13/cobra" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect" +) + +func init() { + inspectors = append(inspectors, subcommand{ + Command: cobra.Command{ + Use: "dump-trees", + Short: "A clone of `btrfs inspect-internal dump-tree`", + Args: cliutil.WrapPositionalArgs(cobra.NoArgs), + }, + RunE: func(fs *btrfs.FS, _ *cobra.Command, _ []string) error { + const version = "5.18.1" + fmt.Printf("btrfs-progs v%v\n", version) + return btrfsinspect.DumpTrees(os.Stdout, os.Stderr, fs) + }, + }) +} diff --git a/cmd/btrfs-rec/inspect_lsfiles.go b/cmd/btrfs-rec/inspect_lsfiles.go new file mode 100644 index 0000000..2da31aa --- /dev/null +++ b/cmd/btrfs-rec/inspect_lsfiles.go @@ -0,0 +1,187 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package main + +import ( + "fmt" + "reflect" + "strings" + + "github.com/datawire/dlib/derror" + "github.com/datawire/ocibuild/pkg/cliutil" + "github.com/spf13/cobra" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem" + "git.lukeshu.com/btrfs-progs-ng/lib/util" +) + +func init() { + inspectors = append(inspectors, subcommand{ + Command: cobra.Command{ + Use: "ls-files", + Short: "A listing of all files in the filesystem", + Args: cliutil.WrapPositionalArgs(cobra.NoArgs), + }, + RunE: func(fs *btrfs.FS, _ *cobra.Command, _ []string) error { + printSubvol(fs, "", "", "/", btrfs.Key{ + ObjectID: btrfs.FS_TREE_OBJECTID, + ItemType: btrfsitem.ROOT_ITEM_KEY, + Offset: 0, + }) + return nil + }, + }) +} + +const ( + tS = "    " + tl = "│   " + tT = "├── " + tL = "└── " +) + +func printSubvol(fs *btrfs.FS, prefix0, prefix1, name string, key btrfs.Key) { + root, err := fs.TreeLookup(btrfs.ROOT_TREE_OBJECTID, key) + if err != nil { + fmt.Printf("%s%q error: could not look up root %v: %v\n", prefix0, name, key, err) + return + } + rootBody := root.Body.(btrfsitem.Root) + + printDir(fs, root.Head.Key.ObjectID, prefix0, prefix1, name, rootBody.RootDirID) +} + +func printDir(fs *btrfs.FS, fsTree btrfs.ObjID, prefix0, prefix1, dirName string, dirInode btrfs.ObjID) { + var errs derror.MultiError + items, err := fs.TreeSearchAll(fsTree, func(key btrfs.Key) int { + return util.CmpUint(dirInode, key.ObjectID) + }) + if err != nil { + errs = append(errs, fmt.Errorf("read dir: %w", err)) + } + var dirInodeDat btrfsitem.Inode + var dirInodeDatOK bool + membersByIndex := make(map[uint64]btrfsitem.DirEntry) + membersByNameHash := make(map[uint64]btrfsitem.DirEntry) + for _, item := range items { + switch item.Head.Key.ItemType { + case btrfsitem.INODE_ITEM_KEY: + if dirInodeDatOK { + if !reflect.DeepEqual(dirInodeDat, item.Body.(btrfsitem.Inode)) { + errs = append(errs, fmt.Errorf("read dir: multiple inodes")) + } + continue + } + dirInodeDat = item.Body.(btrfsitem.Inode) + dirInodeDatOK = true + case btrfsitem.INODE_REF_KEY: + // TODO + case btrfsitem.DIR_ITEM_KEY: + entry := item.Body.(btrfsitem.DirEntry) + namehash := btrfsitem.NameHash(entry.Name) + if namehash != item.Head.Key.Offset { + errs = append(errs, fmt.Errorf("read dir: direntry crc32c mismatch: key=%#x crc32c(%q)=%#x", + item.Head.Key.Offset, entry.Name, namehash)) + continue + } + if other, exists := membersByNameHash[namehash]; exists { + if !reflect.DeepEqual(entry, other) { + if string(entry.Name) == string(other.Name) { + errs = append(errs, fmt.Errorf("read dir: multiple instances of direntry crc32c(%q)=%#x", + entry.Name, namehash)) + } else { + errs = append(errs, fmt.Errorf("read dir: multiple instances of direntry crc32c(%q|%q)=%#x", + other.Name, entry.Name, namehash)) + } + } + continue + } + membersByNameHash[btrfsitem.NameHash(entry.Name)] = entry + case btrfsitem.DIR_INDEX_KEY: + index := item.Head.Key.Offset + entry := item.Body.(btrfsitem.DirEntry) + if other, exists := membersByIndex[index]; exists { + if !reflect.DeepEqual(entry, other) { + errs = append(errs, fmt.Errorf("read dir: multiple instances of direntry index %v", index)) + } + continue + } + membersByIndex[index] = entry + //case btrfsitem.XATTR_ITEM_KEY: + default: + errs = append(errs, fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType)) + } + } + fmt.Printf("%s%q\t[ino=%d", + prefix0, dirName, dirInode) + if dirInodeDatOK { + fmt.Printf("\tuid=%d\tgid=%d\tsize=%d]\n", + dirInodeDat.UID, dirInodeDat.GID, dirInodeDat.Size) + } else { + err := fmt.Errorf("read dir: no inode data") + if len(items) == 0 && len(errs) == 1 { + err = errs[0] + errs = nil + } + fmt.Printf("]\terror: %v\n", err) + } + for i, index := range util.SortedMapKeys(membersByIndex) { + entry := membersByIndex[index] + namehash := btrfsitem.NameHash(entry.Name) + if other, ok := membersByNameHash[namehash]; ok { + if !reflect.DeepEqual(entry, other) { + errs = append(errs, fmt.Errorf("read dir: index=%d disagrees with crc32c(%q)=%#x", + index, entry.Name, namehash)) + } + delete(membersByNameHash, namehash) + } else { + errs = append(errs, fmt.Errorf("read dir: no DIR_ITEM crc32c(%q)=%#x for DIR_INDEX index=%d", + entry.Name, namehash, index)) + } + p0, p1 := tT, tl + if (i == len(membersByIndex)-1) && (len(membersByNameHash) == 0) && (len(errs) == 0) { + p0, p1 = tL, tS + } + printDirEntry(fs, fsTree, prefix1+p0, prefix1+p1, entry) + } + for _, namehash := range util.SortedMapKeys(membersByNameHash) { + entry := membersByNameHash[namehash] + errs = append(errs, fmt.Errorf("read dir: no DIR_INDEX for DIR_ITEM crc32c(%q)=%#x", + entry.Name, namehash)) + printDirEntry(fs, fsTree, prefix1+tT, prefix1+tl, entry) + } + for i, err := range errs { + p0, p1 := tT, tl + if i == len(errs)-1 { + p0, p1 = tL, tS + } + fmt.Printf("%serror: %s\n", prefix1+p0, strings.ReplaceAll(err.Error(), "\n", prefix1+p1+" \n")) + } +} + +func printDirEntry(fs *btrfs.FS, fsTree btrfs.ObjID, prefix0, prefix1 string, entry btrfsitem.DirEntry) { + if len(entry.Data) != 0 { + fmt.Printf("%s%q: error: TODO: I don't know how to handle dirent.data\n", + prefix0, entry.Name) + return + } + switch entry.Type { + case btrfsitem.FT_DIR: + switch entry.Location.ItemType { + case btrfsitem.INODE_ITEM_KEY: + printDir(fs, fsTree, prefix0, prefix1, string(entry.Name), entry.Location.ObjectID) + case btrfsitem.ROOT_ITEM_KEY: + key := entry.Location + key.Offset = 0 + printSubvol(fs, prefix0, prefix1, string(entry.Name), key) + default: + fmt.Printf("%s%q\t[location=%v type=%v] error: I'm not sure how to print a %v directory\n", + prefix0, entry.Name, entry.Location, entry.Type, entry.Location.ItemType) + } + default: + fmt.Printf("%s%q\t[location=%v type=%v]\n", prefix0, entry.Name, entry.Location, entry.Type) + } +} diff --git a/cmd/btrfs-rec/inspect_lstrees.go b/cmd/btrfs-rec/inspect_lstrees.go new file mode 100644 index 0000000..976514f --- /dev/null +++ b/cmd/btrfs-rec/inspect_lstrees.go @@ -0,0 +1,68 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package main + +import ( + "fmt" + "os" + "strconv" + "text/tabwriter" + + "github.com/datawire/ocibuild/pkg/cliutil" + "github.com/spf13/cobra" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" + "git.lukeshu.com/btrfs-progs-ng/lib/util" +) + +func init() { + inspectors = append(inspectors, subcommand{ + Command: cobra.Command{ + Use: "ls-trees", + Short: "A brief view what types of items are in each tree", + Args: cliutil.WrapPositionalArgs(cobra.NoArgs), + }, + RunE: func(fs *btrfs.FS, _ *cobra.Command, _ []string) error { + var treeErrCnt int + var treeItemCnt map[btrfsitem.Type]int + btrfsutil.WalkAllTrees(fs, btrfsutil.WalkAllTreesHandler{ + PreTree: func(name string, treeID btrfs.ObjID) { + treeErrCnt = 0 + treeItemCnt = make(map[btrfsitem.Type]int) + fmt.Printf("tree id=%v name=%q\n", treeID, name) + }, + Err: func(_ error) { + treeErrCnt++ + }, + TreeWalkHandler: btrfs.TreeWalkHandler{ + Item: func(_ btrfs.TreePath, item btrfs.Item) error { + typ := item.Head.Key.ItemType + treeItemCnt[typ] = treeItemCnt[typ] + 1 + return nil + }, + }, + PostTree: func(_ string, _ btrfs.ObjID) { + totalItems := 0 + for _, cnt := range treeItemCnt { + totalItems += cnt + } + numWidth := len(strconv.Itoa(util.Max(treeErrCnt, totalItems))) + + table := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0) + fmt.Fprintf(table, " errors\t% *s\n", numWidth, strconv.Itoa(treeErrCnt)) + for _, typ := range util.SortedMapKeys(treeItemCnt) { + fmt.Fprintf(table, " %v items\t% *s\n", typ, numWidth, strconv.Itoa(treeItemCnt[typ])) + } + fmt.Fprintf(table, " total items\t% *s\n", numWidth, strconv.Itoa(totalItems)) + table.Flush() + }, + }) + + return nil + }, + }) +} diff --git a/cmd/btrfs-rec/inspect_mount.go b/cmd/btrfs-rec/inspect_mount.go new file mode 100644 index 0000000..5aae54a --- /dev/null +++ b/cmd/btrfs-rec/inspect_mount.go @@ -0,0 +1,26 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package main + +import ( + "github.com/datawire/ocibuild/pkg/cliutil" + "github.com/spf13/cobra" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect" +) + +func init() { + inspectors = append(inspectors, subcommand{ + Command: cobra.Command{ + Use: "mount MOUNTPOINT", + Short: "Mount the filesystem read-only", + Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), + }, + RunE: func(fs *btrfs.FS, cmd *cobra.Command, args []string) error { + return btrfsinspect.MountRO(cmd.Context(), fs, args[0]) + }, + }) +} diff --git a/cmd/btrfs-rec/main.go b/cmd/btrfs-rec/main.go index 9b285b1..b68bdd8 100644 --- a/cmd/btrfs-rec/main.go +++ b/cmd/btrfs-rec/main.go @@ -74,7 +74,7 @@ func main() { var openFlag int = os.O_RDONLY argparserInspect := &cobra.Command{ - Use: "inpsect {[flags]|SUBCOMMAND}", + Use: "inspect {[flags]|SUBCOMMAND}", Short: "Inspect (but don't modify) a broken btrfs filesystem", Args: cliutil.WrapPositionalArgs(cliutil.OnlySubcommands), diff --git a/cmd/btrfs-rec/repair_clearbadnodes.go b/cmd/btrfs-rec/repair_clearbadnodes.go new file mode 100644 index 0000000..58b8bd6 --- /dev/null +++ b/cmd/btrfs-rec/repair_clearbadnodes.go @@ -0,0 +1,28 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package main + +import ( + "os" + + "github.com/datawire/ocibuild/pkg/cliutil" + "github.com/spf13/cobra" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsrepair" +) + +func init() { + repairers = append(repairers, subcommand{ + Command: cobra.Command{ + Use: "clear-bad-nodes", + Short: "Overwrite corrupt nodes with empty nodes", + Args: cliutil.WrapPositionalArgs(cobra.NoArgs), + }, + RunE: func(fs *btrfs.FS, _ *cobra.Command, _ []string) error { + return btrfsrepair.ClearBadNodes(os.Stdout, os.Stderr, fs) + }, + }) +} -- cgit v1.2.3-2-g168b