From 1ba83231195ea3b78ce545f4518f70c74819345b Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 31 Mar 2023 10:28:02 -0600 Subject: btrfsutil: Add a ReadGraph function, rebuildtrees: Clean up scan to match --- lib/btrfsutil/graph.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'lib/btrfsutil/graph.go') diff --git a/lib/btrfsutil/graph.go b/lib/btrfsutil/graph.go index fe7fe70..7863e0d 100644 --- a/lib/btrfsutil/graph.go +++ b/lib/btrfsutil/graph.go @@ -12,6 +12,7 @@ import ( "github.com/datawire/dlib/dlog" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" "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" @@ -283,3 +284,57 @@ func (g Graph) FinalCheck(ctx context.Context, fs btrfstree.NodeSource) error { return nil } + +func ReadGraph(_ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr) (Graph, error) { + // read-superblock ///////////////////////////////////////////////////////////// + ctx := dlog.WithField(_ctx, "btrfs.util.read-graph.step", "read-superblock") + dlog.Info(ctx, "Reading superblock...") + sb, err := fs.Superblock() + if err != nil { + return Graph{}, err + } + + // read-roots ////////////////////////////////////////////////////////////////// + ctx = dlog.WithField(_ctx, "btrfs.util.read-graph.step", "read-roots") + graph := NewGraph(ctx, *sb) + + // read-nodes ////////////////////////////////////////////////////////////////// + ctx = dlog.WithField(_ctx, "btrfs.util.read-graph.step", "read-nodes") + dlog.Infof(ctx, "Reading node data from FS...") + var stats textui.Portion[int] + stats.D = len(nodeList) + progressWriter := textui.NewProgress[textui.Portion[int]]( + ctx, + dlog.LogLevelInfo, + textui.Tunable(1*time.Second)) + progressWriter.Set(stats) + for _, laddr := range nodeList { + if err := ctx.Err(); err != nil { + return Graph{}, err + } + node, err := fs.AcquireNode(ctx, laddr, btrfstree.NodeExpectations{ + LAddr: containers.OptionalValue(laddr), + }) + if err != nil { + fs.ReleaseNode(node) + return Graph{}, err + } + graph.InsertNode(node) + fs.ReleaseNode(node) + stats.N++ + progressWriter.Set(stats) + } + if stats.N != stats.D { + panic("should not happen") + } + progressWriter.Done() + dlog.Info(ctx, "... done reading node data") + + // check /////////////////////////////////////////////////////////////////////// + ctx = dlog.WithField(_ctx, "btrfs.util.read-graph.step", "check") + if err := graph.FinalCheck(ctx, fs); err != nil { + return Graph{}, err + } + + return graph, nil +} -- cgit v1.2.3-2-g168b From c1578391cc2089cd224fd8325c333038e0ba7b7b Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 31 Mar 2023 18:01:47 -0600 Subject: maps: Add HasKey and HaveAnyKeysInCommon functions, use them --- lib/btrfsutil/graph.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/btrfsutil/graph.go') diff --git a/lib/btrfsutil/graph.go b/lib/btrfsutil/graph.go index 7863e0d..db036d8 100644 --- a/lib/btrfsutil/graph.go +++ b/lib/btrfsutil/graph.go @@ -226,7 +226,7 @@ func (g Graph) FinalCheck(ctx context.Context, fs btrfstree.NodeSource) error { stats.D = len(g.EdgesTo) progressWriter.Set(stats) for laddr := range g.EdgesTo { - if _, ok := g.Nodes[laddr]; !ok { + if !maps.HasKey(g.Nodes, laddr) { node, err := fs.AcquireNode(ctx, laddr, btrfstree.NodeExpectations{ LAddr: containers.OptionalValue(laddr), }) -- cgit v1.2.3-2-g168b From 3e35c0eb69b0fdae195d8fa372a026977acba737 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 2 Apr 2023 18:57:43 -0600 Subject: btrfsutil: GraphNode: Add a CheckExpectations method --- lib/btrfsutil/graph.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'lib/btrfsutil/graph.go') diff --git a/lib/btrfsutil/graph.go b/lib/btrfsutil/graph.go index db036d8..8e26c08 100644 --- a/lib/btrfsutil/graph.go +++ b/lib/btrfsutil/graph.go @@ -10,6 +10,7 @@ import ( "reflect" "time" + "github.com/datawire/dlib/derror" "github.com/datawire/dlib/dlog" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" @@ -72,6 +73,47 @@ func (n GraphNode) String() string { n.Level, n.Generation, n.Owner, len(n.Items)) } +func (n GraphNode) CheckExpectations(g Graph, exp btrfstree.NodeExpectations) error { + var errs derror.MultiError + if exp.LAddr.OK && n.Addr != exp.LAddr.Val { + errs = append(errs, fmt.Errorf("read from laddr=%v but claims to be at laddr=%v", + exp.LAddr.Val, n.Addr)) + } + if exp.Level.OK && n.Level != exp.Level.Val { + errs = append(errs, fmt.Errorf("expected level=%v but claims to be level=%v", + exp.Level.Val, n.Level)) + } + if n.Level > btrfstree.MaxLevel { + errs = append(errs, fmt.Errorf("maximum level=%v but claims to be level=%v", + btrfstree.MaxLevel, n.Level)) + } + if exp.Generation.OK && n.Generation != exp.Generation.Val { + errs = append(errs, fmt.Errorf("expected generation=%v but claims to be generation=%v", + exp.Generation.Val, n.Generation)) + } + if exp.Owner != nil { + if err := exp.Owner(n.Owner, n.Generation); err != nil { + errs = append(errs, err) + } + } + if n.NumItems(g) == 0 { + errs = append(errs, fmt.Errorf("has no items")) + } else { + if minItem := n.MinItem(g); exp.MinItem.OK && exp.MinItem.Val.Compare(minItem) > 0 { + errs = append(errs, fmt.Errorf("expected minItem>=%v but node has minItem=%v", + exp.MinItem.Val, minItem)) + } + if maxItem := n.MaxItem(g); exp.MaxItem.OK && exp.MaxItem.Val.Compare(maxItem) < 0 { + errs = append(errs, fmt.Errorf("expected maxItem<=%v but node has maxItem=%v", + exp.MaxItem.Val, maxItem)) + } + } + if len(errs) > 0 { + return errs + } + return nil +} + type GraphEdge struct { // It is invalid for both 'FromRoot' and 'FromNode' to be // non-zero. If both are zero, then the GraphEdge is from the -- cgit v1.2.3-2-g168b