summaryrefslogtreecommitdiff
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
parentae42c45061e6e68ed2cdb455ba45a14c39f7cae7 (diff)
wip ls-files
-rw-r--r--cmd/btrfs-rec/inspect_lsfiles.go294
-rw-r--r--lib/btrfs/btrfsitem/item_fileextent.go24
-rw-r--r--lib/btrfs/btrfsitem/item_inode.go2
-rw-r--r--lib/btrfs/btrfsitem/item_inoderef.go36
-rw-r--r--lib/btrfs/btrfsitem/items_gen.go4
-rw-r--r--lib/btrfs/io4_fs.go16
-rw-r--r--lib/btrfsprogs/btrfsinspect/print_tree.go8
-rwxr-xr-xscripts/main.sh2
8 files changed, 240 insertions, 146 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))
}
diff --git a/lib/btrfs/btrfsitem/item_fileextent.go b/lib/btrfs/btrfsitem/item_fileextent.go
index a69c67a..b7e394e 100644
--- a/lib/btrfs/btrfsitem/item_fileextent.go
+++ b/lib/btrfs/btrfsitem/item_fileextent.go
@@ -28,20 +28,22 @@ type FileExtent struct { // EXTENT_DATA=108
binstruct.End `bin:"off=0x15"`
// only one of these, depending on .Type
- BodyInline []byte `bin:"-"` // .Type == FILE_EXTENT_INLINE
- BodyExtent struct { // .Type == FILE_EXTENT_REG or FILE_EXTENT_PREALLOC
- // Position and size of extent within the device
- DiskByteNr btrfsvol.LogicalAddr `bin:"off=0x0, siz=0x8"`
- DiskNumBytes btrfsvol.AddrDelta `bin:"off=0x8, siz=0x8"`
+ BodyInline []byte `bin:"-"` // .Type == FILE_EXTENT_INLINE
+ BodyExtent FileExtentExtent `bin:"-"` // .Type == FILE_EXTENT_REG or FILE_EXTENT_PREALLOC
+}
+
+type FileExtentExtent struct {
+ // Position and size of extent within the device
+ DiskByteNr btrfsvol.LogicalAddr `bin:"off=0x0, siz=0x8"`
+ DiskNumBytes btrfsvol.AddrDelta `bin:"off=0x8, siz=0x8"`
- // Position of data within the extent
- Offset btrfsvol.AddrDelta `bin:"off=0x10, siz=0x8"`
+ // Position of data within the extent
+ Offset btrfsvol.AddrDelta `bin:"off=0x10, siz=0x8"`
- // Decompressed/unencrypted size
- NumBytes int64 `bin:"off=0x18, siz=0x8"`
+ // Decompressed/unencrypted size
+ NumBytes int64 `bin:"off=0x18, siz=0x8"`
- binstruct.End `bin:"off=0x20"`
- } `bin:"-"`
+ binstruct.End `bin:"off=0x20"`
}
func (o *FileExtent) UnmarshalBinary(dat []byte) (int, error) {
diff --git a/lib/btrfs/btrfsitem/item_inode.go b/lib/btrfs/btrfsitem/item_inode.go
index 27204f6..49b2031 100644
--- a/lib/btrfs/btrfsitem/item_inode.go
+++ b/lib/btrfs/btrfsitem/item_inode.go
@@ -14,7 +14,7 @@ type Inode struct { // INODE_ITEM=1
Generation internal.Generation `bin:"off=0x00, siz=0x08"`
TransID int64 `bin:"off=0x08, siz=0x08"`
Size int64 `bin:"off=0x10, siz=0x08"` // stat
- NumBytes int64 `bin:"off=0x18, siz=0x08"`
+ NumBytes int64 `bin:"off=0x18, siz=0x08"` // allocated bytes, may be larger than size (or smaller if there are holes?)
BlockGroup int64 `bin:"off=0x20, siz=0x08"`
NLink int32 `bin:"off=0x28, siz=0x04"` // stat
UID int32 `bin:"off=0x2C, siz=0x04"` // stat
diff --git a/lib/btrfs/btrfsitem/item_inoderef.go b/lib/btrfs/btrfsitem/item_inoderef.go
index b1eaf1b..083f19e 100644
--- a/lib/btrfs/btrfsitem/item_inoderef.go
+++ b/lib/btrfs/btrfsitem/item_inoderef.go
@@ -12,8 +12,40 @@ import (
)
// key.objectid = inode number of the file
-// key.offset = inode number of the parent file
-type InodeRef struct { // INODE_REF=12
+// key.offset = inode number of the parent directory
+//
+// Might have multiple entries if the same file has multiple hardlinks
+// in the same directory.
+type InodeRefs []InodeRef // INODE_REF=12
+
+func (o *InodeRefs) UnmarshalBinary(dat []byte) (int, error) {
+ *o = nil
+ n := 0
+ for n < len(dat) {
+ var ref InodeRef
+ _n, err := binstruct.Unmarshal(dat[n:], &ref)
+ n += _n
+ if err != nil {
+ return n, err
+ }
+ *o = append(*o, ref)
+ }
+ return n, nil
+}
+
+func (o InodeRefs) MarshalBinary() ([]byte, error) {
+ var dat []byte
+ for _, ref := range o {
+ _dat, err := binstruct.Marshal(ref)
+ dat = append(dat, _dat...)
+ if err != nil {
+ return dat, err
+ }
+ }
+ return dat, nil
+}
+
+type InodeRef struct {
Index int64 `bin:"off=0x0, siz=0x8"`
NameLen uint16 `bin:"off=0x8, siz=0x2"` // [ignored-when-writing]
binstruct.End `bin:"off=0xa"`
diff --git a/lib/btrfs/btrfsitem/items_gen.go b/lib/btrfs/btrfsitem/items_gen.go
index 8573967..d21cd3e 100644
--- a/lib/btrfs/btrfsitem/items_gen.go
+++ b/lib/btrfs/btrfsitem/items_gen.go
@@ -57,7 +57,7 @@ var keytype2gotype = map[Type]reflect.Type{
FREE_SPACE_EXTENT_KEY: reflect.TypeOf(Empty{}),
FREE_SPACE_INFO_KEY: reflect.TypeOf(FreeSpaceInfo{}),
INODE_ITEM_KEY: reflect.TypeOf(Inode{}),
- INODE_REF_KEY: reflect.TypeOf(InodeRef{}),
+ INODE_REF_KEY: reflect.TypeOf(InodeRefs{}),
METADATA_ITEM_KEY: reflect.TypeOf(Metadata{}),
ORPHAN_ITEM_KEY: reflect.TypeOf(Empty{}),
PERSISTENT_ITEM_KEY: reflect.TypeOf(DevStats{}),
@@ -91,7 +91,7 @@ func (FreeSpaceBitmap) isItem() {}
func (FreeSpaceHeader) isItem() {}
func (FreeSpaceInfo) isItem() {}
func (Inode) isItem() {}
-func (InodeRef) isItem() {}
+func (InodeRefs) isItem() {}
func (Metadata) isItem() {}
func (Root) isItem() {}
func (RootRef) isItem() {}
diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go
index ad489c7..0ef922b 100644
--- a/lib/btrfs/io4_fs.go
+++ b/lib/btrfs/io4_fs.go
@@ -191,9 +191,15 @@ func (ret *Dir) populate() {
for _, item := range ret.OtherItems {
switch item.Key.ItemType {
case btrfsitem.INODE_REF_KEY:
+ body := item.Body.(btrfsitem.InodeRefs)
+ if len(body) != 1 {
+ ret.Errs = append(ret.Errs, fmt.Errorf("INODE_REF item with %d entries on a directory",
+ len(body)))
+ continue
+ }
ref := InodeRef{
Inode: ObjID(item.Key.Offset),
- InodeRef: item.Body.(btrfsitem.InodeRef),
+ InodeRef: body[0],
}
if ret.DotDot != nil {
if !reflect.DeepEqual(ref, *ret.DotDot) {
@@ -336,13 +342,13 @@ func (ret *File) populate() {
}
pos += size
}
- if ret.InodeItem != nil && pos != ret.InodeItem.Size {
- if ret.InodeItem.Size > pos {
+ if ret.InodeItem != nil && pos != ret.InodeItem.NumBytes {
+ if ret.InodeItem.NumBytes > pos {
ret.Errs = append(ret.Errs, fmt.Errorf("extent gap from %v to %v",
- pos, ret.InodeItem.Size))
+ pos, ret.InodeItem.NumBytes))
} else {
ret.Errs = append(ret.Errs, fmt.Errorf("extent mapped past end of file from %v to %v",
- ret.InodeItem.Size, pos))
+ ret.InodeItem.NumBytes, pos))
}
}
}
diff --git a/lib/btrfsprogs/btrfsinspect/print_tree.go b/lib/btrfsprogs/btrfsinspect/print_tree.go
index ff53953..d7eb6f3 100644
--- a/lib/btrfsprogs/btrfsinspect/print_tree.go
+++ b/lib/btrfsprogs/btrfsinspect/print_tree.go
@@ -135,9 +135,11 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfs.Ob
fmt.Fprintf(out, "\t\tctime %v\n", fmtTime(body.CTime))
fmt.Fprintf(out, "\t\tmtime %v\n", fmtTime(body.MTime))
fmt.Fprintf(out, "\t\totime %v\n", fmtTime(body.OTime))
- case btrfsitem.InodeRef:
- fmt.Fprintf(out, "\t\tindex %v namelen %v name: %s\n",
- body.Index, body.NameLen, body.Name)
+ case btrfsitem.InodeRefs:
+ for _, ref := range body {
+ fmt.Fprintf(out, "\t\tindex %v namelen %v name: %s\n",
+ ref.Index, ref.NameLen, ref.Name)
+ }
//case btrfsitem.INODE_EXTREF_KEY:
// // TODO
case btrfsitem.DirEntry:
diff --git a/scripts/main.sh b/scripts/main.sh
index a9afde3..dbcf3ec 100755
--- a/scripts/main.sh
+++ b/scripts/main.sh
@@ -9,3 +9,5 @@ if ! test -s ../scratch/dump.rebuilt-mappings.json; then
> ../scratch/dump.rebuilt-mappings.json \
2> >(tee >&2 ../scratch/dump.rebuilt-mappings.log)
fi
+time ./btrfs-rec --pv=../scratch/dump-zero.img --mappings=../scratch/dump.rebuilt-mappings.json inspect ls-files \
+ &> ../scratch/dump.ls-files.txt