summaryrefslogtreecommitdiff
path: root/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go
blob: 466082ce705eaaeabe88efa60f5bb69c3afedcbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (C) 2022-2023  Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later

package rebuildtrees

import (
	"context"
	"fmt"

	"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
	"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
	"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
	"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
	"git.lukeshu.com/btrfs-progs-ng/lib/btrfsutil"
)

type forrestCallbacks struct {
	*rebuilder
}

var _ btrfsutil.RebuiltForrestExtendedCallbacks = forrestCallbacks{}

// AddedItem implements btrfsutil.RebuiltForrestExtendedCallbacks.
func (o forrestCallbacks) AddedItem(_ context.Context, tree btrfsprim.ObjID, key btrfsprim.Key) {
	o.addedItemQueue.Insert(keyAndTree{
		TreeID: tree,
		Key:    key,
	})
}

// AddedRoot implements btrfsutil.RebuiltForrestCallbacks.
func (o forrestCallbacks) AddedRoot(_ context.Context, tree btrfsprim.ObjID, _ btrfsvol.LogicalAddr) {
	if retries := o.retryItemQueue[tree]; retries != nil {
		o.addedItemQueue.InsertFrom(retries)
	}
}

// LookupRoot implements btrfsutil.RebuiltForrestCallbacks.
func (o forrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, _item btrfsitem.Root, err error) {
	wantKey := wantWithTree{
		TreeID: btrfsprim.ROOT_TREE_OBJECTID,
		Key: want{
			ObjectID:   tree,
			ItemType:   btrfsitem.ROOT_ITEM_KEY,
			OffsetType: offsetAny,
		},
	}
	ctx = withWant(ctx, logFieldTreeWant, "tree Root", wantKey)
	foundKey, ok := o._want(ctx, wantKey)
	if !ok {
		o.enqueueRetry(btrfsprim.ROOT_TREE_OBJECTID)
		return 0, btrfsitem.Root{}, btrfstree.ErrNoItem
	}
	item, _ := discardErr(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID)).TreeLookup(ctx, foundKey)
	defer item.Body.Free()
	switch itemBody := item.Body.(type) {
	case *btrfsitem.Root:
		return btrfsprim.Generation(foundKey.Offset), *itemBody, nil
	case *btrfsitem.Error:
		graphCallbacks(o).FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", foundKey, itemBody.Err))
		return 0, btrfsitem.Root{}, itemBody.Err
	default:
		// This is a panic because the item decoder should not emit ROOT_ITEM items as anything but
		// btrfsitem.Root or btrfsitem.Error without this code also being updated.
		panic(fmt.Errorf("should not happen: ROOT_ITEM item has unexpected type: %T", itemBody))
	}
}

// LookupUUID implements btrfsutil.RebuiltForrestCallbacks.
func (o forrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) {
	wantKey := wantWithTree{
		TreeID: btrfsprim.UUID_TREE_OBJECTID,
		Key:    wantFromKey(btrfsitem.UUIDToKey(uuid)),
	}
	ctx = withWant(ctx, logFieldTreeWant, "resolve parent UUID", wantKey)
	if !o._wantOff(ctx, wantKey) {
		if id, ok := o.slowLookupUUID(ctx, uuid); ok {
			return id, nil
		}
		return 0, btrfstree.ErrNoItem
	}
	item, _ := discardErr(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID)).TreeLookup(ctx, wantKey.Key.Key())
	defer item.Body.Free()
	switch itemBody := item.Body.(type) {
	case *btrfsitem.UUIDMap:
		return itemBody.ObjID, nil
	case *btrfsitem.Error:
		graphCallbacks(o).FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", wantKey, itemBody.Err))
		if id, ok := o.slowLookupUUID(ctx, uuid); ok {
			return id, nil
		}
		return 0, itemBody.Err
	default:
		// This is a panic because the item decoder should not emit UUID_SUBVOL items as anything but
		// btrfsitem.UUIDMap or btrfsitem.Error without this code also being updated.
		panic(fmt.Errorf("should not happen: UUID_SUBVOL item has unexpected type: %T", itemBody))
	}
}

func (o forrestCallbacks) slowLookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) {
	rootTree, err := o.rebuilt.RebuiltTree(ctx, btrfsprim.ROOT_TREE_OBJECTID)
	if err != nil {
		o.enqueueRetry(btrfsprim.ROOT_TREE_OBJECTID)
		return 0, false
	}
	var ret btrfsprim.ObjID
	_ = rootTree.TreeRange(ctx, func(item btrfstree.Item) bool {
		if item.Key.ItemType != btrfsprim.ROOT_ITEM_KEY {
			return true
		}
		switch itemBody := item.Body.(type) {
		case *btrfsitem.Root:
			if itemBody.UUID == uuid {
				ret = item.Key.ObjectID
				return false
			}
		case *btrfsitem.Error:
			graphCallbacks(o).FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", item.Key, itemBody.Err))
		default:
			// This is a panic because the item decoder should not emit ROOT_ITEM items as anything but
			// btrfsitem.Root or btrfsitem.Error without this code also being updated.
			panic(fmt.Errorf("should not happen: ROOT_ITEM item has unexpected type: %T", itemBody))
		}
		return true
	})
	if ret == 0 {
		o.enqueueRetry(btrfsprim.ROOT_TREE_OBJECTID)
		return 0, false
	}
	return ret, true
}