summaryrefslogtreecommitdiff
path: root/pkg/btrfs/io_fs.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-05-24 21:53:28 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-05-24 21:53:28 -0600
commit23ab1f8be6a1f4b5ce01e05f8ed3f6b5dae30d0b (patch)
tree854801aa60dd204cd390c40f7cafb326e981014f /pkg/btrfs/io_fs.go
parentb0dd4d1f0c8262e3680570d529d00c9ebead1a91 (diff)
stuff
Diffstat (limited to 'pkg/btrfs/io_fs.go')
-rw-r--r--pkg/btrfs/io_fs.go123
1 files changed, 123 insertions, 0 deletions
diff --git a/pkg/btrfs/io_fs.go b/pkg/btrfs/io_fs.go
new file mode 100644
index 0000000..52f742a
--- /dev/null
+++ b/pkg/btrfs/io_fs.go
@@ -0,0 +1,123 @@
+package btrfs
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+)
+
+type FS struct {
+ Devices []*Device
+
+ initErr error
+ uuid2dev map[UUID]*Device
+ chunks []SysChunk
+}
+
+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)
+ }
+ }
+ 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:])
+ if err != nil {
+ return err
+ }
+ done += LogicalAddr(n)
+ }
+ return nil
+}
+
+func (fs *FS) readLogicalMaybeShort(laddr LogicalAddr, dat []byte) (int, error) {
+ if err := fs.init(); err != nil {
+ return 0, err
+ }
+
+ 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, int64(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
+}