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