diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-06-01 10:22:09 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-06-01 10:45:36 -0600 |
commit | 774b5baa5aa5772b29865e8a570025d94ec34d2a (patch) | |
tree | 0ce0704bd1a39b3b7b0b4a000fe9f9206907d3ac /pkg/btrfs | |
parent | 8dde45b09728fb753b072c73ff624dde832c073b (diff) |
better error tolerance
Diffstat (limited to 'pkg/btrfs')
-rw-r--r-- | pkg/btrfs/btrfsitem/item_empty.go | 2 | ||||
-rw-r--r-- | pkg/btrfs/btrfsitem/items.go | 39 | ||||
-rw-r--r-- | pkg/btrfs/io2_fs.go | 18 | ||||
-rw-r--r-- | pkg/btrfs/types_btree.go | 53 |
4 files changed, 82 insertions, 30 deletions
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 |