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 }