diff --git a/compressionoptions.go b/compressionoptions.go index 5f2d881..b85b8f1 100644 --- a/compressionoptions.go +++ b/compressionoptions.go @@ -14,12 +14,13 @@ const ( zstdCompression ) -//TODO: implement decompress for each type of Options +//TODO: implement for each type of Options type CompressionOptions interface { Decompress(*io.SectionReader, int) ([]byte, error) DecompressCopy(*io.Reader, *io.Writer) (int, error) Compress(*io.SectionReader, int) ([]byte, error) CompressCopy(*io.Reader, *io.Writer) (int, error) + Reader(io.Reader) (*io.ReadCloser, error) } //TODO: Allow creation of options for compression. @@ -32,7 +33,7 @@ type gzipOptionsRaw struct { //GzipOptions is the options used for gzip compression. Backed by the raw format, with strategies parsed. type GzipOptions struct { - CompressionOptions //TODO: remove + CompressionOptions raw *gzipOptionsRaw DefaultStrategy bool FilteredStrategy bool @@ -41,9 +42,54 @@ type GzipOptions struct { FixedStretegy bool } -func NewGzipOptions(raw gzipOptionsRaw) GzipOptions { +type byteWriterReader struct { + byts []byte + offset int +} + +func newByteReadWrite(length int) *byteWriterReader { + return &byteWriterReader{ + byts: make([]byte, length), + offset: 0, + } +} + +func newByteReadWriteFromBytes(byts []byte) *byteWriterReader { + return &byteWriterReader{ + byts: byts, + offset: 0, + } +} + +func (bwr *byteWriterReader) getBytes() []byte { + return bwr.byts +} + +//Read reads the bytes. +func (bwr *byteWriterReader) Read(byt []byte) (int, error) { + if len(bwr.byts) < bwr.offset+len(byt) { + bytesWritten := len(bwr.byts) - bwr.offset + for i := 0; i < bytesWritten; i++ { + byt[i] = bwr.byts[i+bwr.offset] + } + return bytesWritten, io.EOF + } + for i := 0; i < len(byt); i++ { + byt[i] = bwr.byts[bwr.offset+i] + } + bwr.offset += len(byt) + return len(byt), nil +} + +//Write writes to the bytes. WILL expand to accept the incoming bytes. +func (bwr *byteWriterReader) Write(byts []byte) (int, error) { + bwr.byts = append(bwr.byts, byts...) + return len(byts), nil +} + +func NewGzipOptions(raw gzipOptionsRaw) *GzipOptions { //TODO: parse strategies - return GzipOptions{ + return &GzipOptions{ raw: &raw, } } @@ -54,21 +100,12 @@ func (gzipOp *GzipOptions) Decompress(rdr *io.SectionReader, blockSize int) ([]b if err != nil { return nil, err } - out := make([]byte, 0) - var tmp []byte - read := blockSize - for read == blockSize { - tmp = make([]byte, blockSize) - read, err = zlibRdr.Read(tmp) - if err != io.EOF { - return nil, err - } - if read < blockSize { - tmp = tmp[:read] - } - out = append(out, tmp...) + bytrw := newByteReadWrite(0) + _, err = io.Copy(bytrw, zlibRdr) + if err != nil { + return bytrw.byts, err } - return out, nil + return bytrw.byts, nil } func (gzipOp *GzipOptions) DecompressCopy(rdr *io.Reader, wrt *io.Writer) (int, error) { @@ -82,16 +119,26 @@ func (gzipOp *GzipOptions) DecompressCopy(rdr *io.Reader, wrt *io.Writer) (int, } func (gzipOp *GzipOptions) Compress(rdr *io.SectionReader, blockSize int) ([]byte, error) { - + bytWrt := newByteReadWrite(0) + zlibWrt := zlib.NewWriter(bytWrt) //TODO: allow setting level + defer zlibWrt.Close() + _, err := io.Copy(zlibWrt, rdr) + if err != nil { + return bytWrt.byts, err + } + return bytWrt.byts, nil } func (gzipOp *GzipOptions) CompressCopy(rdr *io.Reader, wrt *io.Writer) (int, error) { - zlibWrt, err := zlib.NewWriter(*wrt) //TODO: allow setting level + zlibWrt := zlib.NewWriter(*wrt) //TODO: allow setting level defer zlibWrt.Close() - if err != nil { - return 0, err - } + n, err := io.Copy(zlibWrt, *rdr) + return int(n), err +} +func (gzipOp *GzipOptions) Reader(rdr io.Reader) (*io.ReadCloser, error) { + read, err := zlib.NewReader(rdr) + return &read, err } type xzOptionsRaw struct { diff --git a/internal/inode/inode.go b/internal/inode/inode.go new file mode 100644 index 0000000..08b7906 --- /dev/null +++ b/internal/inode/inode.go @@ -0,0 +1,91 @@ +package inode + +type CommonHeader struct { + InodeType uint16 + Permissions uint16 + UID uint16 + GID uint16 + ModifiedTime uint32 + Number uint32 +} + +type BasicDirectory struct { + DirectoryIndex uint32 + HardLinks uint32 + DirectorySize uint16 + DirectoryOffset uint16 + ParentInodeNumber uint32 +} + +type ExtendedDirectoryInit struct { + HardLinks uint32 + DirectorySize uint32 + DirectoryIndex uint32 + ParentInodeNumber uint32 + IndexCount uint16 //one less then directory indexes following structure + DirectoryOffset uint16 + XattrIndex uint32 +} + +type ExtendedDirectory struct { + ExtendedDirectoryInit + //TODO indexes []DirectoryIndex +} + +type BasicFile struct { + BlockStart uint32 + FragmentIndex uint32 + FragmentOffset uint32 + Size uint32 + BlockSizes []uint32 +} + +type ExtendedFile struct { + BlockStart uint32 + Size uint32 + Sparse uint64 + HardLinks uint32 + FragmentIndex uint32 + FragmentOffset uint32 + XattrIndex uint32 + BlockSizes []uint32 +} + +type BasicSymlinkInit struct { + HardLinks uint32 + TargetPathSize uint32 +} + +type BasicSymlink struct { + BasicSymlinkInit + targetPath []byte //len is TargetPathSize +} + +type ExtendedSymlinkInit struct { + HardLinks uint32 + TargetPathSize uint32 +} + +type ExtendedSymlink struct { + targetPath []byte + XattrIndex uint32 +} + +type BasicDevice struct { + HardLinks uint32 + Device uint32 +} + +type ExtendedDevice struct { + BasicDevice + XattrIndex uint32 +} + +type BasicIPC struct { + HardLink uint32 +} + +type ExtendedIPC struct { + BasicIPC + XattrIndex uint32 +} diff --git a/squashfs.go b/squashfs.go index 9729626..a677a4d 100644 --- a/squashfs.go +++ b/squashfs.go @@ -33,28 +33,32 @@ func NewSquashfs(reader io.ReaderAt) (*Squashfs, error) { } flags := superblock.GetFlags() var compressionOptions CompressionOptions - if flags.CompressorOptions { - switch superblock.Compression { - case zlibCompression: + switch superblock.Compression { + case zlibCompression: + if flags.CompressorOptions { var gzipOpRaw gzipOptionsRaw err = binary.Read(&rdr, binary.LittleEndian, &gzipOpRaw) if err != nil { return nil, err } compressionOptions = NewGzipOptions(gzipOpRaw) - break - case xzCompression: + } else { + compressionOptions = NewGzipOptions(gzipOptionsRaw{}) + } + case xzCompression: + if flags.CompressorOptions { var xzOpRaw xzOptionsRaw err = binary.Read(&rdr, binary.LittleEndian, xzOpRaw) if err != nil { return nil, err } compressionOptions = NewXzOption(xzOpRaw) - break - default: - //TODO: all the compression options - return nil, errors.New("This type of compression isn't supported yet") + } else { + compressionOptions = NewXzOption(xzOptionsRaw{}) } + default: + //TODO: all the compression options + return nil, errors.New("This type of compression isn't supported yet") } //TODO: parse more info return &Squashfs{ @@ -70,14 +74,21 @@ func (s *Squashfs) GetFlags() SuperblockFlags { return s.super.GetFlags() } -//Metadata is a parsed metadata block -type Metadata struct { +//metadata is a parsed metadata block +type metadata struct { Compressed bool Size uint16 - Data *io.SectionReader + Data io.Reader } -func (s *Squashfs) parseNextMetadata() (*Metadata, error) { +func (m *metadata) close() { + switch m.Data.(type) { + case io.ReadCloser: + m.Data.(io.ReadCloser).Close() + } +} + +func (s *Squashfs) parseNextMetadata() (*metadata, error) { var metaHeader uint16 err := binary.Read(s.rdr, binary.LittleEndian, metaHeader) if err != nil { @@ -85,12 +96,38 @@ func (s *Squashfs) parseNextMetadata() (*Metadata, error) { } if metaHeader&0x8000 == 0x8000 { metaHeader = metaHeader &^ 0x8000 - //TODO: read compressed metadata - return nil, errors.New("Metadata is compressed, which is not implemented yet") + compressRead, err := s.compressionOptions.Reader(io.NewSectionReader(s.rdr, s.rdr.offset, int64(metaHeader))) + return &metadata{ + Compressed: true, + Size: metaHeader, + Data: *compressRead, + }, err } - return &Metadata{ + return &metadata{ Compressed: false, Size: metaHeader, - //TODO: Data: io.NewSectionReader(s.rdr, , metaHeader), + Data: io.NewSectionReader(s.rdr, s.rdr.offset, int64(metaHeader)), + }, nil +} + +func (s *Squashfs) parseMetadataAt(offset int64) (*metadata, error) { + var metaHeader uint16 + err := binary.Read(s.rdr, binary.LittleEndian, metaHeader) + if err != nil { + return nil, err + } + if metaHeader&0x8000 == 0x8000 { + metaHeader = metaHeader &^ 0x8000 + compressRead, err := s.compressionOptions.Reader(io.NewSectionReader(s.rdr, offset, int64(metaHeader))) + return &metadata{ + Compressed: true, + Size: metaHeader, + Data: *compressRead, + }, err + } + return &metadata{ + Compressed: false, + Size: metaHeader, + Data: io.NewSectionReader(s.rdr, offset, int64(metaHeader)), }, nil }