Skip to content

Commit

Permalink
Support sector size of 4096 bytes (#4)
Browse files Browse the repository at this point in the history
* default chunk reader

* custom size chunk reader

* single usage for dynamic reader

* refactor functions

* sector reader

* fix(inode): change reader size for inode reader

* fix(xfs): wip fix parse single directory entries

* fix(dir2block): wip support multi block entry

---------

Co-authored-by: masahiro331 <m_fujimura@r.recruit.co.jp>
  • Loading branch information
amirbenun and masahiro331 committed Jun 5, 2023
1 parent 7a08aef commit 679b545
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 90 deletions.
100 changes: 74 additions & 26 deletions xfs/ag.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ type BtreeShortBlock struct {

func parseSuperBlock(r io.Reader) (SuperBlock, error) {
var sb SuperBlock
buf, err := utils.ReadSector(r)
sectorReader := utils.DefaultSectorReader()
buf, err := sectorReader.ReadSector(r)
if err != nil {
return SuperBlock{}, xerrors.Errorf("failed to create superblock reader: %w", err)
}
Expand All @@ -122,50 +123,97 @@ func parseSuperBlock(r io.Reader) (SuperBlock, error) {
if sb.Magicnum != XFS_SB_MAGIC {
return SuperBlock{}, xerrors.Errorf("failed to parse superblock magic byte error: %08x", sb.Magicnum)
}

if sb.Sectsize != utils.SectorSize {
completeSector := int(sb.Sectsize - utils.SectorSize)
buf := make([]byte, completeSector)
i, err := r.Read(buf)
if err != nil {
return SuperBlock{}, xerrors.Errorf("failed to read: %w", err)
}
if i != completeSector {
return SuperBlock{}, xerrors.Errorf("sector size error, read %d byte", i)
}
}
return sb, nil
}

func ParseAG(reader io.Reader) (*AG, error) {
r := io.LimitReader(reader, int64(utils.BlockSize))

var ag AG
var err error
ag.SuperBlock, err = parseSuperBlock(r)
func parseAGF(sectorReader utils.SectorReader, r io.Reader) (AGF, error) {
var agf AGF
buf, err := sectorReader.ReadSector(r)
if err != nil {
return nil, xerrors.Errorf("failed to parse super block: %w", err)
return AGF{}, xerrors.Errorf("failed to create agf reader: %w", err)
}
if err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &agf); err != nil {
return AGF{}, xerrors.Errorf("failed to read agf: %w", err)
}
if agf.Magicnum != XFS_AGF_MAGIC {
return AGF{}, xerrors.Errorf("failed to parse agf magic byte error: %08x", agf.Magicnum)
}
return agf, nil
}

buf, err := utils.ReadSector(r)
func parseAGI(sectorReader utils.SectorReader, r io.Reader) (AGI, error) {
var agi AGI
buf, err := sectorReader.ReadSector(r)
if err != nil {
return nil, xerrors.Errorf("failed to create afg reader: %w", err)
return AGI{}, xerrors.Errorf("failed to create agi reader: %w", err)
}
if err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &ag.Agf); err != nil {
return nil, xerrors.Errorf("failed to read afg: %w", err)
if err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &agi); err != nil {
return AGI{}, xerrors.Errorf("failed to read agi: %w", err)
}
if ag.Agf.Magicnum != XFS_AGF_MAGIC {
return nil, xerrors.Errorf("failed to parse agf magic byte error: %08x", ag.Agf.Magicnum)
if agi.Magicnum != XFS_AGI_MAGIC {
return AGI{}, xerrors.Errorf("failed to parse agi magic byte error: %08x", agi.Magicnum)
}
return agi, nil
}

buf, err = utils.ReadSector(r)
func parseAGFL(sectorReader utils.SectorReader, r io.Reader) (AGFL, error) {
var agfl AGFL
buf, err := sectorReader.ReadSector(r)
if err != nil {
return nil, xerrors.Errorf("failed to create agi reader: %w", err)
return AGFL{}, xerrors.Errorf("failed to create agfl reader: %w", err)
}
if err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &agfl); err != nil {
return AGFL{}, xerrors.Errorf("failed to read agfl: %w", err)
}
if agfl.Magicnum != XFS_AGFL_MAGIC {
return AGFL{}, xerrors.Errorf("failed to parse agfl magic byte error: %08x", agfl.Magicnum)
}
if err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &ag.Agi); err != nil {
return nil, xerrors.Errorf("failed to read agi: %w", err)
return agfl, nil
}

func ParseAG(reader io.Reader) (*AG, error) {
var r io.Reader
var ag AG
var err error
r = io.LimitReader(reader, int64(utils.BlockSize))
ag.SuperBlock, err = parseSuperBlock(r)
if err != nil {
return nil, xerrors.Errorf("failed to parse super block: %w", err)
}
if ag.Agi.Magicnum != XFS_AGI_MAGIC {
return nil, xerrors.Errorf("failed to parse agi magic byte error: %08x", ag.Agi.Magicnum)

sectorReader, err := utils.NewSectorReader(int(ag.SuperBlock.Sectsize))
if err != nil {
return nil, xerrors.Errorf("failed to create chunk reader: %w", err)
}

buf, err = utils.ReadSector(r)
r = io.LimitReader(reader, int64(utils.BlockSize))
ag.Agf, err = parseAGF(sectorReader, r)
if err != nil {
return nil, xerrors.Errorf("failed to create agfl reader: %w", err)
return nil, xerrors.Errorf("failed to parse agf block: %w", err)
}
if err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &ag.Agfl); err != nil {
return nil, xerrors.Errorf("failed to read agfl: %w", err)

r = io.LimitReader(reader, int64(utils.BlockSize))
ag.Agi, err = parseAGI(sectorReader, r)
if err != nil {
return nil, xerrors.Errorf("failed to parse agi block: %w", err)
}
if ag.Agfl.Magicnum != XFS_AGFL_MAGIC {
return nil, xerrors.Errorf("failed to parse agfl magic byte error: %08x", ag.Agfl.Magicnum)

r = io.LimitReader(reader, int64(utils.BlockSize))
ag.Agfl, err = parseAGFL(sectorReader, r)
if err != nil {
return nil, xerrors.Errorf("failed to parse agfl block: %w", err)
}

return &ag, nil
Expand Down
119 changes: 79 additions & 40 deletions xfs/inode.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,11 @@ func (xfs *FileSystem) ParseInode(ino uint64) (*Inode, error) {
return nil, xerrors.Errorf("failed to seek inode: %w", err)
}

buf, err := utils.ReadSector(xfs.r)
sectorReader, err := utils.NewSectorReader(int(xfs.PrimaryAG.SuperBlock.Inodesize))
if err != nil {
return nil, xerrors.Errorf("failed to create sector reader: %w", err)
}
buf, err := sectorReader.ReadSector(xfs.r)
if err != nil {
return nil, xerrors.Errorf("failed to read sector: %w", err)
}
Expand Down Expand Up @@ -621,11 +625,21 @@ func (xfs *FileSystem) parseXDB3Block(r io.Reader) ([]Dir2DataEntry, error) {
return nil, xerrors.Errorf("failed to read XDB3 block reader: %w", err)
}
var tail Dir2BlockTail
tailReader := bytes.NewReader(buf[len(buf)-int(unsafe.Sizeof(tail)):])

tailBlockOffset := len(buf) - int(unsafe.Sizeof(tail))
if tailBlockOffset > len(buf) {
return nil, xerrors.Errorf("failed to calculate tail block offset: %d", tailBlockOffset)
}
tailReader := bytes.NewReader(buf[tailBlockOffset:])
if err := binary.Read(tailReader, binary.BigEndian, &tail); err != nil {
return nil, xerrors.Errorf("failed to read tail binary: %w", err)
}
reader := bytes.NewReader(buf[:uint32(len(buf))-((tail.Count*LEAF_ENTRY_SIZE)+uint32(unsafe.Sizeof(tail)))])

dataEndOffset := uint32(len(buf)) - (tail.Count*LEAF_ENTRY_SIZE + uint32(unsafe.Sizeof(tail)))
if dataEndOffset > uint32(len(buf)) {
return nil, xerrors.Errorf("failed to calculate data end offset: %d", dataEndOffset)
}
reader := bytes.NewReader(buf[:dataEndOffset])

dir2DataEntries, err := xfs.parseDir2DataEntry(reader)
if err != nil {
Expand All @@ -649,19 +663,13 @@ func (xfs *FileSystem) parseDir2DataEntry(r io.Reader) ([]Dir2DataEntry, error)
entry := Dir2DataEntry{}

// Parse Inode number
ino := make([]byte, unsafe.Sizeof(entry.Inumber))
_, err := r.Read(ino)
if err != nil {
if err := binary.Read(r, binary.BigEndian, &entry.Inumber); err != nil {
if err == io.EOF {
return entries, nil
}
return nil, xerrors.Errorf("failed to read inumber: %w", err)
}
if err := binary.Read(bytes.NewReader(ino), binary.BigEndian, &entry.Inumber); err != nil {
return nil, xerrors.Errorf("failed to read inumber binary: %w", err)
}

// Skip FreeTag
if (entry.Inumber >> 48) == XFS_DIR2_DATA_FREE_TAG {
freeLen := (entry.Inumber >> 32) & Mask64Lo(16)
if freeLen != 8 {
Expand Down Expand Up @@ -699,63 +707,94 @@ func (xfs *FileSystem) parseDir2DataEntry(r io.Reader) ([]Dir2DataEntry, error)
align := (int(unsafe.Sizeof(entry.Inumber)) +
int(unsafe.Sizeof(entry.Namelen)) +
int(unsafe.Sizeof(entry.Filetype)) +
int(unsafe.Sizeof(entry.Tag)) + n) % 8
int(unsafe.Sizeof(entry.Tag)) +
int(entry.Namelen)) % 8
if align != 0 {
n, err = r.Read(make([]byte, 8-align))
if err != nil {
return nil, xerrors.Errorf("failed to read alignment: %w", err)
}
if n != int(8-align) {
return nil, xerrors.Errorf("failed to read alignment: expected namelen(%d) actual(%d)", entry.Namelen, n)
return nil, xerrors.Errorf("failed to read alignment: expected (%d) actual(%d)", 8-align, n)
}
}

// Read Tag
if err := binary.Read(r, binary.BigEndian, &entry.Tag); err != nil {
return nil, xerrors.Errorf("failed to read tag: %w", err)
}

entries = append(entries, entry)
}
}

func (xfs *FileSystem) parseDir2Block(bmbtIrec BmbtIrec) (*Dir2Block, error) {
func (xfs *FileSystem) parseDir2Block(bmbtIrec BmbtIrec) ([]Dir2DataEntry, error) {
block := Dir2Block{}
// Skip Leaf and Free node
/*
Skip Leaf and Free node.
The "leaf" block has a special offset defined by XFS_DIR2_LEAF_OFFSET. Currently, this is 32GB and in the extent view,
a block offset of 32GB / sb_blocksize. On a 4KB block filesystem, this is 0x800000 (8388608 decimal).
*/
if int64(bmbtIrec.StartOff)*int64(xfs.PrimaryAG.SuperBlock.BlockSize) >= int64(XFS_DIR2_LEAF_OFFSET) {
return &block, nil
return nil, nil
}

physicalBlockOffset := xfs.PrimaryAG.SuperBlock.BlockToPhysicalOffset(bmbtIrec.StartBlock)
_, err := xfs.seekBlock(physicalBlockOffset)
if err != nil {
return nil, xerrors.Errorf("failed to seek block: %w", err)
}

// TODO: add tests, If Block count greater than 2
b, err := utils.ReadBlock(xfs.r)
if err != nil {
return nil, xerrors.Errorf("failed to read block: %w", err)
}
r := bytes.NewReader(b)
if err := binary.Read(r, binary.BigEndian, &block.Header); err != nil {
return nil, xerrors.Errorf("failed to parse block header error: %w", err)
}
switch block.Header.Magic {
case XFS_DIR3_DATA_MAGIC:
block.Entries, err = xfs.parseXDD3Block(r)
var buf []byte
for blockOffset := bmbtIrec.StartBlock; blockOffset < bmbtIrec.StartBlock+bmbtIrec.BlockCount; blockOffset++ {
physicalBlockOffset := xfs.PrimaryAG.SuperBlock.BlockToPhysicalOffset(blockOffset)
_, err := xfs.seekBlock(physicalBlockOffset)
if err != nil {
return nil, xerrors.Errorf("failed to parse XDD3 block: %w", err)
return nil, xerrors.Errorf("failed to seek block: %w", err)
}
case XFS_DIR3_BLOCK_MAGIC:
block.Entries, err = xfs.parseXDB3Block(r)
blockData, err := utils.ReadBlock(xfs.r)
if err != nil {
return nil, xerrors.Errorf("failed to parse XDB3 block: %w", err)
return nil, xerrors.Errorf("failed to read block: %w", err)
}
default:
return nil, xerrors.Errorf("failed to parse header error magic: %v: %w", block.Header.Magic, UnsupportedDir2BlockHeaderErr)
buf = append(buf, blockData...)

// if the next block is not a leader, it is a continuation of the previous block
if blockOffset != bmbtIrec.StartBlock+bmbtIrec.BlockCount-1 && // not last block
!xfs.nextBlockIsLeader(blockOffset) { // not leader
continue
}

// if the next block is a leader, it is the last block of the directory
magicBytes := binary.BigEndian.Uint32(buf[:4])
reader := bytes.NewReader(buf)
if err := binary.Read(reader, binary.BigEndian, &block.Header); err != nil {
return nil, xerrors.Errorf("failed to parse dir3 data header error: %w", err)
}
switch magicBytes {
case XFS_DIR3_DATA_MAGIC:
entries, err := xfs.parseXDD3Block(reader)
if err != nil {
return nil, xerrors.Errorf("failed to parse XDD3 block: %w", err)
}
block.Entries = append(block.Entries, entries...)
case XFS_DIR3_BLOCK_MAGIC:
entries, err := xfs.parseXDB3Block(reader)
if err != nil {
return nil, xerrors.Errorf("failed to parse XDB3 block: %w", err)
}
block.Entries = append(block.Entries, entries...)
default:
return nil, xerrors.Errorf("unknown magic bytes: %x", magicBytes)
}

// reset buf
buf = []byte{}
}

return &block, nil
return block.Entries, nil
}

func (xfs *FileSystem) nextBlockIsLeader(blockOffset uint64) bool {
physicalBlockOffset := xfs.PrimaryAG.SuperBlock.BlockToPhysicalOffset(blockOffset + 1)
xfs.seekBlock(physicalBlockOffset)
blockData, _ := utils.ReadBlock(xfs.r)

magic := binary.BigEndian.Uint32(blockData[:4])
return magic == XFS_DIR3_DATA_MAGIC || magic == XFS_DIR3_BLOCK_MAGIC
}

func parseEntry(r io.Reader, i8count bool) (*Dir2SfEntry, error) {
Expand Down

0 comments on commit 679b545

Please sign in to comment.