github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/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 implements utilities for reading and writing cpio archives. 6 // 7 // Currently, only newc-formatted cpio archives are supported through cpio.Newc. 8 // 9 // Reading from or writing to a file: 10 // 11 // f, err := os.Open(...) 12 // if err ... 13 // recReader := cpio.Newc.Reader(f) 14 // err := ForEachRecord(recReader, func(r cpio.Record) error { 15 // 16 // }) 17 // 18 // // Or... 19 // recWriter := cpio.Newc.Writer(f) 20 // 21 // 22 // Reading from or writing to an in-memory archive: 23 // 24 // a := cpio.InMemArchive() 25 // err := a.WriteRecord(...) 26 // 27 // recReader := a.Reader() // Reads from the "beginning." 28 // 29 // if a.Contains("bar/foo") { 30 // 31 // } 32 package cpio 33 34 import ( 35 "fmt" 36 "io" 37 "os" 38 "syscall" 39 "time" 40 41 "github.com/u-root/u-root/pkg/ls" 42 "github.com/u-root/u-root/pkg/uio" 43 "golang.org/x/sys/unix" 44 ) 45 46 var ( 47 formatMap = make(map[string]RecordFormat) 48 49 // Debug can be set e.g. to log.Printf to enable debug prints from 50 // marshaling/unmarshaling cpio archives. 51 Debug = func(string, ...interface{}) {} 52 ) 53 54 // Record represents a CPIO record, which represents a Unix file. 55 type Record struct { 56 // ReaderAt contains the content of this CPIO record. 57 io.ReaderAt 58 59 // Info is metadata describing the CPIO record. 60 Info 61 62 // metadata about this item's place in the file 63 RecPos int64 // Where in the file this record is 64 RecLen uint64 // How big the record is. 65 FilePos int64 // Where in the CPIO the file's contents are. 66 } 67 68 // String implements a fmt.Stringer for Record. 69 // 70 // String returns a string long-formatted like `ls` would format it. 71 func (r Record) String() string { 72 s := ls.LongStringer{ 73 Human: true, 74 Name: ls.NameStringer{}, 75 } 76 return s.FileString(LSInfoFromRecord(r)) 77 } 78 79 // Info holds metadata about files. 80 type Info struct { 81 Ino uint64 82 Mode uint64 83 UID uint64 84 GID uint64 85 NLink uint64 86 MTime uint64 87 FileSize uint64 88 Dev uint64 89 Major uint64 90 Minor uint64 91 Rmajor uint64 92 Rminor uint64 93 Name string 94 } 95 96 func (i Info) String() string { 97 return fmt.Sprintf("%s: Ino %d Mode %#o UID %d GID %d NLink %d MTime %v FileSize %d Major %d Minor %d Rmajor %d Rminor %d", 98 i.Name, 99 i.Ino, 100 i.Mode, 101 i.UID, 102 i.GID, 103 i.NLink, 104 time.Unix(int64(i.MTime), 0).UTC(), 105 i.FileSize, 106 i.Major, 107 i.Minor, 108 i.Rmajor, 109 i.Rminor) 110 } 111 112 // A RecordReader reads one record from an archive. 113 type RecordReader interface { 114 ReadRecord() (Record, error) 115 } 116 117 // A RecordWriter writes one record to an archive. 118 type RecordWriter interface { 119 WriteRecord(Record) error 120 } 121 122 // A RecordFormat gives readers and writers for dealing with archives from io 123 // objects. 124 // 125 // CPIO files have a number of records, of which newc is the most widely used 126 // today. 127 type RecordFormat interface { 128 Reader(r io.ReaderAt) RecordReader 129 Writer(w io.Writer) RecordWriter 130 } 131 132 // Format returns the RecordFormat with that name, if it exists. 133 func Format(name string) (RecordFormat, error) { 134 op, ok := formatMap[name] 135 if !ok { 136 return nil, fmt.Errorf("%q is not in cpio format map %v", name, formatMap) 137 } 138 return op, nil 139 } 140 141 func modeFromLinux(mode uint64) os.FileMode { 142 m := os.FileMode(mode & 0777) 143 switch mode & syscall.S_IFMT { 144 case syscall.S_IFBLK: 145 m |= os.ModeDevice 146 case syscall.S_IFCHR: 147 m |= os.ModeDevice | os.ModeCharDevice 148 case syscall.S_IFDIR: 149 m |= os.ModeDir 150 case syscall.S_IFIFO: 151 m |= os.ModeNamedPipe 152 case syscall.S_IFLNK: 153 m |= os.ModeSymlink 154 case syscall.S_IFREG: 155 // nothing to do 156 case syscall.S_IFSOCK: 157 m |= os.ModeSocket 158 } 159 if mode&syscall.S_ISGID != 0 { 160 m |= os.ModeSetgid 161 } 162 if mode&syscall.S_ISUID != 0 { 163 m |= os.ModeSetuid 164 } 165 if mode&syscall.S_ISVTX != 0 { 166 m |= os.ModeSticky 167 } 168 return m 169 } 170 171 // LSInfoFromRecord converts a Record to be usable with the ls package for 172 // listing files. 173 func LSInfoFromRecord(rec Record) ls.FileInfo { 174 var target string 175 176 mode := modeFromLinux(rec.Mode) 177 if mode&os.ModeType == os.ModeSymlink { 178 if l, err := uio.ReadAll(rec); err != nil { 179 target = err.Error() 180 } else { 181 target = string(l) 182 } 183 } 184 185 return ls.FileInfo{ 186 Name: rec.Name, 187 Mode: mode, 188 Rdev: unix.Mkdev(uint32(rec.Rmajor), uint32(rec.Rminor)), 189 UID: uint32(rec.UID), 190 GID: uint32(rec.GID), 191 Size: int64(rec.FileSize), 192 MTime: time.Unix(int64(rec.MTime), 0).UTC(), 193 SymlinkTarget: target, 194 } 195 }