diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-06-01 01:27:19 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-06-01 01:27:19 -0600 |
commit | 3825cf60fd652f22acc438d50028701d27a7402d (patch) | |
tree | 24b86afb8513891274dfaa8b982c4c94c1a65a5d /pkg/btrfs/io_fs.go | |
parent | 2348cdbe2a3417990a2088f9e4e91adf0c45617d (diff) |
wow
Diffstat (limited to 'pkg/btrfs/io_fs.go')
-rw-r--r-- | pkg/btrfs/io_fs.go | 326 |
1 files changed, 0 insertions, 326 deletions
diff --git a/pkg/btrfs/io_fs.go b/pkg/btrfs/io_fs.go deleted file mode 100644 index 4f52d86..0000000 --- a/pkg/btrfs/io_fs.go +++ /dev/null @@ -1,326 +0,0 @@ -package btrfs - -import ( - "bytes" - "fmt" - "reflect" - - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -type FS struct { - Devices []*Device - - initErr error - uuid2dev map[UUID]*Device - 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 - } - fs.uuid2dev = make(map[UUID]*Device, len(fs.Devices)) - for _, dev := range fs.Devices { - sbs, err := dev.Superblocks() - if err != nil { - fs.initErr = fmt.Errorf("file %q: %w", dev.Name(), err) - return fs.initErr - } - - a := sbs[0].Data - a.Checksum = CSum{} - a.Self = 0 - for i, sb := range sbs[1:] { - b := sb.Data - b.Checksum = CSum{} - b.Self = 0 - if !reflect.DeepEqual(a, b) { - fs.initErr = fmt.Errorf("file %q: superblock %d disagrees with superblock 0", - dev.Name(), i+1) - return fs.initErr - } - } - sb := sbs[0] - if other, exists := fs.uuid2dev[sb.Data.DevItem.DevUUID]; exists { - fs.initErr = fmt.Errorf("file %q and file %q have the same device ID: %v", - other.Name(), dev.Name(), sb.Data.DevItem.DevUUID) - return fs.initErr - } - fs.uuid2dev[sb.Data.DevItem.DevUUID] = dev - syschunks, err := sb.Data.ParseSysChunkArray() - if err != nil { - fs.initErr = fmt.Errorf("file %q: %w", dev.Name(), err) - return fs.initErr - } - for _, chunk := range syschunks { - fs.chunks = append(fs.chunks, chunk) - } - if err := fs.WalkTree(sb.Data.ChunkTree, func(key Key, dat []byte) error { - pair := SysChunk{ - Key: key, - } - if err := binstruct.Unmarshal(dat, &pair.Chunk); err != nil { - return err - } - dat = dat[0x30:] - for i := 0; i < int(pair.Chunk.NumStripes); i++ { - var stripe Stripe - if err := binstruct.Unmarshal(dat, &stripe); err != nil { - return err - } - pair.Chunk.Stripes = append(pair.Chunk.Stripes, stripe) - dat = dat[0x20:] - } - fs.chunks = append(fs.chunks, pair) - return nil - }); err != nil { - fs.initErr = err - return fs.initErr - } - } - return nil -} - -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 done, err - } - } - return done, nil -} - -func (fs *FS) maybeShortReadAt(dat []byte, laddr LogicalAddr) (int, error) { - type physicalAddr struct { - Dev UUID - Addr PhysicalAddr - } - - paddrs := make(map[physicalAddr]struct{}) - - for _, chunk := range fs.chunks { - if chunk.Offset <= uint64(laddr) && uint64(laddr) < chunk.Offset+uint64(chunk.Chunk.Size) { - offsetWithinChunk := uint64(laddr) - chunk.Offset - if offsetWithinChunk+uint64(len(dat)) > chunk.Chunk.Size { - dat = dat[:chunk.Chunk.Size-offsetWithinChunk] - } - for _, stripe := range chunk.Chunk.Stripes { - paddrs[physicalAddr{ - Dev: stripe.DeviceUUID, - Addr: PhysicalAddr(stripe.Offset + offsetWithinChunk), - }] = struct{}{} - } - } - } - - if len(paddrs) == 0 { - return 0, fmt.Errorf("could not map logical address %v", laddr) - } - - buf := make([]byte, len(dat)) - first := true - for paddr := range paddrs { - dev, ok := fs.uuid2dev[paddr.Dev] - if !ok { - return 0, fmt.Errorf("device=%s does not exist", paddr.Dev) - } - 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 { - copy(dat, buf) - } else { - if !bytes.Equal(dat, buf) { - return 0, fmt.Errorf("inconsistent stripes at laddr=%v len=%d", laddr, len(dat)) - } - } - } - 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) - } - - if nodeHeader.Addr != addr { - return nil, fmt.Errorf("node@%d: read from laddr=%d but claims to be at laddr=%d", - addr, addr, nodeHeader.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 - } -} - -func (fs *FS) WalkTree(nodeAddr LogicalAddr, fn func(Key, []byte) error) error { - if nodeAddr == 0 { - return nil - } - node, err := fs.ReadNode(nodeAddr) - if err != nil { - return err - } - switch node := node.(type) { - case *InternalNode: - for _, item := range node.Body { - // fn(item.Data.Key, TODO) - if err := fs.WalkTree(item.Data.BlockPtr, fn); err != nil { - return err - } - } - case *LeafNode: - for _, item := range node.Body { - if err := fn(item.Data.Key, item.Data.Data.Data); err != nil { - return err - } - } - } - return nil -} |