diff options
Diffstat (limited to 'pkg/btrfs/io_fs.go')
-rw-r--r-- | pkg/btrfs/io_fs.go | 172 |
1 files changed, 163 insertions, 9 deletions
diff --git a/pkg/btrfs/io_fs.go b/pkg/btrfs/io_fs.go index 52f742a..5dee6dc 100644 --- a/pkg/btrfs/io_fs.go +++ b/pkg/btrfs/io_fs.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "reflect" + + "lukeshu.com/btrfs-tools/pkg/binstruct" ) type FS struct { @@ -14,6 +16,71 @@ type FS struct { chunks []SysChunk } +func (fs *FS) Name() string { + sb, err := fs.Superblock() + if err != nil { + return fmt.Sprintf("fs_uuid=%s", "(unreadable)") + } + return fmt.Sprintf("fs_uuid=%s", sb.Data.FSUUID) +} + +func (fs *FS) Size() (LogicalAddr, error) { + var ret LogicalAddr + for _, dev := range fs.Devices { + sz, err := dev.Size() + if err != nil { + return 0, fmt.Errorf("file %q: %w", dev.Name(), err) + } + ret += LogicalAddr(sz) + } + return ret, nil +} + +func (fs *FS) Superblocks() ([]Ref[PhysicalAddr, Superblock], error) { + var ret []Ref[PhysicalAddr, Superblock] + for _, dev := range fs.Devices { + sbs, err := dev.Superblocks() + if err != nil { + return nil, fmt.Errorf("file %q: %w", dev.Name(), err) + } + ret = append(ret, sbs...) + } + return ret, nil +} + +func (fs *FS) Superblock() (ret Ref[PhysicalAddr, Superblock], err error) { + sbs, err := fs.Superblocks() + if err != nil { + return ret, err + } + + fname := "" + sbi := 0 + for i, sb := range sbs { + if sb.File.Name() != fname { + fname = sb.File.Name() + sbi = 0 + } else { + sbi++ + } + + if err := sb.Data.ValidateChecksum(); err != nil { + return ret, fmt.Errorf("file %q superblock %d: %w", sb.File.Name(), sbi, err) + } + if i > 0 { + // This is probably wrong, but lots of my + // multi-device code is probably wrong. + if !sb.Data.Equal(sbs[0].Data) { + return ret, fmt.Errorf("file %q superblock %d and file %q superblock %d disagree", + sbs[0].File.Name(), 0, + sb.File.Name(), sbi) + } + } + } + + return sbs[0], nil +} + func (fs *FS) init() error { if fs.uuid2dev != nil { return fs.initErr @@ -58,19 +125,19 @@ func (fs *FS) init() error { return nil } -func (fs *FS) ReadLogicalFull(laddr LogicalAddr, dat []byte) error { - done := LogicalAddr(0) - for done < LogicalAddr(len(dat)) { - n, err := fs.readLogicalMaybeShort(laddr+done, dat[done:]) +func (fs *FS) ReadAt(dat []byte, laddr LogicalAddr) (int, error) { + done := 0 + for done < len(dat) { + n, err := fs.maybeShortReadAt(dat[done:], laddr+LogicalAddr(done)) + done += n if err != nil { - return err + return done, err } - done += LogicalAddr(n) } - return nil + return done, nil } -func (fs *FS) readLogicalMaybeShort(laddr LogicalAddr, dat []byte) (int, error) { +func (fs *FS) maybeShortReadAt(dat []byte, laddr LogicalAddr) (int, error) { if err := fs.init(); err != nil { return 0, err } @@ -108,7 +175,7 @@ func (fs *FS) readLogicalMaybeShort(laddr LogicalAddr, dat []byte) (int, error) if !ok { return 0, fmt.Errorf("device=%s does not exist", paddr.Dev) } - if _, err := dev.ReadAt(buf, int64(paddr.Addr)); err != nil { + if _, err := dev.ReadAt(buf, paddr.Addr); err != nil { return 0, fmt.Errorf("read device=%s paddr=%v: %w", paddr.Dev, paddr.Addr, err) } if first { @@ -121,3 +188,90 @@ func (fs *FS) readLogicalMaybeShort(laddr LogicalAddr, dat []byte) (int, error) } return len(dat), nil } + +func (fs *FS) ReadNode(addr LogicalAddr) (Node, error) { + sb, err := fs.Superblock() + if err != nil { + return nil, err + } + + nodeBuf := make([]byte, sb.Data.NodeSize) + if _, err := fs.ReadAt(nodeBuf, addr); err != nil { + return nil, err + } + + var nodeHeader NodeHeader + if err := binstruct.Unmarshal(nodeBuf, &nodeHeader); err != nil { + return nil, fmt.Errorf("node@%d: %w", addr, err) + } + + if !nodeHeader.MetadataUUID.Equal(sb.Data.EffectiveMetadataUUID()) { + return nil, fmt.Errorf("node@%d: does not look like a node", addr) + } + + stored := nodeHeader.Checksum + calced := CRC32c(nodeBuf[0x20:]) + if !calced.Equal(stored) { + return nil, fmt.Errorf("node@%d: checksum mismatch: stored=%s calculated=%s", + addr, stored, calced) + } + + nodeHeader.Size = sb.Data.NodeSize + + if nodeHeader.Level > 0 { + // internal node + nodeHeader.MaxItems = (sb.Data.NodeSize - 0x65) / 0x21 + ret := &InternalNode{ + Header: Ref[LogicalAddr, NodeHeader]{ + File: fs, + Addr: addr, + Data: nodeHeader, + }, + Body: nil, + } + for i := uint32(0); i < nodeHeader.NumItems; i++ { + itemOff := 0x65 + (0x21 * int(i)) + var item KeyPointer + if err := binstruct.Unmarshal(nodeBuf[itemOff:], &item); err != nil { + return nil, fmt.Errorf("node@%d (internal): item %d: %w", addr, i, err) + } + ret.Body = append(ret.Body, Ref[LogicalAddr, KeyPointer]{ + File: fs, + Addr: addr + LogicalAddr(itemOff), + Data: item, + }) + } + return ret, nil + } else { + // leaf node + nodeHeader.MaxItems = (sb.Data.NodeSize - 0x65) / 0x19 + ret := &LeafNode{ + Header: Ref[LogicalAddr, NodeHeader]{ + File: fs, + Addr: addr, + Data: nodeHeader, + }, + Body: nil, + } + for i := uint32(0); i < nodeHeader.NumItems; i++ { + itemOff := 0x65 + (0x19 * int(i)) + var item Item + if err := binstruct.Unmarshal(nodeBuf[itemOff:], &item); err != nil { + return nil, fmt.Errorf("node@%d (leaf): item %d: %w", addr, i, err) + } + dataOff := 0x65 + int(item.DataOffset) + dataSize := int(item.DataSize) + item.Data = Ref[LogicalAddr, []byte]{ + File: fs, + Addr: addr + LogicalAddr(dataOff), + Data: nodeBuf[dataOff : dataOff+dataSize], + } + ret.Body = append(ret.Body, Ref[LogicalAddr, Item]{ + File: fs, + Addr: addr + LogicalAddr(itemOff), + Data: item, + }) + } + return ret, nil + } +} |