github.com/linuxboot/fiano@v1.2.0/pkg/cbfs/file.go (about)

     1  // Copyright 2018-2021 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 cbfs
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  
    15  	"github.com/linuxboot/fiano/pkg/compression"
    16  )
    17  
    18  var CbfsHeaderMagicNotFound = errors.New("CBFS header magic doesn't match")
    19  
    20  func (f *File) MarshalJSON() ([]byte, error) {
    21  	return json.Marshal(mFile{
    22  		Name:  f.Name,
    23  		Start: f.RecordStart,
    24  		Size:  f.FileHeader.Size,
    25  		Type:  f.FileHeader.Type.String(),
    26  	})
    27  }
    28  
    29  // NewFile reads in the CBFS file at current offset
    30  // On success it seeks to the end of the file.
    31  // On error the current offset withing the ReadSeeker is undefined.
    32  func NewFile(r io.ReadSeeker) (*File, error) {
    33  	var f File
    34  	off, err := r.Seek(0, io.SeekCurrent)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	f.RecordStart = uint32(off)
    39  
    40  	err = Read(r, &f.FileHeader)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	if string(f.Magic[:]) != FileMagic {
    45  		return nil, CbfsHeaderMagicNotFound
    46  	}
    47  	Debug("Found CBFS file at %#02x is %v type %v", f.RecordStart, f, f.Type)
    48  
    49  	var nameSize uint32
    50  	if f.AttrOffset == 0 {
    51  		nameSize = f.SubHeaderOffset - uint32(binary.Size(FileHeader{}))
    52  	} else {
    53  		nameSize = f.AttrOffset - uint32(binary.Size(FileHeader{}))
    54  	}
    55  	if err := ReadName(r, &f, nameSize); err != nil {
    56  		return nil, err
    57  	}
    58  	if err := ReadAttributes(r, &f); err != nil {
    59  		return nil, err
    60  	}
    61  	if err := ReadData(r, &f); err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return &f, nil
    66  }
    67  
    68  // ReadName reads the variable length CBFS name.
    69  func ReadName(r io.Reader, f *File, size uint32) error {
    70  	b := make([]byte, size)
    71  	n, err := r.Read(b)
    72  	if err != nil {
    73  		Debug("ReadName failed:%v", err)
    74  		return err
    75  	}
    76  	fname := cleanString(string(b))
    77  	Debug("ReadName gets '%s' (%#02x)", fname, b)
    78  	if n != len(b) {
    79  		err = fmt.Errorf("ReadName: got %d, want %d for name", n, len(b))
    80  		Debug("ReadName short: %v", err)
    81  		return err
    82  	}
    83  	// discard trailing NULLs
    84  	z := bytes.Split(b, []byte{0})
    85  	Debug("ReadName stripped: '%s'", z)
    86  	f.Name = string(z[0])
    87  	return nil
    88  }
    89  
    90  // ReadAttributes reads the variable length CBFS attribute list.
    91  func ReadAttributes(r io.Reader, f *File) error {
    92  	if f.AttrOffset == 0 {
    93  		return nil
    94  	}
    95  
    96  	b := make([]byte, f.SubHeaderOffset-f.AttrOffset)
    97  	n, err := r.Read(b)
    98  	if err != nil {
    99  		Debug("ReadAttributes failed:%v", err)
   100  		return err
   101  	}
   102  	Debug("ReadAttributes gets %#02x", b)
   103  	if n != len(b) {
   104  		err = fmt.Errorf("ReadAttributes: got %d, want %d for name", n, len(b))
   105  		Debug("ReadAttributes short: %v", err)
   106  		return err
   107  	}
   108  	f.Attr = b
   109  	return nil
   110  }
   111  
   112  // ReadData reads the variable length CBFS file data.
   113  func ReadData(r io.ReadSeeker, f *File) error {
   114  	Debug("ReadData: Seek to %#x", int64(f.RecordStart+f.SubHeaderOffset))
   115  	if _, err := r.Seek(int64(f.RecordStart+f.SubHeaderOffset), io.SeekStart); err != nil {
   116  		return err
   117  	}
   118  	Debug("ReadData: read %#x", f.Size)
   119  	b := make([]byte, f.Size)
   120  	n, err := r.Read(b)
   121  	if err != nil {
   122  		Debug("ReadData failed:%v", err)
   123  		return err
   124  	}
   125  	f.FData = b
   126  	Debug("ReadData gets %#02x", n)
   127  	return nil
   128  }
   129  
   130  // FindAttribute returns the attribute with given tag as
   131  // []byte. It has the size as specified by the tag.
   132  // Returns an error if not found or could not read in total.
   133  func (f *File) FindAttribute(t Tag) ([]byte, error) {
   134  	buf := bytes.NewReader(f.Attr)
   135  	generic := FileAttr{}
   136  
   137  	for {
   138  		if err := binary.Read(buf, Endian, &generic); err != nil {
   139  			return nil, err
   140  		}
   141  		if generic.Tag == uint32(Unused) || generic.Tag == uint32(Unused2) {
   142  			return nil, fmt.Errorf("end tag found")
   143  		}
   144  		// Validate input
   145  		if generic.Size < uint32(binary.Size(generic)) || generic.Size == 0xffffffff {
   146  			return nil, fmt.Errorf("tag is malformed, aborting")
   147  		}
   148  		Debug("FindAttribute: Found attribute with tag %x", generic.Tag)
   149  
   150  		if Tag(generic.Tag) == t {
   151  			_, _ = buf.Seek(-int64(binary.Size(generic)), io.SeekCurrent)
   152  
   153  			ret := make([]byte, generic.Size)
   154  			err := binary.Read(buf, Endian, &ret)
   155  			return ret, err
   156  		} else {
   157  			_, err := buf.Seek(int64(generic.Size)-int64(binary.Size(generic)), io.SeekCurrent)
   158  			if err != nil {
   159  				return nil, err
   160  			}
   161  		}
   162  	}
   163  }
   164  
   165  // Compression returns the algorithm used to compress FData.
   166  // If no compression attribute is found or on error it returns 'None'
   167  func (f *File) Compression() Compression {
   168  	cattr, err := f.FindAttribute(Compressed)
   169  	if err != nil {
   170  		Debug("Compression: No compression tag found: %v", err)
   171  		return None
   172  	}
   173  
   174  	comp := FileAttrCompression{}
   175  	if err := binary.Read(bytes.NewBuffer(cattr), Endian, &comp); err != nil {
   176  		Debug("Compression: failed to read compression tag: %v", err)
   177  		return None
   178  	}
   179  	return comp.Compression
   180  }
   181  
   182  // Decompress returns the decompressed FData
   183  // If FData is not compressed it returns FData
   184  func (f *File) Decompress() ([]byte, error) {
   185  	c := f.Compression()
   186  	if c == None {
   187  		return f.FData, nil
   188  	} else if c == LZMA {
   189  		compressor := compression.LZMA{}
   190  		return compressor.Decode(f.FData)
   191  	} else if c == LZ4 {
   192  		compressor := compression.LZ4{}
   193  		return compressor.Decode(f.FData)
   194  	}
   195  	return nil, fmt.Errorf("Unknown compression")
   196  }