diff --git a/README.md b/README.md index 787d63e..fc85bd2 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Thanks also to [distri's squashfs library](https://github.com/distr1/distri/tree # Not Working (Yet). Roughly in order. -* Give a list of files - * In io.FileStat (?) form * Figure out fragments * Extracting files * from inodes. * from path. * from file info. +* Give a list of files + * In io.FileStat (?) form * Reading the UID, GUID, Xatt, Compression Options, Export, and Fragment tables. * Implement other compression types (Should be relatively easy) * Squashing # Where I'm at. -* Working on reading data blocks \ No newline at end of file +* Reading fragments does not seem to be working right now \ No newline at end of file diff --git a/blockreader.go b/blockreader.go index 6a3ca8c..75ad9b0 100644 --- a/blockreader.go +++ b/blockreader.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "errors" + "fmt" "io" ) @@ -67,6 +68,7 @@ func (br *BlockReader) parseMetadata() error { } func (br *BlockReader) readNextDataBlock() error { + fmt.Println("reading new block") meta := br.headers[len(br.headers)-1] r := io.NewSectionReader(br.s.r, br.offset, int64(meta.size)) if meta.compressed { diff --git a/datareader.go b/datareader.go index 60c2e40..f2b342e 100644 --- a/datareader.go +++ b/datareader.go @@ -6,6 +6,7 @@ import ( "io" ) +//DataReader reads data from data blocks. type DataReader struct { r *Reader offset int64 //offset relative to the beginning of the squash file @@ -13,9 +14,9 @@ type DataReader struct { curBlock int //Which block in sizes is currently cached curData []byte curReadOffset int //offset relative to the currently cached data - readOffset int //offset relative to the beginning } +//DataBlock holds info about a given data block from it's size type DataBlock struct { begOffset int64 //The offset relative to the beginning of the squash file. Makes it easier to seek to it. size uint32 @@ -23,6 +24,7 @@ type DataBlock struct { uncompressedSize uint32 } +//NewDataBlockSize creates a new squashfs.datablock from a given size. func NewDataBlockSize(raw uint32) (dbs DataBlock) { dbs.compressed = raw&1<<24 != 1<<24 dbs.size = raw &^ 1 << 24 @@ -32,6 +34,7 @@ func NewDataBlockSize(raw uint32) (dbs DataBlock) { return } +//NewDataReader creates a new data reader at the given offset, with the blocks defined by sizes func (r *Reader) NewDataReader(offset int64, sizes []uint32) (*DataReader, error) { var dr DataReader dr.r = r @@ -62,6 +65,9 @@ func (d *DataReader) readNextBlock() error { } func (d *DataReader) readCurBlock() error { + if d.curBlock >= len(d.blocks) { + return io.EOF + } if d.blocks[d.curBlock].size == 0 { d.curData = make([]byte, d.r.super.BlockSize) d.blocks[d.curBlock].uncompressedSize = d.r.super.BlockSize @@ -96,7 +102,6 @@ func (d *DataReader) Read(p []byte) (int, error) { for i := 0; i < len(p); i++ { p[i] = d.curData[d.curReadOffset+i] } - d.readOffset += len(p) d.curReadOffset += len(p) return len(p), nil } @@ -106,7 +111,6 @@ func (d *DataReader) Read(p []byte) (int, error) { if d.curReadOffset == len(d.curData) { err := d.readNextBlock() if err != nil { - d.readOffset += read return read, err } curRead = 0 @@ -120,37 +124,8 @@ func (d *DataReader) Read(p []byte) (int, error) { } } } - d.readOffset += read if read != len(p) { return read, errors.New("Didn't read enough data") } return read, nil } - -func (d *DataReader) Seek(offset int64, whence int) (int, error) { - if whence == io.SeekStart { - d.readOffset = int(offset) - d.curReadOffset = int(offset) - d.curBlock = 0 - read := int64(0) - for i, block := range d.blocks { - if block.uncompressedSize == 0 { - d.curBlock = i - err := d.readCurBlock() - if err != nil { - return 0, err - } - } - read += int64(block.uncompressedSize) - if offset-read < 0 { - d.curReadOffset = int((offset - read) + int64(block.uncompressedSize)) - } - } - } - switch whence { - case io.SeekCurrent: - d.readOffset += int(offset) - d.curReadOffset += int(offset) - case io.SeekEnd: - } -} diff --git a/fragment.go b/fragment.go index a1f2d48..2d28f04 100644 --- a/fragment.go +++ b/fragment.go @@ -3,35 +3,16 @@ package squashfs import ( "encoding/binary" "errors" + "fmt" "io" "github.com/CalebQ42/GoSquashfs/internal/inode" ) -type FragmentEntryRaw struct { - Start uint64 - Size uint32 - _unused uint32 -} - type FragmentEntry struct { - start uint64 - size uint32 - compressed bool -} - -//NewFragmentEntry reads a fragment entry from the given io.Reader. -func (r *Reader) NewFragmentEntry(rdr io.Reader) (*FragmentEntry, error) { - var entry FragmentEntry - var raw FragmentEntryRaw - err := binary.Read(rdr, binary.LittleEndian, &raw) - if err != nil { - return nil, err - } - entry.start = raw.Start - entry.compressed = raw.Size&0x1000000 == 0x1000000 - entry.size = raw.Size &^ 0x1000000 - return &entry, nil + Start uint64 + Size uint32 + Unused uint32 } //GetFragmentDataFromInode returns the fragment data for a given inode. @@ -52,6 +33,7 @@ func (r *Reader) GetFragmentDataFromInode(in *inode.Inode) ([]byte, error) { } fragIndex = bf.Init.FragmentIndex fragOffset = bf.Init.FragmentOffset + fmt.Println(fragIndex, fragOffset, size) } else if in.Type == inode.ExtFileType { bf := in.Info.(inode.ExtendedFile) if !bf.Fragmented { @@ -67,17 +49,33 @@ func (r *Reader) GetFragmentDataFromInode(in *inode.Inode) ([]byte, error) { } else { return nil, errors.New("Inode type not supported") } - frag := r.fragEntries[fragIndex] - datRdr, err := r.NewDataReader(int64(frag.start), []uint32{frag.size}) + fmt.Println("fragment index", fragIndex) + //reading the fragment entry first + fragBlockIndex := int(fragIndex / 512) + fragEntryRdr, err := r.NewBlockReader(int64(r.fragOffsets[fragBlockIndex])) if err != nil { return nil, err } - _, err = datRdr.Seek(int64(fragOffset), io.SeekStart) + _, err = fragEntryRdr.Seek(int64(16*fragIndex), io.SeekStart) + if err != nil { + return nil, err + } + var entry FragmentEntry + err = binary.Read(fragEntryRdr, binary.LittleEndian, &entry) + if err != nil { + return nil, err + } + //now reading the actual fragment + dr, err := r.NewDataReader(int64(entry.Start), []uint32{entry.Size}) + if err != nil { + return nil, err + } + _, err = dr.Read(make([]byte, fragOffset)) if err != nil { return nil, err } tmp := make([]byte, size) - _, err = datRdr.Read(tmp) + err = binary.Read(dr, binary.LittleEndian, &tmp) if err != nil { return nil, err } diff --git a/internal/inode/inode.go b/internal/inode/inodetypes.go similarity index 100% rename from internal/inode/inode.go rename to internal/inode/inodetypes.go diff --git a/reader.go b/reader.go index faaa836..e97b599 100644 --- a/reader.go +++ b/reader.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" "io" - "strings" + "math" "github.com/CalebQ42/GoSquashfs/internal/inode" ) @@ -33,7 +33,7 @@ type Reader struct { super Superblock flags SuperblockFlags decompressor Decompressor - fragEntries []*FragmentEntry + fragOffsets []uint64 } //NewSquashfsReader returns a new squashfs.Reader from an io.ReaderAt @@ -58,16 +58,19 @@ func NewSquashfsReader(r io.ReaderAt) (*Reader, error) { //TODO: parse compressor options return nil, ErrCompressorOptions } - br, err := rdr.NewBlockReader(int64(rdr.super.FragTableStart)) - if err != nil { - return &rdr, err - } - for i := 0; i < int(rdr.super.FragCount); i++ { - entry, err := rdr.NewFragmentEntry(br) - if err != nil { - return &rdr, err + fragBlocks := int(math.Ceil(float64(rdr.super.FragCount) / 512.0)) + if fragBlocks > 0 { + offset := int64(rdr.super.FragTableStart) + for i := 0; i < fragBlocks; i++ { + tmp := make([]byte, 8) + _, err = r.ReadAt(tmp, offset) + if err != nil { + fmt.Println("Error while reading fragment block offsets") + return nil, err + } + rdr.fragOffsets = append(rdr.fragOffsets, binary.LittleEndian.Uint64(tmp)) + offset += 8 } - rdr.fragEntries = append(rdr.fragEntries, entry) } return &rdr, nil } @@ -119,12 +122,3 @@ func (r *Reader) readDir(i *inode.Inode) (paths []string, err error) { } return } - -func (r *Reader) readDirTable() error { - paths, err := r.GetFilesList() - if err != nil { - return err - } - fmt.Println(strings.Join(paths, "\n")) - return nil -} diff --git a/squash_test.go b/squash_test.go index 501819f..28743ca 100644 --- a/squash_test.go +++ b/squash_test.go @@ -35,10 +35,6 @@ func TestMain(t *testing.T) { if err != nil { t.Fatal(err) } - err = rdr.readDirTable() - if err != nil { - t.Fatal(err) - } i, err := rdr.GetInodeFromPath("code-oss.desktop") if err != nil { t.Fatal(err)