From e20213c3f7ca8cf8ea7a362c87af9e9b2a672935 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Sat, 28 Nov 2020 05:03:56 -0600 Subject: [PATCH] Reorganization. --- compression.go | 33 --------- file.go | 66 ++++++++++++++--- filereader.go | 4 +- internal/compression/types.go | 13 ++++ internal/compression/zlib.go | 24 +++++++ metadatareader.go => metadata.go | 11 +++ reader.go | 17 ++--- utils.go | 119 ------------------------------- 8 files changed, 117 insertions(+), 170 deletions(-) delete mode 100644 compression.go create mode 100644 internal/compression/types.go create mode 100644 internal/compression/zlib.go rename metadatareader.go => metadata.go (91%) delete mode 100644 utils.go diff --git a/compression.go b/compression.go deleted file mode 100644 index 28b15d4..0000000 --- a/compression.go +++ /dev/null @@ -1,33 +0,0 @@ -package squashfs - -import ( - "bytes" - "compress/zlib" - "io" -) - -//Decompressor is a squashfs decompressor interface. Allows for easy decompression no matter the type of compression. -type decompressor interface { - Decompress(io.Reader) ([]byte, error) -} - -type compressor interface { - Compress(io.Reader) ([]byte, error) -} - -//ZlibDecompressor is a decompressor for gzip type compression -type zlibDecompressor struct{} - -//Decompress reads the entirety of the given reader and returns it uncompressed as a byte slice. -func (z *zlibDecompressor) Decompress(r io.Reader) ([]byte, error) { - rdr, err := zlib.NewReader(r) - if err != nil { - return nil, err - } - var data bytes.Buffer - _, err = io.Copy(&data, rdr) - if err != nil { - return nil, err - } - return data.Bytes(), nil -} diff --git a/file.go b/file.go index 96fdb23..66aa225 100644 --- a/file.go +++ b/file.go @@ -11,11 +11,11 @@ import ( var ( //ErrNotDirectory is returned when you're trying to do directory things with a non-directory - ErrNotDirectory = errors.New("File is not a directory") + errNotDirectory = errors.New("File is not a directory") //ErrNotFile is returned when you're trying to do file things with a directory - ErrNotFile = errors.New("File is not a file") + errNotFile = errors.New("File is not a file") //ErrNotReading is returned when running functions that are only meant to be used when reading a squashfs - ErrNotReading = errors.New("Function only supported when reading a squashfs") + errNotReading = errors.New("Function only supported when reading a squashfs") ) //File is the main way to interact with files within squashfs, or when putting files into a squashfs. @@ -48,10 +48,10 @@ func (r *Reader) newFileFromDirEntry(entry *directory.Entry) (fil *File, err err func (f *File) GetChildren() (children []*File, err error) { children = make([]*File, 0) if f.r == nil { - return nil, ErrNotReading + return nil, errNotReading } if !f.IsDir() { - return nil, ErrNotDirectory + return nil, errNotDirectory } dir, err := f.r.readDirFromInode(f.in) if err != nil { @@ -76,10 +76,10 @@ func (f *File) GetChildren() (children []*File, err error) { func (f *File) GetChildrenRecursively() (children []*File, err error) { children = make([]*File, 0) if f.r == nil { - return nil, ErrNotReading + return nil, errNotReading } if !f.IsDir() { - return nil, ErrNotDirectory + return nil, errNotDirectory } chil, err := f.GetChildren() if err != nil { @@ -176,7 +176,7 @@ func (f *File) GetSymlinkFile() *File { //When reading, Close is safe to use, but any subsequent Read calls resets to the beginning of the file. func (f *File) Close() error { if f.IsDir() { - return ErrNotFile + return errNotFile } if closer, is := f.Reader.(io.Closer); is { closer.Close() @@ -199,3 +199,53 @@ func (f *File) Read(p []byte) (int, error) { } return f.Reader.Read(p) } + +//ReadDirFromInode returns a fully populated directory.Directory from a given inode.Inode. +//If the given inode is not a directory it returns an error. +func (r *Reader) readDirFromInode(i *inode.Inode) (*directory.Directory, error) { + var offset uint32 + var metaOffset uint16 + var size uint16 + switch i.Type { + case inode.BasicDirectoryType: + offset = i.Info.(inode.BasicDirectory).DirectoryIndex + metaOffset = i.Info.(inode.BasicDirectory).DirectoryOffset + size = i.Info.(inode.BasicDirectory).DirectorySize + case inode.ExtDirType: + offset = i.Info.(inode.ExtendedDirectory).Init.DirectoryIndex + metaOffset = i.Info.(inode.ExtendedDirectory).Init.DirectoryOffset + size = uint16(i.Info.(inode.ExtendedDirectory).Init.DirectorySize) + default: + return nil, errors.New("Not a directory inode") + } + br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset))) + if err != nil { + return nil, err + } + _, err = br.Seek(int64(metaOffset), io.SeekStart) + if err != nil { + return nil, err + } + dir, err := directory.NewDirectory(br, size) + if err != nil { + return dir, err + } + return dir, nil +} + +//GetInodeFromEntry returns the inode associated with a given directory.Entry +func (r *Reader) getInodeFromEntry(en *directory.Entry) (*inode.Inode, error) { + br, err := r.newMetadataReader(int64(r.super.InodeTableStart + uint64(en.Header.InodeOffset))) + if err != nil { + return nil, err + } + _, err = br.Seek(int64(en.Init.Offset), io.SeekStart) + if err != nil { + return nil, err + } + i, err := inode.ProcessInode(br, r.super.BlockSize) + if err != nil { + return nil, err + } + return i, nil +} diff --git a/filereader.go b/filereader.go index bf07f2d..b6111bd 100644 --- a/filereader.go +++ b/filereader.go @@ -22,7 +22,7 @@ type fileReader struct { var ( //ErrPathIsNotFile returns when trying to read from a file, but the given path is NOT a file. - ErrPathIsNotFile = errors.New("The given path is not a file") + errPathIsNotFile = errors.New("The given path is not a file") ) //ReadFile provides a squashfs.FileReader for the file at the given location. @@ -30,7 +30,7 @@ func (r *Reader) newFileReader(in *inode.Inode) (*fileReader, error) { var rdr fileReader rdr.in = in if in.Type != inode.BasicFileType && in.Type != inode.ExtFileType { - return nil, ErrPathIsNotFile + return nil, errPathIsNotFile } switch in.Type { case inode.BasicFileType: diff --git a/internal/compression/types.go b/internal/compression/types.go new file mode 100644 index 0000000..09b06eb --- /dev/null +++ b/internal/compression/types.go @@ -0,0 +1,13 @@ +package compression + +import "io" + +//Compressor is a squashfs decompressor interface. Allows for easy compression. +type Compressor interface { + Compress(io.Reader) ([]byte, error) +} + +//Decompressor is a squashfs decompressor interface. Allows for easy decompression no matter the type of compression. +type Decompressor interface { + Decompress(io.Reader) ([]byte, error) +} diff --git a/internal/compression/zlib.go b/internal/compression/zlib.go new file mode 100644 index 0000000..2a2fce8 --- /dev/null +++ b/internal/compression/zlib.go @@ -0,0 +1,24 @@ +package compression + +import ( + "bytes" + "compress/zlib" + "io" +) + +//Zlib is a decompressor for gzip type compression +type Zlib struct{} + +//Decompress reads the entirety of the given reader and returns it uncompressed as a byte slice. +func (z *Zlib) Decompress(r io.Reader) ([]byte, error) { + rdr, err := zlib.NewReader(r) + if err != nil { + return nil, err + } + var data bytes.Buffer + _, err = io.Copy(&data, rdr) + if err != nil { + return nil, err + } + return data.Bytes(), nil +} diff --git a/metadatareader.go b/metadata.go similarity index 91% rename from metadatareader.go rename to metadata.go index 5c6261a..5e8562f 100644 --- a/metadatareader.go +++ b/metadata.go @@ -52,6 +52,17 @@ func (s *Reader) newMetadataReaderFromInodeRef(ref uint64) (*metadataReader, err return br, nil } +//ProcessInodeRef processes an inode reference and returns two values +// +//The first value is the inode table offset. AKA, it's where the metadata block of the inode STARTS relative to the inode table. +// +//The second value is the offset of the inode, INSIDE of the metadata. +func processInodeRef(inodeRef uint64) (tableOffset uint64, metaOffset uint64) { + tableOffset = inodeRef >> 16 + metaOffset = inodeRef &^ 0xFFFFFFFF0000 + return +} + func (br *metadataReader) parseMetadata() error { var raw uint16 err := binary.Read(io.NewSectionReader(br.s.r, br.offset, 2), binary.LittleEndian, &raw) diff --git a/reader.go b/reader.go index 02f7085..13d8b9a 100644 --- a/reader.go +++ b/reader.go @@ -6,6 +6,7 @@ import ( "io" "math" + "github.com/CalebQ42/squashfs/internal/compression" "github.com/CalebQ42/squashfs/internal/inode" ) @@ -15,11 +16,11 @@ const ( var ( //ErrNoMagic is returned if the magic number in the superblock isn't correct. - ErrNoMagic = errors.New("Magic number doesn't match. Either isn't a squashfs or corrupted") + errNoMagic = errors.New("Magic number doesn't match. Either isn't a squashfs or corrupted") //ErrIncompatibleCompression is returned if the compression type in the superblock doesn't work. - ErrIncompatibleCompression = errors.New("Compression type unsupported") + errIncompatibleCompression = errors.New("Compression type unsupported") //ErrCompressorOptions is returned if compressor options is present. It's not currently supported. - ErrCompressorOptions = errors.New("Compressor options is not currently supported") + errCompressorOptions = errors.New("Compressor options is not currently supported") //ErrFragmentTableIssues is returned if there's trouble reading the fragment table when creating a reader. //When this is returned, the reader is still returned. errFragmentTableIssues = errors.New("Trouble while reading the fragment table") @@ -31,7 +32,7 @@ type Reader struct { r io.ReaderAt super superblock flags superblockFlags - decompressor decompressor + decompressor compression.Decompressor fragOffsets []uint64 } @@ -44,18 +45,18 @@ func NewSquashfsReader(r io.ReaderAt) (*Reader, error) { return nil, err } if rdr.super.Magic != magic { - return nil, ErrNoMagic + return nil, errNoMagic } rdr.flags = rdr.super.GetFlags() switch rdr.super.CompressionType { case gzipCompression: - rdr.decompressor = &zlibDecompressor{} + rdr.decompressor = &compression.Zlib{} default: - return nil, ErrIncompatibleCompression + return nil, errIncompatibleCompression } if rdr.flags.CompressorOptions { //TODO: parse compressor options - return nil, ErrCompressorOptions + return nil, errCompressorOptions } fragBlocks := int(math.Ceil(float64(rdr.super.FragCount) / 512.0)) if fragBlocks > 0 { diff --git a/utils.go b/utils.go deleted file mode 100644 index 24f2dbc..0000000 --- a/utils.go +++ /dev/null @@ -1,119 +0,0 @@ -package squashfs - -import ( - "errors" - "io" - "strings" - - "github.com/CalebQ42/squashfs/internal/directory" - "github.com/CalebQ42/squashfs/internal/inode" -) - -var ( - //ErrNotFound means that the given path is NOT present in the archive - ErrNotFound = errors.New("Path not found") -) - -//ProcessInodeRef processes an inode reference and returns two values -// -//The first value is the inode table offset. AKA, it's where the metadata block of the inode STARTS relative to the inode table. -// -//The second value is the offset of the inode, INSIDE of the metadata. -func processInodeRef(inodeRef uint64) (tableOffset uint64, metaOffset uint64) { - tableOffset = inodeRef >> 16 - metaOffset = inodeRef &^ 0xFFFFFFFF0000 - return -} - -//ReadDirFromInode returns a fully populated directory.Directory from a given inode.Inode. -//If the given inode is not a directory it returns an error. -func (r *Reader) readDirFromInode(i *inode.Inode) (*directory.Directory, error) { - var offset uint32 - var metaOffset uint16 - var size uint16 - switch i.Type { - case inode.BasicDirectoryType: - offset = i.Info.(inode.BasicDirectory).DirectoryIndex - metaOffset = i.Info.(inode.BasicDirectory).DirectoryOffset - size = i.Info.(inode.BasicDirectory).DirectorySize - case inode.ExtDirType: - offset = i.Info.(inode.ExtendedDirectory).Init.DirectoryIndex - metaOffset = i.Info.(inode.ExtendedDirectory).Init.DirectoryOffset - size = uint16(i.Info.(inode.ExtendedDirectory).Init.DirectorySize) - default: - return nil, errors.New("Not a directory inode") - } - br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset))) - if err != nil { - return nil, err - } - _, err = br.Seek(int64(metaOffset), io.SeekStart) - if err != nil { - return nil, err - } - dir, err := directory.NewDirectory(br, size) - if err != nil { - return dir, err - } - return dir, nil -} - -//GetInodeFromEntry returns the inode associated with a given directory.Entry -func (r *Reader) getInodeFromEntry(en *directory.Entry) (*inode.Inode, error) { - br, err := r.newMetadataReader(int64(r.super.InodeTableStart + uint64(en.Header.InodeOffset))) - if err != nil { - return nil, err - } - _, err = br.Seek(int64(en.Init.Offset), io.SeekStart) - if err != nil { - return nil, err - } - i, err := inode.ProcessInode(br, r.super.BlockSize) - if err != nil { - return nil, err - } - return i, nil -} - -//GetInodeFromPath returns the inode at the given path, relative to root. -//The given path can start with or without "/" -func (r *Reader) getInodeFromPath(path string) (*inode.Inode, error) { - path = strings.TrimSuffix(strings.TrimPrefix(path, "/"), "/") - pathDirs := strings.Split(path, "/") - rdr, err := r.newMetadataReaderFromInodeRef(r.super.RootInodeRef) - if err != nil { - return nil, err - } - curInodeDir, err := inode.ProcessInode(rdr, r.super.BlockSize) - if err != nil { - return nil, err - } - if path == "" { - return curInodeDir, nil - } - for depth := 0; depth < len(pathDirs); depth++ { - if curInodeDir.Type != inode.BasicDirectoryType && curInodeDir.Type != inode.ExtDirType { - return nil, ErrNotFound - } - dir, err := r.readDirFromInode(curInodeDir) - if err != nil { - return nil, err - } - for _, entry := range dir.Entries { - if entry.Name == pathDirs[depth] { - if depth == len(pathDirs)-1 { - in, err := r.getInodeFromEntry(&entry) - if err != nil { - return nil, err - } - return in, nil - } - curInodeDir, err = r.getInodeFromEntry(&entry) - if err != nil { - return nil, err - } - } - } - } - return nil, ErrNotFound -}