summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-03-03 09:56:06 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-03-17 22:30:37 -0400
commitcd199b402673b154add1e492b8ddfec43309dc5d (patch)
treef28cfa320367b8f73f90e9354cd9c2c4a9c5feb7
parente1569565240cf6487ebeb5ec4c1386c8ba70f493 (diff)
cmd/btrfs-rec: inspect ls-files: Move the code to a sub-package
-rw-r--r--cmd/btrfs-rec/inspect/lsfiles/lsfiles.go249
-rw-r--r--cmd/btrfs-rec/inspect_lsfiles.go226
2 files changed, 253 insertions, 222 deletions
diff --git a/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go b/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
new file mode 100644
index 0000000..a713b8a
--- /dev/null
+++ b/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
@@ -0,0 +1,249 @@
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Package lsfiles is the guts of the `btrfs-rec inspect ls-files`
+// command, which prints a tree-listing of all files in the
+// filesystem.
+package lsfiles
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "path"
+ "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/btrfs/btrfsprim"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
+ "git.lukeshu.com/btrfs-progs-ng/lib/diskio"
+ "git.lukeshu.com/btrfs-progs-ng/lib/maps"
+ "git.lukeshu.com/btrfs-progs-ng/lib/textui"
+)
+
+func LsFiles(
+ out io.Writer,
+ fs interface {
+ btrfstree.TreeOperator
+ Superblock() (*btrfstree.Superblock, error)
+ diskio.ReaderAt[btrfsvol.LogicalAddr]
+ },
+) (err error) {
+ defer func() {
+ if _err := derror.PanicToError(recover()); _err != nil {
+ textui.Fprintf(out, "\n\n%+v\n", _err)
+ err = _err
+ }
+ }()
+
+ printSubvol(out, "", true, "/", btrfs.NewSubvolume(
+ fs,
+ btrfsprim.FS_TREE_OBJECTID,
+ false,
+ ))
+
+ return nil
+}
+
+const (
+ tS = "    "
+ tl = "│   "
+ tT = "├── "
+ tL = "└── "
+)
+
+func printText(out io.Writer, prefix string, isLast bool, name, text string) {
+ first, rest := tT, tl
+ if isLast {
+ first, rest = tL, tS
+ }
+ for i, line := range strings.Split(textui.Sprintf("%q %s", name, text), "\n") {
+ _, _ = io.WriteString(out, prefix)
+ if i == 0 {
+ _, _ = io.WriteString(out, first)
+ } else {
+ _, _ = io.WriteString(out, rest)
+ }
+ _, _ = io.WriteString(out, line)
+ _, _ = io.WriteString(out, "\n")
+ }
+}
+
+func printSubvol(out io.Writer, prefix string, isLast bool, name string, subvol *btrfs.Subvolume) {
+ rootInode, err := subvol.GetRootInode()
+ if err != nil {
+ printText(out, prefix, isLast, name+"/", textui.Sprintf("subvol_id=%v err=%v",
+ subvol.TreeID, fmtErr(err)))
+ return
+ }
+ dir, err := subvol.LoadDir(rootInode)
+ if err != nil {
+ printText(out, prefix, isLast, name+"/", textui.Sprintf("subvol_id=%v err=%v",
+ subvol.TreeID, fmtErr(err)))
+ return
+ }
+ if name == "/" {
+ printDir(out, prefix, isLast, name, dir)
+ return
+ }
+ printText(out, prefix, isLast, name+"/", textui.Sprintf("subvol_id=%v", subvol.TreeID))
+ if isLast {
+ prefix += tS
+ } else {
+ prefix += tl
+ }
+ printDir(out, prefix, true, name, dir)
+}
+
+func fmtErr(err error) string {
+ errStr := err.Error()
+ if strings.Contains(errStr, "\n") {
+ errStr = "\\\n" + errStr
+ }
+ return errStr
+}
+
+func fmtInode(inode btrfs.BareInode) string {
+ var mode btrfsitem.StatMode
+ if inode.InodeItem == nil {
+ inode.Errs = append(inode.Errs, errors.New("missing INODE_ITEM"))
+ } else {
+ mode = inode.InodeItem.Mode
+ }
+ ret := textui.Sprintf("ino=%v mode=%v", inode.Inode, mode)
+ if len(inode.Errs) > 0 {
+ ret += " err=" + fmtErr(inode.Errs)
+ }
+ return ret
+}
+
+func printDir(out io.Writer, prefix string, isLast bool, name string, dir *btrfs.Dir) {
+ printText(out, prefix, isLast, name+"/", fmtInode(dir.BareInode))
+ if isLast {
+ prefix += tS
+ } else {
+ prefix += tl
+ }
+ for i, childName := range maps.SortedKeys(dir.ChildrenByName) {
+ printDirEntry(
+ out,
+ prefix,
+ i == len(dir.ChildrenByName)-1,
+ dir.SV,
+ path.Join(name, childName),
+ dir.ChildrenByName[childName])
+ }
+}
+
+func printDirEntry(out io.Writer, prefix string, isLast bool, subvol *btrfs.Subvolume, name string, entry btrfsitem.DirEntry) {
+ if len(entry.Data) != 0 {
+ panic(fmt.Errorf("TODO: I don't know how to handle dirent.data: %q", name))
+ }
+ switch entry.Type {
+ case btrfsitem.FT_DIR:
+ switch entry.Location.ItemType {
+ case btrfsitem.INODE_ITEM_KEY:
+ dir, err := subvol.LoadDir(entry.Location.ObjectID)
+ if err != nil {
+ printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
+ return
+ }
+ printDir(out, prefix, isLast, name, dir)
+ case btrfsitem.ROOT_ITEM_KEY:
+ printSubvol(out, prefix, isLast, name, subvol.NewChildSubvolume(entry.Location.ObjectID))
+ default:
+ panic(fmt.Errorf("TODO: I don't know how to handle an FT_DIR with location.ItemType=%v: %q",
+ entry.Location.ItemType, name))
+ }
+ case btrfsitem.FT_SYMLINK:
+ if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
+ panic(fmt.Errorf("TODO: I don't know how to handle an FT_SYMLINK with location.ItemType=%v: %q",
+ entry.Location.ItemType, name))
+ }
+ file, err := subvol.LoadFile(entry.Location.ObjectID)
+ if err != nil {
+ printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
+ return
+ }
+ printSymlink(out, prefix, isLast, name, file)
+ case btrfsitem.FT_REG_FILE:
+ if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
+ panic(fmt.Errorf("TODO: I don't know how to handle an FT_REG_FILE with location.ItemType=%v: %q",
+ entry.Location.ItemType, name))
+ }
+ file, err := subvol.LoadFile(entry.Location.ObjectID)
+ if err != nil {
+ printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
+ return
+ }
+ printFile(out, prefix, isLast, name, file)
+ case btrfsitem.FT_SOCK:
+ if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
+ panic(fmt.Errorf("TODO: I don't know how to handle an FT_SOCK with location.ItemType=%v: %q",
+ entry.Location.ItemType, name))
+ }
+ file, err := subvol.LoadFile(entry.Location.ObjectID)
+ if err != nil {
+ printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
+ return
+ }
+ printSocket(out, prefix, isLast, name, file)
+ case btrfsitem.FT_FIFO:
+ if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
+ panic(fmt.Errorf("TODO: I don't know how to handle an FT_FIFO with location.ItemType=%v: %q",
+ entry.Location.ItemType, name))
+ }
+ file, err := subvol.LoadFile(entry.Location.ObjectID)
+ if err != nil {
+ printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
+ return
+ }
+ printPipe(out, prefix, isLast, name, file)
+ default:
+ panic(fmt.Errorf("TODO: I don't know how to handle a fileType=%v: %q",
+ entry.Type, name))
+ }
+}
+
+func printSymlink(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
+ var tgt []byte
+ if file.InodeItem != nil {
+ var err error
+ tgt, err = io.ReadAll(io.NewSectionReader(file, 0, file.InodeItem.Size))
+ if err != nil {
+ file.Errs = append(file.Errs, err)
+ }
+ }
+ printText(out, prefix, isLast, name, textui.Sprintf(
+ "-> %q : %s",
+ tgt,
+ fmtInode(file.BareInode)))
+}
+
+func printFile(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
+ if file.InodeItem != nil {
+ if _, err := io.Copy(io.Discard, io.NewSectionReader(file, 0, file.InodeItem.Size)); err != nil {
+ file.Errs = append(file.Errs, err)
+ }
+ }
+ printText(out, prefix, isLast, name, fmtInode(file.BareInode))
+}
+
+func printSocket(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
+ if file.InodeItem != nil && file.InodeItem.Size > 0 {
+ panic(fmt.Errorf("TODO: I don't know how to handle a socket with size>0: %q", name))
+ }
+ printText(out, prefix, isLast, name, fmtInode(file.BareInode))
+}
+
+func printPipe(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
+ if file.InodeItem != nil && file.InodeItem.Size > 0 {
+ panic(fmt.Errorf("TODO: I don't know how to handle a pipe with size>0: %q", name))
+ }
+ printText(out, prefix, isLast, name, fmtInode(file.BareInode))
+}
diff --git a/cmd/btrfs-rec/inspect_lsfiles.go b/cmd/btrfs-rec/inspect_lsfiles.go
index e9fd057..04b5ec5 100644
--- a/cmd/btrfs-rec/inspect_lsfiles.go
+++ b/cmd/btrfs-rec/inspect_lsfiles.go
@@ -6,23 +6,14 @@ package main
import (
"bufio"
- "errors"
- "fmt"
- "io"
"os"
- "path"
- "strings"
- "github.com/datawire/dlib/derror"
"github.com/datawire/ocibuild/pkg/cliutil"
"github.com/spf13/cobra"
+ "git.lukeshu.com/btrfs-progs-ng/cmd/btrfs-rec/inspect/lsfiles"
"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/btrfsprim"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfsutil"
- "git.lukeshu.com/btrfs-progs-ng/lib/maps"
- "git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
func init() {
@@ -37,219 +28,10 @@ func init() {
err = _err
}
}()
- defer func() {
- if _err := derror.PanicToError(recover()); _err != nil {
- textui.Fprintf(out, "\n\n%+v\n", _err)
- err = _err
- }
- }()
- ctx := cmd.Context()
- printSubvol(out, "", true, "/", btrfs.NewSubvolume(
- btrfsutil.NewOldRebuiltForrest(ctx, fs),
- btrfsprim.FS_TREE_OBJECTID,
- false,
- ))
-
- return nil
+ return lsfiles.LsFiles(
+ out,
+ btrfsutil.NewOldRebuiltForrest(cmd.Context(), fs))
}),
})
}
-
-const (
- tS = "    "
- tl = "│   "
- tT = "├── "
- tL = "└── "
-)
-
-func printText(out io.Writer, prefix string, isLast bool, name, text string) {
- first, rest := tT, tl
- if isLast {
- first, rest = tL, tS
- }
- for i, line := range strings.Split(textui.Sprintf("%q %s", name, text), "\n") {
- _, _ = io.WriteString(out, prefix)
- if i == 0 {
- _, _ = io.WriteString(out, first)
- } else {
- _, _ = io.WriteString(out, rest)
- }
- _, _ = io.WriteString(out, line)
- _, _ = io.WriteString(out, "\n")
- }
-}
-
-func printSubvol(out io.Writer, prefix string, isLast bool, name string, subvol *btrfs.Subvolume) {
- rootInode, err := subvol.GetRootInode()
- if err != nil {
- printText(out, prefix, isLast, name+"/", textui.Sprintf("subvol_id=%v err=%v",
- subvol.TreeID, fmtErr(err)))
- return
- }
- dir, err := subvol.LoadDir(rootInode)
- if err != nil {
- printText(out, prefix, isLast, name+"/", textui.Sprintf("subvol_id=%v err=%v",
- subvol.TreeID, fmtErr(err)))
- return
- }
- if name == "/" {
- printDir(out, prefix, isLast, name, dir)
- return
- }
- printText(out, prefix, isLast, name+"/", textui.Sprintf("subvol_id=%v", subvol.TreeID))
- if isLast {
- prefix += tS
- } else {
- prefix += tl
- }
- printDir(out, prefix, true, name, dir)
-}
-
-func fmtErr(err error) string {
- errStr := err.Error()
- if strings.Contains(errStr, "\n") {
- errStr = "\\\n" + errStr
- }
- return errStr
-}
-
-func fmtInode(inode btrfs.BareInode) string {
- var mode btrfsitem.StatMode
- if inode.InodeItem == nil {
- inode.Errs = append(inode.Errs, errors.New("missing INODE_ITEM"))
- } else {
- mode = inode.InodeItem.Mode
- }
- ret := textui.Sprintf("ino=%v mode=%v", inode.Inode, mode)
- if len(inode.Errs) > 0 {
- ret += " err=" + fmtErr(inode.Errs)
- }
- return ret
-}
-
-func printDir(out io.Writer, prefix string, isLast bool, name string, dir *btrfs.Dir) {
- printText(out, prefix, isLast, name+"/", fmtInode(dir.BareInode))
- if isLast {
- prefix += tS
- } else {
- prefix += tl
- }
- for i, childName := range maps.SortedKeys(dir.ChildrenByName) {
- printDirEntry(
- out,
- prefix,
- i == len(dir.ChildrenByName)-1,
- dir.SV,
- path.Join(name, childName),
- dir.ChildrenByName[childName])
- }
-}
-
-func printDirEntry(out io.Writer, prefix string, isLast bool, subvol *btrfs.Subvolume, name string, entry btrfsitem.DirEntry) {
- if len(entry.Data) != 0 {
- panic(fmt.Errorf("TODO: I don't know how to handle dirent.data: %q", name))
- }
- switch entry.Type {
- case btrfsitem.FT_DIR:
- switch entry.Location.ItemType {
- case btrfsitem.INODE_ITEM_KEY:
- dir, err := subvol.LoadDir(entry.Location.ObjectID)
- if err != nil {
- printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
- return
- }
- printDir(out, prefix, isLast, name, dir)
- case btrfsitem.ROOT_ITEM_KEY:
- printSubvol(out, prefix, isLast, name, subvol.NewChildSubvolume(entry.Location.ObjectID))
- default:
- panic(fmt.Errorf("TODO: I don't know how to handle an FT_DIR with location.ItemType=%v: %q",
- entry.Location.ItemType, name))
- }
- case btrfsitem.FT_SYMLINK:
- if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
- panic(fmt.Errorf("TODO: I don't know how to handle an FT_SYMLINK with location.ItemType=%v: %q",
- entry.Location.ItemType, name))
- }
- file, err := subvol.LoadFile(entry.Location.ObjectID)
- if err != nil {
- printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
- return
- }
- printSymlink(out, prefix, isLast, name, file)
- case btrfsitem.FT_REG_FILE:
- if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
- panic(fmt.Errorf("TODO: I don't know how to handle an FT_REG_FILE with location.ItemType=%v: %q",
- entry.Location.ItemType, name))
- }
- file, err := subvol.LoadFile(entry.Location.ObjectID)
- if err != nil {
- printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
- return
- }
- printFile(out, prefix, isLast, name, file)
- case btrfsitem.FT_SOCK:
- if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
- panic(fmt.Errorf("TODO: I don't know how to handle an FT_SOCK with location.ItemType=%v: %q",
- entry.Location.ItemType, name))
- }
- file, err := subvol.LoadFile(entry.Location.ObjectID)
- if err != nil {
- printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
- return
- }
- printSocket(out, prefix, isLast, name, file)
- case btrfsitem.FT_FIFO:
- if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
- panic(fmt.Errorf("TODO: I don't know how to handle an FT_FIFO with location.ItemType=%v: %q",
- entry.Location.ItemType, name))
- }
- file, err := subvol.LoadFile(entry.Location.ObjectID)
- if err != nil {
- printText(out, prefix, isLast, name, textui.Sprintf("%v err=%v", entry.Type, fmtErr(err)))
- return
- }
- printPipe(out, prefix, isLast, name, file)
- default:
- panic(fmt.Errorf("TODO: I don't know how to handle a fileType=%v: %q",
- entry.Type, name))
- }
-}
-
-func printSymlink(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
- var tgt []byte
- if file.InodeItem != nil {
- var err error
- tgt, err = io.ReadAll(io.NewSectionReader(file, 0, file.InodeItem.Size))
- if err != nil {
- file.Errs = append(file.Errs, err)
- }
- }
- printText(out, prefix, isLast, name, textui.Sprintf(
- "-> %q : %s",
- tgt,
- fmtInode(file.BareInode)))
-}
-
-func printFile(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
- if file.InodeItem != nil {
- if _, err := io.Copy(io.Discard, io.NewSectionReader(file, 0, file.InodeItem.Size)); err != nil {
- file.Errs = append(file.Errs, err)
- }
- }
- printText(out, prefix, isLast, name, fmtInode(file.BareInode))
-}
-
-func printSocket(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
- if file.InodeItem != nil && file.InodeItem.Size > 0 {
- panic(fmt.Errorf("TODO: I don't know how to handle a socket with size>0: %q", name))
- }
- printText(out, prefix, isLast, name, fmtInode(file.BareInode))
-}
-
-func printPipe(out io.Writer, prefix string, isLast bool, name string, file *btrfs.File) {
- if file.InodeItem != nil && file.InodeItem.Size > 0 {
- panic(fmt.Errorf("TODO: I don't know how to handle a pipe with size>0: %q", name))
- }
- printText(out, prefix, isLast, name, fmtInode(file.BareInode))
-}