From 56e44b0630448d44f7aa7f85b2098007ddbae06f Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 15 Mar 2023 15:17:11 -0600 Subject: btrfstree: Distinguish between tree-not-found and item-not-found --- lib/btrfs/btrfstree/btree.go | 5 ++- lib/btrfs/btrfstree/btree_err.go | 32 ++++++++++++++++++ lib/btrfs/btrfstree/btree_err_test.go | 64 +++++++++++++++++++++++++++++++++++ lib/btrfs/btrfstree/btree_forrest.go | 4 +++ lib/btrfs/btrfstree/btree_tree.go | 8 ++--- 5 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 lib/btrfs/btrfstree/btree_err.go create mode 100644 lib/btrfs/btrfstree/btree_err_test.go (limited to 'lib/btrfs/btrfstree') diff --git a/lib/btrfs/btrfstree/btree.go b/lib/btrfs/btrfstree/btree.go index 2fd7c39..7b3721b 100644 --- a/lib/btrfs/btrfstree/btree.go +++ b/lib/btrfs/btrfstree/btree.go @@ -45,7 +45,10 @@ type TreeOperator interface { // If some items are able to be read, but there is an error reading the // full set, then it might return *both* a list of items and an error. // - // If no such item is found, an error that is io/fs.ErrNotExist is + // If the tree is not found, an error that is ErrNoTree is + // returned. + // + // If no such item is found, an error that is ErrNoItem is // returned. TreeSearchAll(treeID btrfsprim.ObjID, fn func(key btrfsprim.Key, size uint32) int) ([]Item, error) // size is math.MaxUint32 for key-pointers } diff --git a/lib/btrfs/btrfstree/btree_err.go b/lib/btrfs/btrfstree/btree_err.go new file mode 100644 index 0000000..57fb283 --- /dev/null +++ b/lib/btrfs/btrfstree/btree_err.go @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfstree + +import ( + iofs "io/fs" +) + +// For both ErrNoItem and ErrNoTree, `errors.Is(err, +// io/fs.ErrNotExist)` returns true. +var ( + ErrNoItem = errNotExist("item") + ErrNoTree = errNotExist("tree") +) + +func errNotExist(thing string) error { + return ¬ExistError{thing} +} + +type notExistError struct { + thing string +} + +func (e *notExistError) Error() string { + return e.thing + " does not exist" +} + +func (*notExistError) Is(target error) bool { + return target == iofs.ErrNotExist +} diff --git a/lib/btrfs/btrfstree/btree_err_test.go b/lib/btrfs/btrfstree/btree_err_test.go new file mode 100644 index 0000000..381bd56 --- /dev/null +++ b/lib/btrfs/btrfstree/btree_err_test.go @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfstree_test + +import ( + "errors" + "fmt" + iofs "io/fs" + "testing" + + "github.com/stretchr/testify/assert" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree" +) + +func TestErrs(t *testing.T) { + t.Parallel() + + errItem := fmt.Errorf("my item: %w", btrfstree.ErrNoItem) + errTree := fmt.Errorf("my tree: %w", btrfstree.ErrNoTree) + + // 1. errItem + // 2. errTree + // 3. btrfstree.ErrNoItem + // 4. btrfstree.ErrNoTree + // 5. iofs.ErrNotExist + + // 1 + assert.Equal(t, errors.Is(errItem, errItem), true) + assert.Equal(t, errors.Is(errItem, errTree), false) + assert.Equal(t, errors.Is(errItem, btrfstree.ErrNoItem), true) + assert.Equal(t, errors.Is(errItem, btrfstree.ErrNoTree), false) + assert.Equal(t, errors.Is(errItem, iofs.ErrNotExist), true) + + // 2 + assert.Equal(t, errors.Is(errTree, errItem), false) + assert.Equal(t, errors.Is(errTree, errTree), true) + assert.Equal(t, errors.Is(errTree, btrfstree.ErrNoItem), false) + assert.Equal(t, errors.Is(errTree, btrfstree.ErrNoTree), true) + assert.Equal(t, errors.Is(errTree, iofs.ErrNotExist), true) + + // 3 + assert.Equal(t, errors.Is(btrfstree.ErrNoItem, errItem), false) + assert.Equal(t, errors.Is(btrfstree.ErrNoItem, errTree), false) + assert.Equal(t, errors.Is(btrfstree.ErrNoItem, btrfstree.ErrNoItem), true) + assert.Equal(t, errors.Is(btrfstree.ErrNoItem, btrfstree.ErrNoTree), false) + assert.Equal(t, errors.Is(btrfstree.ErrNoItem, iofs.ErrNotExist), true) + + // 4 + assert.Equal(t, errors.Is(btrfstree.ErrNoTree, errItem), false) + assert.Equal(t, errors.Is(btrfstree.ErrNoTree, errTree), false) + assert.Equal(t, errors.Is(btrfstree.ErrNoTree, btrfstree.ErrNoItem), false) + assert.Equal(t, errors.Is(btrfstree.ErrNoTree, btrfstree.ErrNoTree), true) + assert.Equal(t, errors.Is(btrfstree.ErrNoTree, iofs.ErrNotExist), true) + + // 5 + assert.Equal(t, errors.Is(iofs.ErrNotExist, errItem), false) + assert.Equal(t, errors.Is(iofs.ErrNotExist, errTree), false) + assert.Equal(t, errors.Is(iofs.ErrNotExist, btrfstree.ErrNoItem), false) + assert.Equal(t, errors.Is(iofs.ErrNotExist, btrfstree.ErrNoTree), false) + assert.Equal(t, errors.Is(iofs.ErrNotExist, iofs.ErrNotExist), true) +} diff --git a/lib/btrfs/btrfstree/btree_forrest.go b/lib/btrfs/btrfstree/btree_forrest.go index 0bab610..625fb30 100644 --- a/lib/btrfs/btrfstree/btree_forrest.go +++ b/lib/btrfs/btrfstree/btree_forrest.go @@ -5,6 +5,7 @@ package btrfstree import ( + "errors" "fmt" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem" @@ -69,6 +70,9 @@ func LookupTreeRoot(fs TreeOperator, sb Superblock, treeID btrfsprim.ObjID) (*Tr default: rootItem, err := fs.TreeSearch(btrfsprim.ROOT_TREE_OBJECTID, RootItemSearchFn(treeID)) if err != nil { + if errors.Is(err, ErrNoItem) { + err = ErrNoTree + } return nil, err } switch rootItemBody := rootItem.Body.(type) { diff --git a/lib/btrfs/btrfstree/btree_tree.go b/lib/btrfs/btrfstree/btree_tree.go index afaaf92..c51efa5 100644 --- a/lib/btrfs/btrfstree/btree_tree.go +++ b/lib/btrfs/btrfstree/btree_tree.go @@ -180,7 +180,7 @@ func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key, }} for { if path.Node(-1).ToNodeAddr == 0 { - return nil, nil, iofs.ErrNotExist + return nil, nil, ErrNoItem } node, err := fs.ReadNode(path) if err != nil { @@ -204,7 +204,7 @@ func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key, }) if !ok { FreeNodeRef(node) - return nil, nil, iofs.ErrNotExist + return nil, nil, ErrNoItem } toMaxKey := path.Node(-1).ToMaxKey if lastGood+1 < len(node.Data.BodyInterior) { @@ -228,7 +228,7 @@ func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key, // // + + + + 0 - - - - // - // Such an item might not exist; in this case, return nil/ErrNotExist. + // Such an item might not exist; in this case, return (nil, ErrNoItem). // Multiple such items might exist; in this case, it does not matter which // is returned. // @@ -238,7 +238,7 @@ func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key, }) if !ok { FreeNodeRef(node) - return nil, nil, iofs.ErrNotExist + return nil, nil, ErrNoItem } path = append(path, TreePathElem{ FromTree: node.Data.Head.Owner, -- cgit v1.2.3-2-g168b