github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/cpio/archive.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 "io" 9 "strings" 10 ) 11 12 // Archive is an in-memory list of files. 13 // 14 // Archive itself is a RecordWriter, and Archive.Reader() returns a new 15 // RecordReader for the archive starting from the first file. 16 type Archive struct { 17 // Files is a map of relative archive path -> record. 18 Files map[string]Record 19 20 // Order is a list of relative archive paths and represents the order 21 // in which Files were added. 22 Order []string 23 } 24 25 // InMemArchive returns an in-memory file archive. 26 func InMemArchive() *Archive { 27 return &Archive{ 28 Files: make(map[string]Record), 29 } 30 } 31 32 // ArchiveFromRecords creates a new Archive from the records. 33 func ArchiveFromRecords(rs []Record) *Archive { 34 a := InMemArchive() 35 for _, r := range rs { 36 a.WriteRecord(r) 37 } 38 return a 39 } 40 41 // ArchiveFromReader reads records from r into a new Archive in memory. 42 func ArchiveFromReader(r RecordReader) (*Archive, error) { 43 a := InMemArchive() 44 if err := Concat(a, r, nil); err != nil { 45 return nil, err 46 } 47 return a, nil 48 } 49 50 // WriteRecord implements RecordWriter and adds a record to the archive. 51 // 52 // WriteRecord uses Normalize to deduplicate paths. 53 func (a *Archive) WriteRecord(r Record) error { 54 r.Name = Normalize(r.Name) 55 a.Files[r.Name] = r 56 a.Order = append(a.Order, r.Name) 57 return nil 58 } 59 60 // Empty returns whether the archive has any files in it. 61 func (a *Archive) Empty() bool { 62 return len(a.Files) == 0 63 } 64 65 // Contains returns true if a record matching r is in the archive. 66 func (a *Archive) Contains(r Record) bool { 67 r.Name = Normalize(r.Name) 68 if s, ok := a.Files[r.Name]; ok { 69 return Equal(r, s) 70 } 71 return false 72 } 73 74 // Get returns a record for the normalized path or false if there is none. 75 // 76 // The path is normalized using Normalize, so Get("/bin/bar") is the same as 77 // Get("bin/bar") is the same as Get("bin//bar"). 78 func (a *Archive) Get(path string) (Record, bool) { 79 r, ok := a.Files[Normalize(path)] 80 return r, ok 81 } 82 83 // String implements fmt.Stringer. 84 // 85 // String lists files like ls would. 86 func (a *Archive) String() string { 87 var b strings.Builder 88 r := a.Reader() 89 for { 90 record, err := r.ReadRecord() 91 if err != nil { 92 return b.String() 93 } 94 b.WriteString(record.String()) 95 b.WriteString("\n") 96 } 97 } 98 99 type archiveReader struct { 100 a *Archive 101 pos int 102 } 103 104 // Reader returns a RecordReader for the archive that starts at the first 105 // record. 106 func (a *Archive) Reader() RecordReader { 107 return &EOFReader{&archiveReader{a: a}} 108 } 109 110 // ReadRecord implements RecordReader. 111 func (ar *archiveReader) ReadRecord() (Record, error) { 112 if ar.pos >= len(ar.a.Order) { 113 return Record{}, io.EOF 114 } 115 116 path := ar.a.Order[ar.pos] 117 ar.pos++ 118 return ar.a.Files[path], nil 119 }