diff --git a/internal/compression/gzip.go b/internal/compression/gzip.go index 98f3cd4..0a4e591 100644 --- a/internal/compression/gzip.go +++ b/internal/compression/gzip.go @@ -15,23 +15,21 @@ type gzipInit struct { //Gzip is a decompressor for gzip type compression. Uses zlib for compression and decompression type Gzip struct { - CompressionLevel int32 - HasCustomWindow bool - HasStrategies bool + gzipInit + HasCustomWindow bool + HasStrategies bool } //NewGzipCompressorWithOptions creates a new gzip compressor/decompressor with options read from the given reader. func NewGzipCompressorWithOptions(r io.Reader) (*Gzip, error) { var gzip Gzip - var init gzipInit - err := binary.Read(r, binary.LittleEndian, &init) + err := binary.Read(r, binary.LittleEndian, &gzip.gzipInit) if err != nil { return nil, err } - gzip.CompressionLevel = init.CompressionLevel //TODO: proper support for window size and strategies - gzip.HasCustomWindow = init.WindowSize != 15 - gzip.HasStrategies = init.Strategies != 0 && init.Strategies != 1 + gzip.HasCustomWindow = gzip.WindowSize != 15 + gzip.HasStrategies = gzip.Strategies != 0 && gzip.Strategies != 1 return &gzip, nil } @@ -58,9 +56,6 @@ func (g *Gzip) Compress(data []byte) ([]byte, error) { if err != nil { return nil, err } - err = wrt.Flush() - if err != nil { - return nil, err - } + wrt.Close() return buf.Bytes(), nil } diff --git a/internal/compression/lz4.go b/internal/compression/lz4.go index 2cbfd89..3f0e4d8 100644 --- a/internal/compression/lz4.go +++ b/internal/compression/lz4.go @@ -35,3 +35,21 @@ func (l *Lz4) Decompress(r io.Reader) ([]byte, error) { _, err := io.Copy(&buf, rdr) return buf.Bytes(), err } + +//Compress implements compression.Compress +func (l *Lz4) Compress(data []byte) ([]byte, error) { + var buf bytes.Buffer + w := lz4.NewWriter(&buf) + if l.HC { + err := w.Apply(lz4.CompressionLevelOption(lz4.Level9)) + if err != nil { + return nil, err + } + } + _, err := w.Write(data) + if err != nil { + return nil, err + } + w.Close() + return buf.Bytes(), nil +} diff --git a/internal/compression/lzma.go b/internal/compression/lzma.go index b9e7c9c..c5de987 100644 --- a/internal/compression/lzma.go +++ b/internal/compression/lzma.go @@ -23,3 +23,18 @@ func (l *Lzma) Decompress(rdr io.Reader) ([]byte, error) { } return buf.Bytes(), nil } + +//Compress implements compression.Compress +func (l *Lzma) Compress(data []byte) ([]byte, error) { + var buf bytes.Buffer + w, err := lzma.NewWriter(&buf) + if err != nil { + return nil, err + } + _, err = w.Write(data) + if err != nil { + return nil, err + } + w.Close() + return buf.Bytes(), nil +} diff --git a/internal/compression/xz.go b/internal/compression/xz.go index c860ef3..8f8fc58 100644 --- a/internal/compression/xz.go +++ b/internal/compression/xz.go @@ -53,3 +53,23 @@ func (x *Xz) Decompress(rdr io.Reader) ([]byte, error) { } return buf.Bytes(), nil } + +//Compress implements compression.Compress +func (x *Xz) Compress(data []byte) ([]byte, error) { + var buf bytes.Buffer + w, err := xz.NewWriter(&buf) + if err != nil { + return nil, err + } + w.DictCap = int(x.DictionarySize) + err = w.Verify() + if err != nil { + return nil, err + } + _, err = w.Write(data) + if err != nil { + return nil, err + } + w.Close() + return buf.Bytes(), nil +} diff --git a/internal/compression/zstd.go b/internal/compression/zstd.go index 38b88b1..074cc78 100644 --- a/internal/compression/zstd.go +++ b/internal/compression/zstd.go @@ -34,3 +34,18 @@ func (z *Zstd) Decompress(r io.Reader) ([]byte, error) { _, err = io.Copy(&buf, rdr) return buf.Bytes(), err } + +//Compress impelements compression.Compress +func (z *Zstd) Compress(data []byte) ([]byte, error) { + var buf bytes.Buffer + w, err := zstd.NewWriter(&buf, zstd.WithEncoderLevel(zstd.EncoderLevel(z.CompressionLevel))) + if err != nil { + return nil, err + } + _, err = w.Write(data) + if err != nil { + return nil, err + } + w.Close() + return buf.Bytes(), nil +} diff --git a/reader.go b/reader.go index 04c6851..9d45fcb 100644 --- a/reader.go +++ b/reader.go @@ -77,9 +77,6 @@ func NewSquashfsReader(r io.ReaderAt) (*Reader, error) { if err != nil { return nil, err } - if lz4.HC { - hasUnsupportedOptions = true - } rdr.decompressor = lz4 case ZstdCompression: zstd, err := compression.NewZstdCompressorWithOptions(io.NewSectionReader(rdr.r, int64(binary.Size(rdr.super)), 4)) diff --git a/writer.go b/writer.go index ce8f394..65a990d 100644 --- a/writer.go +++ b/writer.go @@ -7,6 +7,8 @@ import ( "os" "path" "strings" + + "github.com/CalebQ42/squashfs/internal/compression" ) type fileHolder struct { @@ -14,6 +16,8 @@ type fileHolder struct { path string name string symLocation string + UID int + GUID int folder bool symlink bool } @@ -21,19 +25,37 @@ type fileHolder struct { //Writer is used to creaste squashfs archives. Currently unusable //TODO: Make usable type Writer struct { + compressor compression.Compressor structure map[string][]*fileHolder symlinkTable map[string]string //[oldpath]newpath + superblock superblock compressionType int - allowErrors bool //AllowErrors allows errors when adding folders and their children. + allowErrors bool } -//NewWriter creates a new +//NewWriter creates a new with the default options (Gzip compression and allow errors) func NewWriter() (*Writer, error) { return NewWriterWithOptions(GzipCompression, true) } +//NewWriterWithOptions creates a new squashfs.Writer with the given options. +//compressionType can be of any types, except LZO (which this library doesn't have support for yet) +//allowErrors determines if, when adding folders, it allows errors encountered with it's sub-directories and instead logs the errors. func NewWriterWithOptions(compressionType int, allowErrors bool) (*Writer, error) { - + if compressionType < 0 || compressionType > 6 { + return nil, errors.New("Incorrect compression type") + } + if compressionType == 3 { + return nil, errors.New("LZO compression is not (currently) supported") + } + return &Writer{ + structure: map[string][]*fileHolder{ + "/": make([]*fileHolder, 0), + }, + symlinkTable: make(map[string]string), + compressionType: compressionType, + allowErrors: allowErrors, + }, nil } //AddFile attempts to add an os.File to the archive at it's root. @@ -83,7 +105,7 @@ func (w *Writer) AddFileTo(filepath string, file *os.File) error { return err } err = w.AddFileToFolder(holder.path+"/"+holder.name, fil) - if err != nil && !w.AllowErrors { + if err != nil && !w.allowErrors { for _, dir := range dirsAdded { w.Remove(dir) } @@ -92,7 +114,7 @@ func (w *Writer) AddFileTo(filepath string, file *os.File) error { log.Println("Error while adding", fil.Name()) log.Println(err) } - if !w.AllowErrors { + if !w.allowErrors { dirsAdded = append(dirsAdded, holder.path+"/"+holder.name) } } @@ -149,6 +171,7 @@ func (w *Writer) Remove(filepath string) bool { // //If this is not run before writing, you may end up with broken symlinks. func (w *Writer) FixSymlinks() error { + //TODO return errors.New("DON'T") } @@ -164,5 +187,6 @@ func (w *Writer) WriteToFilename(filepath string) error { //WriteTo attempts to write the archive to the given io.Writer. func (w *Writer) WriteTo(write io.Writer) (int64, error) { + //TODO return 0, errors.New("I SAID DON'T") } diff --git a/writer_compression_options.go b/writer_compression_options.go new file mode 100644 index 0000000..f02eda6 --- /dev/null +++ b/writer_compression_options.go @@ -0,0 +1,15 @@ +package squashfs + +//TODO: Allow settings the options + +// func (w *Writer) SetGzipOptions() error {} + +// func (w *Writer) SetLzmaOptions() error {} + +// func (w *Writer) SetLzoOptions() error {} + +// func (w *Writer) SetXzOptions() error {} + +// func (w *Writer) SetLz4Options() error {} + +// func (w *Writer) SetZstdOptions() error {}