summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-06-01 10:22:09 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-06-01 10:45:36 -0600
commit774b5baa5aa5772b29865e8a570025d94ec34d2a (patch)
tree0ce0704bd1a39b3b7b0b4a000fe9f9206907d3ac
parent8dde45b09728fb753b072c73ff624dde832c073b (diff)
better error tolerance
-rw-r--r--cmd/btrfs-dump-tree/main.go19
-rw-r--r--pkg/btrfs/btrfsitem/item_empty.go2
-rw-r--r--pkg/btrfs/btrfsitem/items.go39
-rw-r--r--pkg/btrfs/io2_fs.go18
-rw-r--r--pkg/btrfs/types_btree.go53
5 files changed, 95 insertions, 36 deletions
diff --git a/cmd/btrfs-dump-tree/main.go b/cmd/btrfs-dump-tree/main.go
index a1c7a03..b51e999 100644
--- a/cmd/btrfs-dump-tree/main.go
+++ b/cmd/btrfs-dump-tree/main.go
@@ -75,11 +75,13 @@ func Main(imgfilename string) (err error) {
return err
}
}
- if err := fs.WalkTree(superblock.Data.RootTree, func(key btrfs.Key, body btrfsitem.Item) error {
- if key.ItemType != btrfsitem.ROOT_ITEM_KEY {
- return nil
- }
- return printTree(fs, body.(btrfsitem.Root).ByteNr)
+ if err := fs.WalkTree(superblock.Data.RootTree, btrfs.WalkTreeHandler{
+ Item: func(key btrfs.Key, body btrfsitem.Item) error {
+ if key.ItemType != btrfsitem.ROOT_ITEM_KEY {
+ return nil
+ }
+ return printTree(fs, body.(btrfsitem.Root).ByteNr)
+ },
}); err != nil {
return err
}
@@ -91,7 +93,8 @@ func Main(imgfilename string) (err error) {
func printTree(fs *btrfs.FS, root btrfs.LogicalAddr) error {
nodeRef, err := fs.ReadNode(root)
if err != nil {
- return err
+ fmt.Fprintf(os.Stderr, "error: %v\n", err)
+ return nil
}
node := nodeRef.Data
printHeaderInfo(node)
@@ -209,6 +212,10 @@ func printTree(fs *btrfs.FS, root btrfs.LogicalAddr) error {
// // TODO
//case btrfsitem.TEMPORARY_ITEM_KEY:
// // TODO
+ case btrfsitem.Error:
+ fmt.Printf("\t\t(error) error item: %v\n", body.Err)
+ default:
+ fmt.Printf("\t\t(error) unhandled item type: %T\n", body)
}
}
}
diff --git a/pkg/btrfs/btrfsitem/item_empty.go b/pkg/btrfs/btrfsitem/item_empty.go
index ed0f66f..fe7ae90 100644
--- a/pkg/btrfs/btrfsitem/item_empty.go
+++ b/pkg/btrfs/btrfsitem/item_empty.go
@@ -5,5 +5,5 @@ import (
)
type Empty struct { // UNTYPED=0, QGROUP_RELATION=246
- binstruct.End `bin:"off=48"`
+ binstruct.End `bin:"off=0"`
}
diff --git a/pkg/btrfs/btrfsitem/items.go b/pkg/btrfs/btrfsitem/items.go
index 91b7029..3c4392e 100644
--- a/pkg/btrfs/btrfsitem/items.go
+++ b/pkg/btrfs/btrfsitem/items.go
@@ -14,19 +14,46 @@ type Item interface {
isItem()
}
-func UnmarshalItem(keytyp Type, dat []byte) (Item, error) {
+type Error struct {
+ Dat []byte
+ Err error
+}
+
+func (Error) isItem() {}
+
+func (o Error) MarshalBinary() ([]byte, error) {
+ return o.Dat, nil
+}
+
+func (o *Error) UnmarshalBinary(dat []byte) (int, error) {
+ o.Dat = dat
+ return len(dat), nil
+}
+
+// Rather than returning a separate error value, return an Error item.
+func UnmarshalItem(keytyp Type, dat []byte) Item {
gotyp, ok := keytype2gotype[keytyp]
if !ok {
- return nil, fmt.Errorf("btrfsitem.UnmarshalItem: unknown item type: %v", keytyp)
+ return Error{
+ Dat: dat,
+ Err: fmt.Errorf("btrfsitem.UnmarshalItem(typ=%v, dat): unknown item type", keytyp),
+ }
}
retPtr := reflect.New(gotyp)
n, err := binstruct.Unmarshal(dat, retPtr.Interface())
if err != nil {
- return nil, fmt.Errorf("btrfsitem.UnmarshalItem: %w", err)
+ return Error{
+ Dat: dat,
+ Err: fmt.Errorf("btrfsitem.UnmarshalItem(typ=%v, dat): %w", keytyp, err),
+ }
+
}
if n < len(dat) {
- return nil, fmt.Errorf("btrfsitem.UnmarshalItem: left over data: got %d bytes but only consumed %d",
- len(dat), n)
+ return Error{
+ Dat: dat,
+ Err: fmt.Errorf("btrfsitem.UnmarshalItem(typ=%v, dat): left over data: got %d bytes but only consumed %d",
+ keytyp, len(dat), n),
+ }
}
- return retPtr.Elem().Interface().(Item), nil
+ return retPtr.Elem().Interface().(Item)
}
diff --git a/pkg/btrfs/io2_fs.go b/pkg/btrfs/io2_fs.go
index b7752c7..f8ebe93 100644
--- a/pkg/btrfs/io2_fs.go
+++ b/pkg/btrfs/io2_fs.go
@@ -122,15 +122,17 @@ func (fs *FS) Init() error {
for _, chunk := range syschunks {
fs.chunks = append(fs.chunks, chunk)
}
- if err := fs.WalkTree(sb.Data.ChunkTree, func(key Key, body btrfsitem.Item) error {
- if key.ItemType != btrfsitem.CHUNK_ITEM_KEY {
+ if err := fs.WalkTree(sb.Data.ChunkTree, WalkTreeHandler{
+ Item: func(key Key, body btrfsitem.Item) error {
+ if key.ItemType != btrfsitem.CHUNK_ITEM_KEY {
+ return nil
+ }
+ fs.chunks = append(fs.chunks, SysChunk{
+ Key: key,
+ Chunk: body.(btrfsitem.Chunk),
+ })
return nil
- }
- fs.chunks = append(fs.chunks, SysChunk{
- Key: key,
- Chunk: body.(btrfsitem.Chunk),
- })
- return nil
+ },
}); err != nil {
fs.initErr = err
return fs.initErr
diff --git a/pkg/btrfs/types_btree.go b/pkg/btrfs/types_btree.go
index cac5f9a..88b9eb2 100644
--- a/pkg/btrfs/types_btree.go
+++ b/pkg/btrfs/types_btree.go
@@ -138,10 +138,7 @@ func (node *Node) UnmarshalBinary(nodeBuf []byte) (int, error) {
}
dataBuf := nodeBuf[dataOff : dataOff+dataSize]
lastRead = max(lastRead, dataOff+dataSize)
- item.Body, err = btrfsitem.UnmarshalItem(item.Head.Key.ItemType, dataBuf)
- if err != nil {
- return max(n, lastRead), fmt.Errorf("(leaf): item %d: %w", i, err)
- }
+ item.Body = btrfsitem.UnmarshalItem(item.Head.Key.ItemType, dataBuf)
node.BodyLeaf = append(node.BodyLeaf, item)
}
@@ -171,7 +168,7 @@ func (fs *FS) ReadNode(addr LogicalAddr) (util.Ref[LogicalAddr, Node], error) {
sb, err := fs.Superblock()
if err != nil {
- return ret, err
+ return ret, fmt.Errorf("btrfs.FS.ReadNode: %w", err)
}
// read
@@ -185,24 +182,24 @@ func (fs *FS) ReadNode(addr LogicalAddr) (util.Ref[LogicalAddr, Node], error) {
node.Size = sb.Data.NodeSize
if _, err := node.UnmarshalBinary(nodeBuf); err != nil {
- return ret, fmt.Errorf("node@%d: %w", addr, err)
+ return ret, fmt.Errorf("btrfs.FS.ReadNode: node@%d: %w", addr, err)
}
// sanity checking
if !node.Head.MetadataUUID.Equal(sb.Data.EffectiveMetadataUUID()) {
- return ret, fmt.Errorf("node@%d: does not look like a node", addr)
+ return ret, fmt.Errorf("btrfs.FS.ReadNode: node@%d: does not look like a node", addr)
}
if node.Head.Addr != addr {
- return ret, fmt.Errorf("node@%d: read from laddr=%d but claims to be at laddr=%d",
+ return ret, fmt.Errorf("btrfs.FS.ReadNode: node@%d: read from laddr=%d but claims to be at laddr=%d",
addr, addr, node.Head.Addr)
}
stored := node.Head.Checksum
calced := CRC32c(nodeBuf[binstruct.StaticSize(CSum{}):])
if !calced.Equal(stored) {
- return ret, fmt.Errorf("node@%d: checksum mismatch: stored=%s calculated=%s",
+ return ret, fmt.Errorf("btrfs.FS.ReadNode: node@%d: checksum mismatch: stored=%s calculated=%s",
addr, stored, calced)
}
@@ -215,23 +212,49 @@ func (fs *FS) ReadNode(addr LogicalAddr) (util.Ref[LogicalAddr, Node], error) {
}, nil
}
-func (fs *FS) WalkTree(nodeAddr LogicalAddr, fn func(Key, btrfsitem.Item) error) error {
+type WalkTreeHandler struct {
+ // Callbacks for items on internal nodes
+ PreKeyPointer func(KeyPointer) error
+ PostKeyPointer func(KeyPointer) error
+ // Callbacks for items on leaf nodes
+ Item func(Key, btrfsitem.Item) error
+ // Error handler
+ NodeError func(error) error
+}
+
+func (fs *FS) WalkTree(nodeAddr LogicalAddr, cbs WalkTreeHandler) error {
if nodeAddr == 0 {
return nil
}
node, err := fs.ReadNode(nodeAddr)
if err != nil {
- return err
+ if cbs.NodeError != nil {
+ err = cbs.NodeError(err)
+ }
+ if err != nil {
+ return fmt.Errorf("btrfs.FS.WalkTree: %w", err)
+ }
}
for _, item := range node.Data.BodyInternal {
- // fn(item.Data.Key, TODO)
- if err := fs.WalkTree(item.BlockPtr, fn); err != nil {
+ if cbs.PreKeyPointer != nil {
+ if err := cbs.PreKeyPointer(item); err != nil {
+ return err
+ }
+ }
+ if err := fs.WalkTree(item.BlockPtr, cbs); err != nil {
return err
}
+ if cbs.PostKeyPointer != nil {
+ if err := cbs.PostKeyPointer(item); err != nil {
+ return err
+ }
+ }
}
for _, item := range node.Data.BodyLeaf {
- if err := fn(item.Head.Key, item.Body); err != nil {
- return err
+ if cbs.Item != nil {
+ if err := cbs.Item(item.Head.Key, item.Body); err != nil {
+ return fmt.Errorf("btrfs.FS.WalkTree: callback: %w", err)
+ }
}
}
return nil