github.com/linuxboot/fiano@v1.2.0/pkg/compression/systemlzma.go (about)

     1  // Copyright 2018 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package compression
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"os/exec"
    11  )
    12  
    13  // SystemLZMA implements Compression and calls out to the system's compressor
    14  // (except for Decode which uses the Go-based decompressor). The sytem's
    15  // compressor is typically faster and generates smaller files than the Go-based
    16  // implementation.
    17  type SystemLZMA struct {
    18  	xzPath string
    19  }
    20  
    21  // Name returns the type of compression employed.
    22  func (c *SystemLZMA) Name() string {
    23  	return "LZMA"
    24  }
    25  
    26  // Decode decodes a byte slice of LZMA data.
    27  func (c *SystemLZMA) Decode(encodedData []byte) ([]byte, error) {
    28  	// When the xz command compresses, it stores an End-Of-Stream (EOS)
    29  	// marker at the end and 0xFFFFFFFFFFFFFFFF in the header. EDK2's
    30  	// decompressor is primitive and will try to allocate
    31  	// 0xFFFFFFFFFFFFFFFF bytes and fail. So, the Encode function writes
    32  	// the size in the header which works for EDK2's tool. Unfortunately,
    33  	// xz considers an lzma file which has both a valid size and EOS corrupt,
    34  	// but will still decompress it and return exit status 1 (false
    35  	// positive). We simply use the Go decompressor despite being slow.
    36  	return (&LZMA{}).Decode(encodedData)
    37  }
    38  
    39  // Encode encodes a byte slice with LZMA.
    40  func (c *SystemLZMA) Encode(decodedData []byte) ([]byte, error) {
    41  	cmd := exec.Command(c.xzPath, "--format=lzma", "-7", "--stdout")
    42  	cmd.Stdin = bytes.NewBuffer(decodedData)
    43  	encodedData, err := cmd.Output()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	// Quoting the XZ Utils manpage:
    49  	//
    50  	//     xz supports decompressing .lzma files with or without
    51  	//     end-of-payload marker, but all .lzma files created by xz will
    52  	//     use end-of-payload marker and have uncompressed size marked as
    53  	//     unknown in the .lzma header. This may be a problem in some
    54  	//     uncommon situations. For example, a .lzma decompressor in an
    55  	//     embedded device might work only with files that have known
    56  	//     uncompressed size.
    57  	//
    58  	// This also affects some UEFI implementations, so the size must be
    59  	// written to the header.
    60  	buf := &bytes.Buffer{}
    61  	if err := binary.Write(buf, binary.LittleEndian, uint64(len(decodedData))); err != nil {
    62  		return nil, err
    63  	}
    64  	copy(encodedData[5:5+8], buf.Bytes())
    65  	return encodedData, nil
    66  }