diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-07-14 04:45:48 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-07-14 05:45:45 -0600 |
commit | bb73a2fb7678698353bb995754e8702caa2f6e0a (patch) | |
tree | 0ecd149e8ea61db883c7450accfacf42db08b82e /cmd/btrfs-rec | |
parent | ae42c45061e6e68ed2cdb455ba45a14c39f7cae7 (diff) |
wip ls-files
Diffstat (limited to 'cmd/btrfs-rec')
-rw-r--r-- | cmd/btrfs-rec/inspect_lsfiles.go | 294 |
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)) } |