summaryrefslogtreecommitdiff
path: root/cmd/btrfs-rec
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-07-14 04:45:48 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-07-14 05:45:45 -0600
commitbb73a2fb7678698353bb995754e8702caa2f6e0a (patch)
tree0ecd149e8ea61db883c7450accfacf42db08b82e /cmd/btrfs-rec
parentae42c45061e6e68ed2cdb455ba45a14c39f7cae7 (diff)
wip ls-files
Diffstat (limited to 'cmd/btrfs-rec')
-rw-r--r--cmd/btrfs-rec/inspect_lsfiles.go294
1 files changed, 172 insertions, 122 deletions
diff --git a/cmd/btrfs-rec/inspect_lsfiles.go b/cmd/btrfs-rec/inspect_lsfiles.go
index 44b4363..e706c1f 100644
--- a/cmd/btrfs-rec/inspect_lsfiles.go
+++ b/cmd/btrfs-rec/inspect_lsfiles.go
@@ -5,8 +5,12 @@
package main
import (
+ "bufio"
+ "errors"
"fmt"
- "reflect"
+ "io"
+ "os"
+ "path"
"strings"
"github.com/datawire/dlib/derror"
@@ -15,7 +19,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/containers"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil"
"git.lukeshu.com/btrfs-progs-ng/lib/maps"
)
@@ -26,12 +30,22 @@ func init() {
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,
+ RunE: func(fs *btrfs.FS, cmd *cobra.Command, _ []string) (err error) {
+ defer func() {
+ if r := derror.PanicToError(recover()); r != nil {
+ fmt.Printf("\n\n%+v\n", r)
+ err = fmt.Errorf("panicked")
+ }
+ }()
+ ctx := cmd.Context()
+
+ out := bufio.NewWriter(os.Stdout)
+ printSubvol(out, "", true, "/", &btrfs.Subvolume{
+ FS: btrfsutil.NewBrokenTrees(ctx, fs),
+ TreeID: btrfs.FS_TREE_OBJECTID,
})
+ out.Flush()
+
return nil
},
})
@@ -44,145 +58,181 @@ const (
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
+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(fmt.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")
}
- rootBody := root.Body.(btrfsitem.Root)
-
- printDir(fs, root.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 containers.NativeCmp(dirInode, key.ObjectID)
- })
+func printSubvol(out io.Writer, prefix string, isLast bool, name string, subvol *btrfs.Subvolume) {
+ rootInode, err := subvol.GetRootInode()
if err != nil {
- errs = append(errs, fmt.Errorf("read dir: %w", err))
+ printText(out, prefix, isLast, name, "err="+err.Error())
+ return
}
- 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.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.Key.Offset {
- errs = append(errs, fmt.Errorf("read dir: direntry crc32c mismatch: key=%#x crc32c(%q)=%#x",
- item.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.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.Key.ItemType))
- }
+ dir, err := subvol.LoadDir(rootInode)
+ if err != nil {
+ printText(out, prefix, isLast, name, "err="+err.Error())
+ return
}
- 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)
+ printDir(out, prefix, isLast, name, dir)
+}
+
+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 {
- 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)
+ mode = inode.InodeItem.Mode
}
- for i, index := range maps.SortedKeys(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)
+ ret := fmt.Sprintf("ino=%v mode=%v", inode.Inode, mode)
+ if len(inode.Errs) > 0 {
+ errStr := inode.Errs.Error()
+ if strings.Contains(errStr, "\n") {
+ ret += " err=\\\n" + errStr
} 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
+ ret += " err=" + errStr
}
- printDirEntry(fs, fsTree, prefix1+p0, prefix1+p1, entry)
}
- for _, namehash := range maps.SortedKeys(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)
+ 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, 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"))
+ 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(fs *btrfs.FS, fsTree btrfs.ObjID, prefix0, prefix1 string, entry btrfsitem.DirEntry) {
+func printDirEntry(out io.Writer, prefix string, isLast bool, subvol *btrfs.Subvolume, name 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
+ 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:
- printDir(fs, fsTree, prefix0, prefix1, string(entry.Name), entry.Location.ObjectID)
+ dir, err := subvol.LoadDir(entry.Location.ObjectID)
+ if err != nil {
+ printText(out, prefix, isLast, name, "err="+err.Error())
+ return
+ }
+ printDir(out, prefix, isLast, name, dir)
case btrfsitem.ROOT_ITEM_KEY:
- key := entry.Location
- key.Offset = 0
- printSubvol(fs, prefix0, prefix1, string(entry.Name), key)
+ printSubvol(out, prefix, isLast, name, &btrfs.Subvolume{
+ FS: subvol.FS,
+ TreeID: entry.Location.ObjectID,
+ })
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)
+ 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, "err="+err.Error())
+ 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, "err="+err.Error())
+ 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, "err="+err.Error())
+ 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, "err="+err.Error())
+ return
}
+ printPipe(out, prefix, isLast, name, file)
default:
- fmt.Printf("%s%q\t[location=%v type=%v]\n", prefix0, entry.Name, entry.Location, entry.Type)
+ panic(fmt.Errorf("TODO: I don't know how to handle an 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, fmt.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))
}