github.com/jlowellwofford/u-root@v1.0.0/pkg/cpio/cpio.go (about)

     1  // Copyright 2013-2017 the u-root 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 cpio
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"path/filepath"
    11  )
    12  
    13  var (
    14  	formatMap = make(map[string]RecordFormat)
    15  	Debug     = func(string, ...interface{}) {}
    16  )
    17  
    18  // A RecordReader reads one record from an archive.
    19  type RecordReader interface {
    20  	ReadRecord() (Record, error)
    21  }
    22  
    23  // A RecordWriter writes on record to an archive.
    24  type RecordWriter interface {
    25  	WriteRecord(Record) error
    26  }
    27  
    28  // A RecordFormat gives readers and writers for dealing with archives.
    29  type RecordFormat interface {
    30  	Reader(r io.ReaderAt) RecordReader
    31  	Writer(w io.Writer) RecordWriter
    32  }
    33  
    34  // Format returns the RecordFormat with that name, if it exists.
    35  func Format(name string) (RecordFormat, error) {
    36  	op, ok := formatMap[name]
    37  	if !ok {
    38  		return nil, fmt.Errorf("%q is not in cpio format map %v", name, formatMap)
    39  	}
    40  	return op, nil
    41  }
    42  
    43  // EOFReader is a RecordReader that converts the Trailer record to io.EOF.
    44  type EOFReader struct {
    45  	RecordReader
    46  }
    47  
    48  // ReadRecord implements RecordReader.
    49  //
    50  // ReadRecord returns io.EOF when the record name is TRAILER!!!.
    51  func (r EOFReader) ReadRecord() (Record, error) {
    52  	rec, err := r.RecordReader.ReadRecord()
    53  	if err != nil {
    54  		return Record{}, err
    55  	}
    56  	// The end of a CPIO archive is marked by a record whose name is
    57  	// "TRAILER!!!".
    58  	if rec.Name == Trailer {
    59  		return Record{}, io.EOF
    60  	}
    61  	return rec, nil
    62  }
    63  
    64  // DedupWriter is a RecordWriter that does not write more than one record with
    65  // the same path.
    66  //
    67  // There seems to be no harm done in stripping duplicate names when the record
    68  // is written, and lots of harm done if we don't do it.
    69  type DedupWriter struct {
    70  	rw RecordWriter
    71  
    72  	// alreadyWritten keeps track of paths already written to rw.
    73  	alreadyWritten map[string]struct{}
    74  }
    75  
    76  // NewDedupWriter returns a new deduplicating rw.
    77  func NewDedupWriter(rw RecordWriter) RecordWriter {
    78  	return &DedupWriter{
    79  		rw:             rw,
    80  		alreadyWritten: make(map[string]struct{}),
    81  	}
    82  }
    83  
    84  // Passthrough copies from a RecordReader to a RecordWriter
    85  // It processes one record at a time to minimize the memory footprint.
    86  func Passthrough(r RecordReader, w RecordWriter) error {
    87  	if err := Concat(w, r, nil); err != nil {
    88  		return err
    89  	}
    90  	if err := WriteTrailer(w); err != nil {
    91  		return err
    92  	}
    93  	return nil
    94  }
    95  
    96  // WriteRecord implements RecordWriter.
    97  //
    98  // If rec.Name was already seen once before, it will not be written again and
    99  // WriteRecord returns nil.
   100  func (dw *DedupWriter) WriteRecord(rec Record) error {
   101  	rec.Name = Normalize(rec.Name)
   102  
   103  	if _, ok := dw.alreadyWritten[rec.Name]; ok {
   104  		return nil
   105  	}
   106  	dw.alreadyWritten[rec.Name] = struct{}{}
   107  	return dw.rw.WriteRecord(rec)
   108  }
   109  
   110  // WriteRecords writes multiple records.
   111  func WriteRecords(w RecordWriter, files []Record) error {
   112  	for _, f := range files {
   113  		if err := w.WriteRecord(f); err != nil {
   114  			return fmt.Errorf("WriteRecords: writing %q got %v", f.Info.Name, err)
   115  		}
   116  	}
   117  	return nil
   118  }
   119  
   120  // WriteTrailer writes the trailer record.
   121  func WriteTrailer(w RecordWriter) error {
   122  	return w.WriteRecord(TrailerRecord)
   123  }
   124  
   125  // Concat reads files from r one at a time, and writes them to w.
   126  func Concat(w RecordWriter, r RecordReader, transform func(Record) Record) error {
   127  	// Read and write one file at a time. We don't want all that in memory.
   128  	for {
   129  		f, err := r.ReadRecord()
   130  		if err == io.EOF {
   131  			return nil
   132  		}
   133  		if err != nil {
   134  			return err
   135  		}
   136  		if transform != nil {
   137  			f = transform(f)
   138  		}
   139  		if err := w.WriteRecord(f); err != nil {
   140  			return err
   141  		}
   142  	}
   143  }
   144  
   145  // Archive implements RecordWriter and is an in-memory list of files.
   146  //
   147  // Archive.Reader returns a RecordReader for this archive.
   148  type Archive struct {
   149  	// Files is a map of relative archive path -> record.
   150  	Files map[string]Record
   151  
   152  	// Order is a list of relative archive paths and represents the order
   153  	// in which Files were added.
   154  	Order []string
   155  }
   156  
   157  // InMemArchive returns an in-memory file archive.
   158  func InMemArchive() *Archive {
   159  	return &Archive{
   160  		Files: make(map[string]Record),
   161  	}
   162  }
   163  
   164  // WriteRecord implements RecordWriter and adds a record to the archive.
   165  func (a *Archive) WriteRecord(r Record) error {
   166  	r.Name = Normalize(r.Name)
   167  	a.Files[r.Name] = r
   168  	a.Order = append(a.Order, r.Name)
   169  	return nil
   170  }
   171  
   172  type archiveReader struct {
   173  	a   *Archive
   174  	pos int
   175  }
   176  
   177  // Reader returns a RecordReader for the archive.
   178  func (a *Archive) Reader() RecordReader {
   179  	return &EOFReader{&archiveReader{a: a}}
   180  }
   181  
   182  // ReadRecord implements RecordReader.
   183  func (ar *archiveReader) ReadRecord() (Record, error) {
   184  	if ar.pos >= len(ar.a.Order) {
   185  		return Record{}, io.EOF
   186  	}
   187  
   188  	path := ar.a.Order[ar.pos]
   189  	ar.pos++
   190  	return ar.a.Files[path], nil
   191  }
   192  
   193  // Contains returns true if a record matching r is in the archive.
   194  func (a *Archive) Contains(r Record) bool {
   195  	r.Name = Normalize(r.Name)
   196  	if s, ok := a.Files[r.Name]; ok {
   197  		return Equal(r, s)
   198  	}
   199  	return false
   200  }
   201  
   202  // ReadArchive reads the entire archive in-memory and makes it accessible by
   203  // paths.
   204  func ReadArchive(rr RecordReader) (*Archive, error) {
   205  	a := &Archive{
   206  		Files: make(map[string]Record),
   207  	}
   208  	err := ForEachRecord(rr, func(r Record) error {
   209  		return a.WriteRecord(r)
   210  	})
   211  	return a, err
   212  }
   213  
   214  // ReadAllRecords returns all records in `r` in the order in which they were
   215  // read.
   216  func ReadAllRecords(rr RecordReader) ([]Record, error) {
   217  	var files []Record
   218  	err := ForEachRecord(rr, func(r Record) error {
   219  		files = append(files, r)
   220  		return nil
   221  	})
   222  	return files, err
   223  }
   224  
   225  // ForEachRecord reads every record from r and applies f.
   226  func ForEachRecord(rr RecordReader, fun func(Record) error) error {
   227  	for {
   228  		rec, err := rr.ReadRecord()
   229  		switch err {
   230  		case io.EOF:
   231  			return nil
   232  
   233  		case nil:
   234  			if err := fun(rec); err != nil {
   235  				return err
   236  			}
   237  
   238  		default:
   239  			return err
   240  		}
   241  	}
   242  }
   243  
   244  // Normalize normalizes path to be relative to /.
   245  func Normalize(path string) string {
   246  	if filepath.IsAbs(path) {
   247  		rel, err := filepath.Rel("/", path)
   248  		if err != nil {
   249  			panic("absolute filepath must be relative to /")
   250  		}
   251  		return rel
   252  	}
   253  	return path
   254  }
   255  
   256  // MakeReproducible changes any fields in a Record such that if we run cpio
   257  // again, with the same files presented to it in the same order, and those
   258  // files have unchanged contents, the cpio file it produces will be bit-for-bit
   259  // identical. This is an essential property for firmware-embedded payloads.
   260  func MakeReproducible(r Record) Record {
   261  	r.Ino = 0
   262  	r.Name = Normalize(r.Name)
   263  	r.MTime = 0
   264  	r.UID = 0
   265  	r.GID = 0
   266  	return r
   267  }
   268  
   269  // MakeAllReproducible makes all given records reproducible as in
   270  // MakeReproducible.
   271  func MakeAllReproducible(files []Record) {
   272  	for i := range files {
   273  		files[i] = MakeReproducible(files[i])
   274  	}
   275  }