diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-06-05 21:30:25 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-06-05 21:31:00 -0600 |
commit | 7b8cdb995ecce81e4603a31e3304f6a2b9401c4c (patch) | |
tree | 4b9c26b3e08db84051512ee6e6e57a8caa9cc8d9 | |
parent | 9c0f3db2bc6bb9fa63542194eda91fab304a6b86 (diff) |
more fsck
-rw-r--r-- | cmd/btrfs-fsck.go | 79 | ||||
-rw-r--r-- | cmd/btrfs-fsck/main.go | 170 | ||||
-rw-r--r-- | pkg/btrfs/btrfsitem/item_chunk.go | 6 | ||||
-rw-r--r-- | pkg/btrfs/io2_fs.go | 2 | ||||
-rw-r--r-- | pkg/btrfs/types_btree.go | 42 | ||||
-rw-r--r-- | pkg/btrfsmisc/fsck.go | 2 |
6 files changed, 217 insertions, 84 deletions
diff --git a/cmd/btrfs-fsck.go b/cmd/btrfs-fsck.go deleted file mode 100644 index 8f48430..0000000 --- a/cmd/btrfs-fsck.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "lukeshu.com/btrfs-tools/pkg/btrfs" - "lukeshu.com/btrfs-tools/pkg/btrfsmisc" - "lukeshu.com/btrfs-tools/pkg/util" -) - -func main() { - if err := Main(os.Args[1]); err != nil { - fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err) - os.Exit(1) - } -} - -func Main(imgfilename string) (err error) { - maybeSetErr := func(_err error) { - if _err != nil && err == nil { - err = _err - } - } - - fh, err := os.Open(imgfilename) - if err != nil { - return err - } - defer func() { - maybeSetErr(fh.Close()) - }() - fs := &btrfs.FS{ - Devices: []*btrfs.Device{ - { - File: fh, - }, - }, - } - - superblock, err := fs.Superblock() - if err != nil { - return fmt.Errorf("superblock: %w", err) - } - - if err := fs.Init(); err != nil { - fmt.Printf("init chunk tree: error: %v\n", err) - } - - foundNodes := make(map[btrfs.LogicalAddr]struct{}) - - if err := btrfsmisc.ScanForNodes(fs.Devices[0], superblock.Data, func(nodeRef *util.Ref[btrfs.PhysicalAddr, btrfs.Node], err error) { - if err != nil { - fmt.Println(err) - return - } - foundNodes[nodeRef.Data.Head.Addr] = struct{}{} - fmt.Printf("node@%d: physical_addr=0x%0X logical_addr=0x%0X generation=%d owner=%v level=%d\n", - nodeRef.Addr, - nodeRef.Addr, nodeRef.Data.Head.Addr, - nodeRef.Data.Head.Generation, nodeRef.Data.Head.Owner, nodeRef.Data.Head.Level) - srcPaddr := btrfs.QualifiedPhysicalAddr{ - Dev: superblock.Data.DevItem.DevUUID, - Addr: nodeRef.Addr, - } - resPaddrs, _ := fs.Resolve(nodeRef.Data.Head.Addr) - if len(resPaddrs) == 0 { - fmt.Printf("node@%d: logical_addr=0x%0X is not mapped\n", - nodeRef.Addr, nodeRef.Data.Head.Addr) - } else if _, ok := resPaddrs[srcPaddr]; !ok { - fmt.Printf("node@%d: logical_addr=0x%0X maps to %v, not %v\n", - nodeRef.Addr, nodeRef.Data.Head.Addr, resPaddrs, srcPaddr) - } - }); err != nil { - return err - } - - return nil -} diff --git a/cmd/btrfs-fsck/main.go b/cmd/btrfs-fsck/main.go new file mode 100644 index 0000000..e1a37ab --- /dev/null +++ b/cmd/btrfs-fsck/main.go @@ -0,0 +1,170 @@ +package main + +import ( + "fmt" + "os" + "sort" + + "lukeshu.com/btrfs-tools/pkg/btrfs" + "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem" + "lukeshu.com/btrfs-tools/pkg/btrfsmisc" + "lukeshu.com/btrfs-tools/pkg/util" +) + +func main() { + if err := Main(os.Args[1]); err != nil { + fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err) + os.Exit(1) + } +} + +func Main(imgfilename string) (err error) { + maybeSetErr := func(_err error) { + if _err != nil && err == nil { + err = _err + } + } + + fh, err := os.Open(imgfilename) + if err != nil { + return err + } + defer func() { + maybeSetErr(fh.Close()) + }() + fs := &btrfs.FS{ + Devices: []*btrfs.Device{ + { + File: fh, + }, + }, + } + + fmt.Printf("\nPass 0: superblocks...\n") /////////////////////////////////////////////////// + + superblock, err := fs.Superblock() + if err != nil { + return fmt.Errorf("superblock: %w", err) + } + + fmt.Printf("\nPass 1: chunk mappings...\n") //////////////////////////////////////////////// + + fmt.Printf("Pass 1: ... initializing chunk mappings\n") + if err := fs.Init(); err != nil { + fmt.Printf("Pass 1: ... init chunk tree: error: %v\n", err) + } + + fmt.Printf("Pass 1: ... walking chunk tree\n") + visitedChunkNodes := make(map[btrfs.LogicalAddr]struct{}) + if err := fs.WalkTree(superblock.Data.ChunkTree, btrfs.WalkTreeHandler{ + MidNode: func(node *util.Ref[btrfs.LogicalAddr, btrfs.Node]) error { + visitedChunkNodes[node.Addr] = struct{}{} + return nil + }, + }); err != nil { + fmt.Printf("Pass 1: ... walk chunk tree: error: %v\n", err) + } + + fmt.Printf("Pass 1: ... scanning for nodes\n") + foundNodes := make(map[btrfs.LogicalAddr][]btrfs.PhysicalAddr) + var lostAndFoundChunks []btrfs.SysChunk + if err := btrfsmisc.ScanForNodes(fs.Devices[0], superblock.Data, func(nodeRef *util.Ref[btrfs.PhysicalAddr, btrfs.Node], err error) { + if err != nil { + fmt.Println(err) + return + } + foundNodes[nodeRef.Data.Head.Addr] = append(foundNodes[nodeRef.Data.Head.Addr], nodeRef.Addr) + _, alreadyVisited := visitedChunkNodes[nodeRef.Data.Head.Addr] + if nodeRef.Data.Head.Owner == btrfs.CHUNK_TREE_OBJECTID && !alreadyVisited { + for i, item := range nodeRef.Data.BodyLeaf { + if item.Head.Key.ItemType != btrfsitem.CHUNK_ITEM_KEY { + continue + } + chunk, ok := item.Body.(btrfsitem.Chunk) + if !ok { + fmt.Printf("Pass 1: node@%d: item %d: error: type is CHUNK_ITEM_KEY, but struct is %T\n", + nodeRef.Addr, i, item.Body) + continue + } + fmt.Printf("Pass 1: node@%d: item %d: found chunk\n", + nodeRef.Addr, i) + lostAndFoundChunks = append(lostAndFoundChunks, btrfs.SysChunk{ + Key: item.Head.Key, + Chunk: chunk, + }) + } + } + }); err != nil { + return err + } + + fmt.Printf("Pass 1: ... re-inserting lost+found chunks\n") + if len(lostAndFoundChunks) > 0 { + panic("TODO") + } + + fmt.Printf("Pass 1: ... re-constructing stripes for lost+found nodes\n") + lostAndFoundNodes := make(map[btrfs.PhysicalAddr]btrfs.LogicalAddr) + for laddr, readPaddrs := range foundNodes { + resolvedPaddrs, _ := fs.Resolve(laddr) + for _, readPaddr := range readPaddrs { + if _, ok := resolvedPaddrs[btrfs.QualifiedPhysicalAddr{ + Dev: superblock.Data.DevItem.DevUUID, + Addr: readPaddr, + }]; !ok { + lostAndFoundNodes[readPaddr] = laddr + } + } + } + sortedPaddrs := make([]btrfs.PhysicalAddr, 0, len(lostAndFoundNodes)) + for paddr := range lostAndFoundNodes { + sortedPaddrs = append(sortedPaddrs, paddr) + } + sort.Slice(sortedPaddrs, func(i, j int) bool { + return sortedPaddrs[i] < sortedPaddrs[j] + }) + type stripe struct { + PAddr btrfs.PhysicalAddr + LAddr btrfs.LogicalAddr + Size uint64 + } + var stripes []stripe + for _, paddr := range sortedPaddrs { + var lastStripe *stripe + if len(stripes) > 0 { + lastStripe = &stripes[len(stripes)-1] + } + if lastStripe != nil && (lastStripe.PAddr+btrfs.PhysicalAddr(lastStripe.Size)) == paddr { + lastStripe.Size += uint64(superblock.Data.NodeSize) + } else { + stripes = append(stripes, stripe{ + PAddr: paddr, + LAddr: lostAndFoundNodes[paddr], + Size: uint64(superblock.Data.NodeSize), + }) + } + } + fmt.Printf("Pass 1: ... reconstructed stripes: %#v\n", stripes) + + fmt.Printf("\nPass 2: ?????????????????????????\n") //////////////////////////////////////// + /* + + fmt.Printf("node@%d: physical_addr=0x%0X logical_addr=0x%0X generation=%d owner=%v level=%d\n", + nodeRef.Addr, + nodeRef.Addr, nodeRef.Data.Head.Addr, + nodeRef.Data.Head.Generation, nodeRef.Data.Head.Owner, nodeRef.Data.Head.Level) + srcPaddr := btrfs.QualifiedPhysicalAddr{ + Dev: superblock.Data.DevItem.DevUUID, + Addr: nodeRef.Addr, + } + resPaddrs, _ := fs.Resolve(nodeRef.Data.Head.Addr) + if len(resPaddrs) == 0 { + fmt.Printf("node@%d: logical_addr=0x%0X is not mapped\n", + nodeRef.Addr, nodeRef.Data.Head.Addr) + } else if _, ok := resPaddrs[srcPaddr]; !ok { + fmt.Printf("node@%d: logical_addr=0x%0X maps to %v, not %v\n", + nodeRef.Addr, nodeRef.Data.Head.Addr, resPaddrs, srcPaddr) + } + */ + return nil +} diff --git a/pkg/btrfs/btrfsitem/item_chunk.go b/pkg/btrfs/btrfsitem/item_chunk.go index eae8339..4389a46 100644 --- a/pkg/btrfs/btrfsitem/item_chunk.go +++ b/pkg/btrfs/btrfsitem/item_chunk.go @@ -23,9 +23,9 @@ type Chunk struct { // CHUNK_ITEM=228 } type ChunkStripe struct { - DeviceID internal.ObjID `bin:"off=0x0, siz=0x8"` - Offset uint64 `bin:"off=0x8, siz=0x8"` - DeviceUUID internal.UUID `bin:"off=0x10, siz=0x10"` + DeviceID internal.ObjID `bin:"off=0x0, siz=0x8"` + Offset internal.PhysicalAddr `bin:"off=0x8, siz=0x8"` + DeviceUUID internal.UUID `bin:"off=0x10, siz=0x10"` binstruct.End `bin:"off=0x20"` } diff --git a/pkg/btrfs/io2_fs.go b/pkg/btrfs/io2_fs.go index ff5415a..ca67a9c 100644 --- a/pkg/btrfs/io2_fs.go +++ b/pkg/btrfs/io2_fs.go @@ -181,7 +181,7 @@ func (fs *FS) Resolve(laddr LogicalAddr) (paddrs map[QualifiedPhysicalAddr]struc for _, stripe := range chunk.Chunk.Stripes { paddrs[QualifiedPhysicalAddr{ Dev: stripe.DeviceUUID, - Addr: PhysicalAddr(stripe.Offset + offsetWithinChunk), + Addr: stripe.Offset + PhysicalAddr(offsetWithinChunk), }] = struct{}{} } } diff --git a/pkg/btrfs/types_btree.go b/pkg/btrfs/types_btree.go index 5adc7ec..89901c3 100644 --- a/pkg/btrfs/types_btree.go +++ b/pkg/btrfs/types_btree.go @@ -2,7 +2,9 @@ package btrfs import ( "encoding/binary" + "errors" "fmt" + iofs "io/fs" "lukeshu.com/btrfs-tools/pkg/binstruct" "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem" @@ -297,6 +299,10 @@ func (fs *FS) ReadNode(addr LogicalAddr) (*util.Ref[LogicalAddr, Node], error) { } type WalkTreeHandler struct { + // Callbacks for entire nodes + PreNode func(LogicalAddr) error + MidNode func(*util.Ref[LogicalAddr, Node]) error + PostNode func(*util.Ref[LogicalAddr, Node]) error // Callbacks for items on internal nodes PreKeyPointer func(KeyPointer) error PostKeyPointer func(KeyPointer) error @@ -310,18 +316,40 @@ func (fs *FS) WalkTree(nodeAddr LogicalAddr, cbs WalkTreeHandler) error { if nodeAddr == 0 { return nil } + if cbs.PreNode != nil { + if err := cbs.PreNode(nodeAddr); err != nil { + if errors.Is(err, iofs.SkipDir) { + return nil + } + return err + } + } node, err := fs.ReadNode(nodeAddr) if err != nil { if cbs.NodeError != nil { err = cbs.NodeError(err) } if err != nil { + if errors.Is(err, iofs.SkipDir) { + return nil + } return fmt.Errorf("btrfs.FS.WalkTree: %w", err) } } + if cbs.MidNode != nil { + if err := cbs.MidNode(node); err != nil { + if errors.Is(err, iofs.SkipDir) { + return nil + } + return err + } + } for _, item := range node.Data.BodyInternal { if cbs.PreKeyPointer != nil { if err := cbs.PreKeyPointer(item); err != nil { + if errors.Is(err, iofs.SkipDir) { + continue + } return err } } @@ -330,6 +358,9 @@ func (fs *FS) WalkTree(nodeAddr LogicalAddr, cbs WalkTreeHandler) error { } if cbs.PostKeyPointer != nil { if err := cbs.PostKeyPointer(item); err != nil { + if errors.Is(err, iofs.SkipDir) { + continue + } return err } } @@ -337,9 +368,20 @@ func (fs *FS) WalkTree(nodeAddr LogicalAddr, cbs WalkTreeHandler) error { for _, item := range node.Data.BodyLeaf { if cbs.Item != nil { if err := cbs.Item(item.Head.Key, item.Body); err != nil { + if errors.Is(err, iofs.SkipDir) { + continue + } return fmt.Errorf("btrfs.FS.WalkTree: callback: %w", err) } } } + if cbs.PostNode != nil { + if err := cbs.PostNode(node); err != nil { + if errors.Is(err, iofs.SkipDir) { + return nil + } + return err + } + } return nil } diff --git a/pkg/btrfsmisc/fsck.go b/pkg/btrfsmisc/fsck.go index a9e7d1d..8d20e6e 100644 --- a/pkg/btrfsmisc/fsck.go +++ b/pkg/btrfsmisc/fsck.go @@ -23,7 +23,7 @@ func ScanForNodes(dev *btrfs.Device, sb btrfs.Superblock, fn func(*util.Ref[btrf } nodeBuf := make([]byte, sb.NodeSize) - for pos := btrfs.PhysicalAddr(0); pos+btrfs.PhysicalAddr(sb.SectorSize) < devSize; pos += btrfs.PhysicalAddr(sb.SectorSize) { + for pos := btrfs.PhysicalAddr(0); pos+btrfs.PhysicalAddr(sb.NodeSize) < devSize; pos += btrfs.PhysicalAddr(sb.SectorSize) { if util.InSlice(pos, btrfs.SuperblockAddrs) { //fmt.Printf("sector@%d is a superblock\n", pos) continue |