summaryrefslogtreecommitdiff
path: root/pkg/btrfs/io_fs.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/btrfs/io_fs.go')
-rw-r--r--pkg/btrfs/io_fs.go172
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
+ }
+}