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 }