github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/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  	"log"
    11  	"path/filepath"
    12  )
    13  
    14  var (
    15  	formatMap = make(map[string]RecordFormat)
    16  	formats   []string
    17  	Debug     = func(string, ...interface{}) {}
    18  )
    19  
    20  func AddFormat(name string, f RecordFormat) {
    21  	if _, ok := formatMap[name]; ok {
    22  		log.Fatalf("cpio: two requests for format %s", name)
    23  	}
    24  
    25  	formatMap[name] = f
    26  	formats = append(formats, name)
    27  }
    28  
    29  func Format(name string) (Archiver, error) {
    30  	op, ok := formatMap[name]
    31  	if !ok {
    32  		return Archiver{}, fmt.Errorf("Format %v is not one of %v", name, formats)
    33  	}
    34  	return Archiver{op}, nil
    35  }
    36  
    37  type Archiver struct {
    38  	RecordFormat
    39  }
    40  
    41  func (a Archiver) Reader(r io.ReaderAt) Reader {
    42  	return Reader{a.RecordFormat.Reader(r)}
    43  }
    44  
    45  func (a Archiver) Writer(w io.Writer) Writer {
    46  	return Writer{rw: a.RecordFormat.Writer(w), alreadyWritten: make(map[string]struct{})}
    47  }
    48  
    49  type Reader struct {
    50  	rr RecordReader
    51  }
    52  
    53  func (r Reader) ReadRecord() (Record, error) {
    54  	rec, err := r.rr.ReadRecord()
    55  	if err != nil {
    56  		return Record{}, err
    57  	}
    58  	// The end of a CPIO archive is marked by a record whose name is "TRAILER!!!".
    59  	if rec.Name == Trailer {
    60  		return Record{}, io.EOF
    61  	}
    62  	return rec, nil
    63  }
    64  
    65  func (r Reader) ReadRecords() ([]Record, error) {
    66  	var files []Record
    67  	for {
    68  		f, err := r.ReadRecord()
    69  		if err == io.EOF {
    70  			return files, nil
    71  		}
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  		files = append(files, f)
    76  	}
    77  }
    78  
    79  type Writer struct {
    80  	rw RecordWriter
    81  
    82  	// There seems to be no harm done in stripping
    83  	// duplicate names when the record is written,
    84  	// and lots of harm done if we don't do it.
    85  	alreadyWritten map[string]struct{}
    86  }
    87  
    88  func (w Writer) WriteRecord(rec Record) error {
    89  	// we do NOT write records with absolute paths.
    90  	if filepath.IsAbs(rec.Name) {
    91  		// There's no constant that means "root".
    92  		// PathSeparator is not really quite right.
    93  		rel, err := filepath.Rel("/", rec.Name)
    94  		if err != nil {
    95  			return fmt.Errorf("Can't make %s relative to /?", rec.Name)
    96  		}
    97  		rec.Name = rel
    98  	}
    99  
   100  	if _, ok := w.alreadyWritten[rec.Name]; ok {
   101  		return nil
   102  	}
   103  	w.alreadyWritten[rec.Name] = struct{}{}
   104  	return w.rw.WriteRecord(rec)
   105  }
   106  
   107  // WriteRecords writes multiple records.
   108  func (w Writer) WriteRecords(files []Record) error {
   109  	for _, f := range files {
   110  		if err := w.WriteRecord(f); err != nil {
   111  			return fmt.Errorf("WriteRecords: writing %q got %v", f.Info.Name, err)
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  // WriteTrailer writes the trailer record.
   118  func (w Writer) WriteTrailer() error {
   119  	return w.WriteRecord(TrailerRecord)
   120  }
   121  
   122  // Concat reads files from r one at a time, and writes them to w.
   123  func (w Writer) Concat(r Reader, transform func(Record) Record) error {
   124  	// Read and write one file at a time. We don't want all that in memory.
   125  	for {
   126  		f, err := r.ReadRecord()
   127  		if err == io.EOF {
   128  			return nil
   129  		}
   130  		if err != nil {
   131  			return err
   132  		}
   133  		if transform != nil {
   134  			f = transform(f)
   135  		}
   136  		if err := w.WriteRecord(f); err != nil {
   137  			return err
   138  		}
   139  	}
   140  }
   141  
   142  // MakeReproducible changes any fields in a Record such that
   143  // if we run cpio again, with the same files presented to it
   144  // in the same order, and those files have unchanged contents,
   145  // the cpio file it produces will be bit-for-bit
   146  // identical. This is an essential property for firmware-embedded
   147  // payloads.
   148  func MakeReproducible(file Record) Record {
   149  	file.MTime = 0
   150  	return file
   151  }
   152  
   153  func MakeAllReproducible(files []Record) {
   154  	for i := range files {
   155  		files[i] = MakeReproducible(files[i])
   156  	}
   157  }