summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-07-02 20:09:16 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-07-02 20:11:00 -0600
commite343bcf49febbf142aefb065fe1fa6b2ea17a247 (patch)
treed79eba185754a91a8093b12ffc3580bbe495c38a
parentc8e863f886c8cf2ee60d28254fa94a6d4f6a566d (diff)
more work on ls-files
-rw-r--r--cmd/btrfs-ls-files/main.go110
-rw-r--r--pkg/btrfs/btrfsitem/item_dir.go21
-rw-r--r--pkg/btrfs/btrfsitem/items.txt6
-rw-r--r--pkg/btrfs/btrfsitem/items_gen.go8
-rw-r--r--pkg/btrfsmisc/print_tree.go2
5 files changed, 118 insertions, 29 deletions
diff --git a/cmd/btrfs-ls-files/main.go b/cmd/btrfs-ls-files/main.go
index 4f9f520..d6b8c97 100644
--- a/cmd/btrfs-ls-files/main.go
+++ b/cmd/btrfs-ls-files/main.go
@@ -3,6 +3,10 @@ package main
import (
"fmt"
"os"
+ "reflect"
+ "strings"
+
+ "github.com/datawire/dlib/derror"
"lukeshu.com/btrfs-tools/pkg/btrfs"
"lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem"
@@ -49,36 +53,116 @@ func Main(imgfilenames ...string) (err error) {
fsTreeRootBody := fsTreeRoot.Body.(btrfsitem.Root)
fsTree := fsTreeRootBody.ByteNr
- return printDir(fs, fsTree, "", "/", fsTreeRootBody.RootDirID, fsTreeRootBody.Inode)
+ printDir(fs, fsTree, "", "", "/", fsTreeRootBody.RootDirID)
+ return nil
}
-func printDir(fs *btrfs.FS, fsTree btrfsvol.LogicalAddr, prefix, dirName string, dirInodeNum btrfs.ObjID, dirInode btrfsitem.Inode) error {
- fmt.Printf("%s[%s\tino=%d\tuid=%d\tgid=%d\tsize=%d] %s\n",
- prefix,
- dirInode.Mode, dirInodeNum, dirInode.UID, dirInode.GID, dirInode.Size,
- dirName)
+const (
+ tS = "    "
+ tl = "│   "
+ tT = "├── "
+ tL = "└── "
+)
+
+func printDir(fs *btrfs.FS, fsTree btrfsvol.LogicalAddr, prefix0, prefix1, dirName string, dirInode btrfs.ObjID) {
+ var errs derror.MultiError
items, err := fs.TreeSearchAll(fsTree, func(key btrfs.Key) int {
- return util.CmpUint(dirInodeNum, key.ObjectID)
+ return util.CmpUint(dirInode, key.ObjectID)
})
if err != nil {
- return fmt.Errorf("read directory %q: %w", dirName, err)
+ 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:
- // TODO
+ if dirInodeDatOK {
+ 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:
- // skip?
+ body := item.Body.(btrfsitem.DirEntries)
+ if len(body) != 1 {
+ errs = append(errs, fmt.Errorf("read dir: multiple direntries in single dir_item?"))
+ continue
+ }
+ for _, entry := range body {
+ 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 {
+ 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:
- for _, entry := range item.Body.(btrfsitem.DirList) {
- fmt.Println(string(entry.Name))
+ for i, entry := range item.Body.(btrfsitem.DirEntries) {
+ index := item.Head.Key.Offset + uint64(i)
+ if _, exists := membersByIndex[index]; exists {
+ errs = append(errs, fmt.Errorf("read dir: multiple instances of direntry index %v", index))
+ continue
+ }
+ membersByIndex[index] = entry
}
case btrfsitem.XATTR_ITEM_KEY:
default:
panic(fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType))
}
}
- return nil
+ fmt.Printf("%s%q\t[ino=%d\t",
+ prefix0, dirName, dirInode)
+ if dirInodeDatOK {
+ fmt.Printf("uid=%d\tgid=%d\tsize=%d]\n",
+ dirInodeDat.UID, dirInodeDat.GID, dirInodeDat.Size)
+ } else {
+ fmt.Printf("error=read dir: no inode data\n")
+ }
+ 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))
+ }
+ prefix := tT
+ if (i == len(membersByIndex)-1) && (len(membersByNameHash) == 0) && (len(errs) == 0) {
+ prefix = tL
+ }
+ printItem(fs, fsTree, prefix1+prefix, prefix1+tS, string(entry.Name), entry.Location)
+ }
+ 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))
+ printItem(fs, fsTree, prefix1+tT, prefix1+tS, string(entry.Name), entry.Location)
+ }
+ for i, err := range errs {
+ prefix := tT
+ if i == len(errs)-1 {
+ prefix = tL
+ }
+ fmt.Printf("%s%s%s\n", prefix1, prefix, strings.ReplaceAll(err.Error(), "\n", prefix1+tS+"\n"))
+ }
+}
+
+func printItem(fs *btrfs.FS, fsTree btrfsvol.LogicalAddr, prefix0, prefix1, name string, location btrfs.Key) {
+ fmt.Printf("%s%q\t[location=%v]\n", prefix0, name, location)
}
diff --git a/pkg/btrfs/btrfsitem/item_dir.go b/pkg/btrfs/btrfsitem/item_dir.go
index 253d257..14541bd 100644
--- a/pkg/btrfs/btrfsitem/item_dir.go
+++ b/pkg/btrfs/btrfsitem/item_dir.go
@@ -2,6 +2,7 @@ package btrfsitem
import (
"fmt"
+ "hash/crc32"
"lukeshu.com/btrfs-tools/pkg/binstruct"
"lukeshu.com/btrfs-tools/pkg/btrfs/internal"
@@ -9,15 +10,19 @@ import (
// key.objectid = inode of directory containing this entry
// key.offset =
-// for DIR_ITEM and XATTR_ITEM = crc32c(name)
+// for DIR_ITEM and XATTR_ITEM = NameHash(name)
// for DIR_INDEX = index id in the directory (starting at 2, because "." and "..")
-type DirList []Dir // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24
+type DirEntries []DirEntry // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24
-func (o *DirList) UnmarshalBinary(dat []byte) (int, error) {
+func NameHash(dat []byte) uint64 {
+ return uint64(^crc32.Update(1, crc32.MakeTable(crc32.Castagnoli), dat))
+}
+
+func (o *DirEntries) UnmarshalBinary(dat []byte) (int, error) {
*o = nil
n := 0
for n < len(dat) {
- var ref Dir
+ var ref DirEntry
_n, err := binstruct.Unmarshal(dat, &ref)
n += _n
if err != nil {
@@ -28,7 +33,7 @@ func (o *DirList) UnmarshalBinary(dat []byte) (int, error) {
return n, nil
}
-func (o DirList) MarshalBinary() ([]byte, error) {
+func (o DirEntries) MarshalBinary() ([]byte, error) {
var ret []byte
for _, ref := range o {
bs, err := binstruct.Marshal(ref)
@@ -40,7 +45,7 @@ func (o DirList) MarshalBinary() ([]byte, error) {
return ret, nil
}
-type Dir struct {
+type DirEntry struct {
Location internal.Key `bin:"off=0x0, siz=0x11"`
TransID int64 `bin:"off=0x11, siz=8"`
DataLen uint16 `bin:"off=0x19, siz=2"` // [ignored-when-writing]
@@ -51,7 +56,7 @@ type Dir struct {
Name []byte `bin:"-"`
}
-func (o *Dir) UnmarshalBinary(dat []byte) (int, error) {
+func (o *DirEntry) UnmarshalBinary(dat []byte) (int, error) {
n, err := binstruct.UnmarshalWithoutInterface(dat, o)
if err != nil {
return n, err
@@ -63,7 +68,7 @@ func (o *Dir) UnmarshalBinary(dat []byte) (int, error) {
return n, nil
}
-func (o Dir) MarshalBinary() ([]byte, error) {
+func (o DirEntry) MarshalBinary() ([]byte, error) {
o.DataLen = uint16(len(o.Data))
o.NameLen = uint16(len(o.Name))
dat, err := binstruct.MarshalWithoutInterface(o)
diff --git a/pkg/btrfs/btrfsitem/items.txt b/pkg/btrfs/btrfsitem/items.txt
index dbcd260..a5f247f 100644
--- a/pkg/btrfs/btrfsitem/items.txt
+++ b/pkg/btrfs/btrfsitem/items.txt
@@ -2,8 +2,8 @@ BLOCK_GROUP_ITEM=192 BlockGroup
CHUNK_ITEM=228 Chunk
DEV_EXTENT=204 DevExtent
DEV_ITEM=216 Dev
-DIR_INDEX=96 DirList
-DIR_ITEM=84 DirList
+DIR_INDEX=96 DirEntries
+DIR_ITEM=84 DirEntries
EXTENT_CSUM=128 ExtentCSum
EXTENT_DATA=108 FileExtent
EXTENT_DATA_REF=178 ExtentDataRef
@@ -24,4 +24,4 @@ TREE_BLOCK_REF=176 Empty
UNTYPED=0:FREE_SPACE_OBJECTID FreeSpaceHeader
UUID_RECEIVED_SUBVOL=252 UUIDMap
UUID_SUBVOL=251 UUIDMap
-XATTR_ITEM=24 DirList
+XATTR_ITEM=24 DirEntries
diff --git a/pkg/btrfs/btrfsitem/items_gen.go b/pkg/btrfs/btrfsitem/items_gen.go
index f0a4274..92e2523 100644
--- a/pkg/btrfs/btrfsitem/items_gen.go
+++ b/pkg/btrfs/btrfsitem/items_gen.go
@@ -43,8 +43,8 @@ var keytype2gotype = map[Type]reflect.Type{
CHUNK_ITEM_KEY: reflect.TypeOf(Chunk{}),
DEV_EXTENT_KEY: reflect.TypeOf(DevExtent{}),
DEV_ITEM_KEY: reflect.TypeOf(Dev{}),
- DIR_INDEX_KEY: reflect.TypeOf(DirList{}),
- DIR_ITEM_KEY: reflect.TypeOf(DirList{}),
+ DIR_INDEX_KEY: reflect.TypeOf(DirEntries{}),
+ DIR_ITEM_KEY: reflect.TypeOf(DirEntries{}),
EXTENT_CSUM_KEY: reflect.TypeOf(ExtentCSum{}),
EXTENT_DATA_KEY: reflect.TypeOf(FileExtent{}),
EXTENT_DATA_REF_KEY: reflect.TypeOf(ExtentDataRef{}),
@@ -64,7 +64,7 @@ var keytype2gotype = map[Type]reflect.Type{
TREE_BLOCK_REF_KEY: reflect.TypeOf(Empty{}),
UUID_RECEIVED_SUBVOL_KEY: reflect.TypeOf(UUIDMap{}),
UUID_SUBVOL_KEY: reflect.TypeOf(UUIDMap{}),
- XATTR_ITEM_KEY: reflect.TypeOf(DirList{}),
+ XATTR_ITEM_KEY: reflect.TypeOf(DirEntries{}),
}
var untypedObjID2gotype = map[internal.ObjID]reflect.Type{
internal.FREE_SPACE_OBJECTID: reflect.TypeOf(FreeSpaceHeader{}),
@@ -75,7 +75,7 @@ func (Chunk) isItem() {}
func (Dev) isItem() {}
func (DevExtent) isItem() {}
func (DevStats) isItem() {}
-func (DirList) isItem() {}
+func (DirEntries) isItem() {}
func (Empty) isItem() {}
func (Extent) isItem() {}
func (ExtentCSum) isItem() {}
diff --git a/pkg/btrfsmisc/print_tree.go b/pkg/btrfsmisc/print_tree.go
index 3a13504..76eb19c 100644
--- a/pkg/btrfsmisc/print_tree.go
+++ b/pkg/btrfsmisc/print_tree.go
@@ -64,7 +64,7 @@ func PrintTree(fs *btrfs.FS, root btrfsvol.LogicalAddr) error {
}
//case btrfsitem.INODE_EXTREF_KEY:
// // TODO
- case btrfsitem.DirList:
+ case btrfsitem.DirEntries:
for _, dir := range body {
fmt.Printf("\t\tlocation %v type %v\n",
FmtKey(dir.Location), dir.Type)