1 package squashfs 2 3 import ( 4 "compress/zlib" 5 "fmt" 6 "io" 7 "math/bits" 8 9 "vimagination.zapto.org/byteio" 10 ) 11 12 const ( 13 CompressorGZIP Compressor = 1 14 CompressorLZMA Compressor = 2 15 CompressorLZO Compressor = 3 16 CompressorXZ Compressor = 4 17 CompressorLZ4 Compressor = 5 18 CompressorZSTD Compressor = 6 19 20 minimumWindowSize = 8 21 maximumWindowSize = 15 22 23 maxStrategy = 21 24 25 maxAlgorithm = 4 26 27 lzoDefaultAlgorithm = 4 28 lzoDefaultCompressionLevel = 8 29 30 maxDictionarySize = 8192 31 32 maxZStdCompressionLevel = 22 33 34 maxFilters = 63 35 ) 36 37 type Compressor uint16 38 39 func (c Compressor) String() string { 40 switch c { 41 case CompressorGZIP: 42 return "gzip" 43 case CompressorLZMA: 44 return "lzma" 45 case CompressorLZO: 46 return "lzo" 47 case CompressorXZ: 48 return "xz" 49 case CompressorLZ4: 50 return "lz4" 51 case CompressorZSTD: 52 return "zstd" 53 } 54 55 return "unknown" 56 } 57 58 func (c Compressor) decompress(r io.Reader) (io.Reader, error) { 59 switch c { 60 case CompressorGZIP: 61 return zlib.NewReader(r) 62 default: 63 return nil, fmt.Errorf("%s: %w", c, ErrUnsupportedCompressor) 64 } 65 } 66 67 type compressedWriter interface { 68 io.Writer 69 Reset(io.Writer) 70 Flush() error 71 } 72 73 type CompressorOptions interface { 74 getCompressedWriter() (compressedWriter, error) 75 asCompressor() Compressor 76 isDefault() bool 77 writeTo(*byteio.StickyLittleEndianWriter) 78 } 79 80 func (c Compressor) parseOptions(hasOptionsFlag bool, ler *byteio.StickyLittleEndianReader) (CompressorOptions, error) { 81 switch c { 82 case CompressorGZIP: 83 if hasOptionsFlag { 84 return parseGZipOptions(ler) 85 } 86 87 return DefaultGzipOptions(), nil 88 case CompressorLZMA: 89 if hasOptionsFlag { 90 return nil, ErrNoCompressorOptions 91 } 92 93 return DefaultLZMAOptions(), nil 94 case CompressorLZO: 95 if hasOptionsFlag { 96 return parseLZOOptions(ler) 97 } 98 99 return DefaultLZOOptions(), nil 100 case CompressorXZ: 101 if hasOptionsFlag { 102 return parseXZOptions(ler) 103 } 104 105 return DefaultXZOptions(), nil 106 case CompressorLZ4: 107 return parseLZ4Options(ler) 108 case CompressorZSTD: 109 if hasOptionsFlag { 110 return parseZStdOptions(ler) 111 } 112 113 return DefaultZStdOptions(), nil 114 } 115 116 return nil, ErrInvalidCompressor 117 } 118 119 type GZipOptions struct { 120 CompressionLevel uint32 121 WindowSize uint16 122 Strategies uint16 123 } 124 125 func parseGZipOptions(ler *byteio.StickyLittleEndianReader) (*GZipOptions, error) { 126 compressionlevel := ler.ReadUint32() 127 if compressionlevel == 0 || compressionlevel > zlib.BestCompression { 128 return nil, ErrInvalidCompressionLevel 129 } 130 131 windowsize := ler.ReadUint16() 132 if windowsize < minimumWindowSize || windowsize > maximumWindowSize { 133 return nil, ErrInvalidWindowSize 134 } 135 136 strategies := ler.ReadUint16() 137 if strategies > maxStrategy { 138 return nil, ErrInvalidCompressionStrategies 139 } 140 141 return &GZipOptions{ 142 CompressionLevel: compressionlevel, 143 WindowSize: windowsize, 144 Strategies: strategies, 145 }, nil 146 } 147 148 func DefaultGzipOptions() *GZipOptions { 149 return &GZipOptions{ 150 CompressionLevel: zlib.BestCompression, 151 WindowSize: maximumWindowSize, 152 } 153 } 154 155 func (g *GZipOptions) getCompressedWriter() (compressedWriter, error) { 156 return zlib.NewWriterLevel(nil, int(g.CompressionLevel)) 157 } 158 159 func (GZipOptions) asCompressor() Compressor { 160 return CompressorGZIP 161 } 162 163 func (g *GZipOptions) isDefault() bool { 164 return g.CompressionLevel == zlib.BestCompression && g.WindowSize == maximumWindowSize 165 } 166 167 func (g *GZipOptions) writeTo(w *byteio.StickyLittleEndianWriter) { 168 w.WriteUint32(g.CompressionLevel) 169 w.WriteUint16(g.WindowSize) 170 w.WriteUint16(g.Strategies) 171 } 172 173 type LZMAOptions struct{} 174 175 func DefaultLZMAOptions() LZMAOptions { 176 return LZMAOptions{} 177 } 178 179 func (LZMAOptions) getCompressedWriter() (compressedWriter, error) { 180 return nil, ErrUnsupportedCompressor 181 } 182 183 func (LZMAOptions) asCompressor() Compressor { 184 return CompressorLZMA 185 } 186 187 func (LZMAOptions) isDefault() bool { 188 return true 189 } 190 191 func (LZMAOptions) writeTo(_ *byteio.StickyLittleEndianWriter) {} 192 193 type LZOOptions struct { 194 Algorithm uint32 195 CompressionLevel uint32 196 } 197 198 func parseLZOOptions(ler *byteio.StickyLittleEndianReader) (*LZOOptions, error) { 199 algorithm := ler.ReadUint32() 200 if algorithm > maxAlgorithm { 201 return nil, ErrInvalidCompressionAlgorithm 202 } 203 204 compressionlevel := ler.ReadUint32() 205 if compressionlevel > zlib.BestCompression || algorithm != 4 && compressionlevel != 0 { 206 return nil, ErrInvalidCompressionLevel 207 } 208 209 return &LZOOptions{ 210 Algorithm: algorithm, 211 CompressionLevel: compressionlevel, 212 }, nil 213 } 214 215 func DefaultLZOOptions() *LZOOptions { 216 return &LZOOptions{ 217 Algorithm: lzoDefaultAlgorithm, 218 CompressionLevel: lzoDefaultCompressionLevel, 219 } 220 } 221 222 func (l *LZOOptions) isDefault() bool { 223 return l.CompressionLevel == lzoDefaultCompressionLevel && l.Algorithm == lzoDefaultAlgorithm 224 } 225 226 func (LZOOptions) getCompressedWriter() (compressedWriter, error) { 227 return nil, ErrUnsupportedCompressor 228 } 229 230 func (LZOOptions) asCompressor() Compressor { 231 return CompressorLZO 232 } 233 234 func (l *LZOOptions) writeTo(w *byteio.StickyLittleEndianWriter) { 235 w.WriteUint32(l.Algorithm) 236 w.WriteUint32(l.CompressionLevel) 237 } 238 239 type XZOptions struct { 240 DictionarySize uint32 241 Filters uint32 242 } 243 244 func parseXZOptions(ler *byteio.StickyLittleEndianReader) (*XZOptions, error) { 245 dictionarysize := ler.ReadUint32() 246 247 lead, trail := bits.LeadingZeros32(dictionarysize), bits.TrailingZeros32(dictionarysize) 248 if dictionarysize < maxDictionarySize || 32-trail-lead > 2 { 249 return nil, ErrInvalidDictionarySize 250 } 251 252 filters := ler.ReadUint32() 253 if filters > maxFilters { 254 return nil, ErrInvalidFilters 255 } 256 257 return &XZOptions{ 258 DictionarySize: dictionarysize, 259 Filters: filters, 260 }, nil 261 } 262 263 func DefaultXZOptions() *XZOptions { 264 return &XZOptions{ 265 DictionarySize: maxDictionarySize, 266 } 267 } 268 269 func (XZOptions) getCompressedWriter() (compressedWriter, error) { 270 return nil, ErrUnsupportedCompressor 271 } 272 273 func (XZOptions) asCompressor() Compressor { 274 return CompressorXZ 275 } 276 277 func (x *XZOptions) isDefault() bool { 278 return x.DictionarySize == maxDictionarySize && x.Filters == 0 279 } 280 281 func (x *XZOptions) writeTo(w *byteio.StickyLittleEndianWriter) { 282 w.WriteUint32(x.DictionarySize) 283 w.WriteUint32(x.Filters) 284 } 285 286 type LZ4Options struct { 287 Version uint32 288 Flags uint32 289 } 290 291 func parseLZ4Options(ler *byteio.StickyLittleEndianReader) (*LZ4Options, error) { 292 if ler.ReadUint32() != 1 { 293 return nil, ErrInvalidCompressorVersion 294 } 295 296 flags := ler.ReadUint32() 297 if flags > 1 { 298 return nil, ErrInvalidCompressorFlags 299 } 300 301 return &LZ4Options{ 302 Version: 1, 303 Flags: flags, 304 }, nil 305 } 306 307 func (LZ4Options) getCompressedWriter() (compressedWriter, error) { 308 return nil, ErrUnsupportedCompressor 309 } 310 311 func (LZ4Options) asCompressor() Compressor { 312 return CompressorLZ4 313 } 314 315 func (LZ4Options) isDefault() bool { 316 return false 317 } 318 319 func (l *LZ4Options) writeTo(w *byteio.StickyLittleEndianWriter) { 320 w.WriteUint32(l.Version) 321 w.WriteUint32(l.Flags) 322 } 323 324 type ZStdOptions struct { 325 CompressionLevel uint32 326 } 327 328 func parseZStdOptions(ler *byteio.StickyLittleEndianReader) (*ZStdOptions, error) { 329 compressionlevel := ler.ReadUint32() 330 if compressionlevel == 0 || compressionlevel > maxZStdCompressionLevel { 331 return nil, ErrInvalidCompressionLevel 332 } 333 334 return &ZStdOptions{ 335 CompressionLevel: compressionlevel, 336 }, nil 337 } 338 339 func DefaultZStdOptions() *ZStdOptions { 340 return &ZStdOptions{ 341 CompressionLevel: zlib.BestCompression, 342 } 343 } 344 345 func (ZStdOptions) getCompressedWriter() (compressedWriter, error) { 346 return nil, ErrUnsupportedCompressor 347 } 348 349 func (ZStdOptions) asCompressor() Compressor { 350 return CompressorZSTD 351 } 352 353 func (z *ZStdOptions) isDefault() bool { 354 return z.CompressionLevel == zlib.BestCompression 355 } 356 357 func (z *ZStdOptions) writeTo(w *byteio.StickyLittleEndianWriter) { 358 w.WriteUint32(z.CompressionLevel) 359 } 360