github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/cpio/newc.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 "bytes" 9 "encoding/binary" 10 "encoding/hex" 11 "fmt" 12 "io" 13 14 "github.com/u-root/u-root/pkg/uio" 15 ) 16 17 const ( 18 newcMagic = "070701" 19 magicLen = 6 20 ) 21 22 var ( 23 // Newc is the newc CPIO record format. 24 Newc RecordFormat = newc{magic: newcMagic} 25 ) 26 27 type header struct { 28 Ino uint32 29 Mode uint32 30 UID uint32 31 GID uint32 32 NLink uint32 33 MTime uint32 34 FileSize uint32 35 Major uint32 36 Minor uint32 37 Rmajor uint32 38 Rminor uint32 39 NameLength uint32 40 CRC uint32 41 } 42 43 func headerFromInfo(i Info) header { 44 var h header 45 h.Ino = uint32(i.Ino) 46 h.Mode = uint32(i.Mode) 47 h.UID = uint32(i.UID) 48 h.GID = uint32(i.GID) 49 h.NLink = uint32(i.NLink) 50 h.MTime = uint32(i.MTime) 51 h.FileSize = uint32(i.FileSize) 52 h.Major = uint32(i.Major) 53 h.Minor = uint32(i.Minor) 54 h.Rmajor = uint32(i.Rmajor) 55 h.Rminor = uint32(i.Rminor) 56 h.NameLength = uint32(len(i.Name)) + 1 57 return h 58 } 59 60 func (h header) Info() Info { 61 var i Info 62 i.Ino = uint64(h.Ino) 63 i.Mode = uint64(h.Mode) 64 i.UID = uint64(h.UID) 65 i.GID = uint64(h.GID) 66 i.NLink = uint64(h.NLink) 67 i.MTime = uint64(h.MTime) 68 i.FileSize = uint64(h.FileSize) 69 i.Major = uint64(h.Major) 70 i.Minor = uint64(h.Minor) 71 i.Rmajor = uint64(h.Rmajor) 72 i.Rminor = uint64(h.Rminor) 73 return i 74 } 75 76 // newc implements RecordFormat for the newc format. 77 type newc struct { 78 magic string 79 } 80 81 // round4 returns the next multiple of 4 close to n. 82 func round4(n int64) int64 { 83 return (n + 3) &^ 0x3 84 } 85 86 type writer struct { 87 n newc 88 w io.Writer 89 pos int64 90 } 91 92 // Writer implements RecordFormat.Writer. 93 func (n newc) Writer(w io.Writer) RecordWriter { 94 return NewDedupWriter(&writer{n: n, w: w}) 95 } 96 97 func (w *writer) Write(b []byte) (int, error) { 98 n, err := w.w.Write(b) 99 if err != nil { 100 return 0, err 101 } 102 w.pos += int64(n) 103 return n, nil 104 } 105 106 func (w *writer) pad() error { 107 if o := round4(w.pos); o != w.pos { 108 var pad [3]byte 109 if _, err := w.Write(pad[:o-w.pos]); err != nil { 110 return err 111 } 112 } 113 return nil 114 } 115 116 // WriteRecord writes newc cpio records. It pads the header+name write to 4 117 // byte alignment and pads the data write as well. 118 func (w *writer) WriteRecord(f Record) error { 119 // Write magic. 120 if _, err := w.Write([]byte(w.n.magic)); err != nil { 121 return err 122 } 123 124 buf := &bytes.Buffer{} 125 hdr := headerFromInfo(f.Info) 126 if f.ReaderAt == nil { 127 hdr.FileSize = 0 128 } 129 hdr.CRC = 0 130 if err := binary.Write(buf, binary.BigEndian, hdr); err != nil { 131 return err 132 } 133 134 hexBuf := make([]byte, hex.EncodedLen(buf.Len())) 135 n := hex.Encode(hexBuf, buf.Bytes()) 136 // It's much easier to debug if we match GNU output format. 137 hexBuf = bytes.ToUpper(hexBuf) 138 139 // Write header. 140 if _, err := w.Write(hexBuf[:n]); err != nil { 141 return err 142 } 143 144 // Append NULL char. 145 cstr := append([]byte(f.Info.Name), 0) 146 // Write name. 147 if _, err := w.Write(cstr); err != nil { 148 return err 149 } 150 151 // Pad to a multiple of 4. 152 if err := w.pad(); err != nil { 153 return err 154 } 155 156 // Some files do not have any content. 157 if f.ReaderAt == nil { 158 return nil 159 } 160 161 // Write file contents. 162 m, err := io.Copy(w, uio.Reader(f)) 163 if err != nil { 164 return err 165 } 166 if m != int64(f.Info.FileSize) { 167 return fmt.Errorf("WriteRecord: %s: wrote %d bytes of file instead of %d bytes; archive is now corrupt", f.Info.Name, m, f.Info.FileSize) 168 } 169 if c, ok := f.ReaderAt.(io.Closer); ok { 170 if err := c.Close(); err != nil { 171 return err 172 } 173 } 174 if m > 0 { 175 return w.pad() 176 } 177 return nil 178 } 179 180 type reader struct { 181 n newc 182 r io.ReaderAt 183 pos int64 184 } 185 186 // Reader implements RecordFormat.Reader. 187 func (n newc) Reader(r io.ReaderAt) RecordReader { 188 return EOFReader{&reader{n: n, r: r}} 189 } 190 191 func (r *reader) read(p []byte) error { 192 n, err := r.r.ReadAt(p, r.pos) 193 if err == io.EOF { 194 return io.EOF 195 } 196 if err != nil || n != len(p) { 197 return fmt.Errorf("ReadAt(pos = %d): got %d, want %d bytes; error %v", r.pos, n, len(p), err) 198 } 199 r.pos += int64(n) 200 return nil 201 } 202 203 func (r *reader) readAligned(p []byte) error { 204 err := r.read(p) 205 r.pos = round4(r.pos) 206 return err 207 } 208 209 // ReadRecord implements RecordReader for the newc cpio format. 210 func (r *reader) ReadRecord() (Record, error) { 211 hdr := header{} 212 recPos := r.pos 213 214 Debug("Next record: pos is %d\n", r.pos) 215 216 buf := make([]byte, hex.EncodedLen(binary.Size(hdr))+magicLen) 217 if err := r.read(buf); err != nil { 218 return Record{}, err 219 } 220 221 // Check the magic. 222 if magic := string(buf[:magicLen]); magic != r.n.magic { 223 return Record{}, fmt.Errorf("reader: magic got %q, want %q", magic, r.n.magic) 224 } 225 Debug("Header is %v\n", buf) 226 227 // Decode hex header fields. 228 dst := make([]byte, binary.Size(hdr)) 229 if _, err := hex.Decode(dst, buf[magicLen:]); err != nil { 230 return Record{}, fmt.Errorf("reader: error decoding hex: %v", err) 231 } 232 if err := binary.Read(bytes.NewReader(dst), binary.BigEndian, &hdr); err != nil { 233 return Record{}, err 234 } 235 Debug("Decoded header is %v\n", hdr) 236 237 // Get the name. 238 nameBuf := make([]byte, hdr.NameLength) 239 if err := r.readAligned(nameBuf); err != nil { 240 return Record{}, err 241 } 242 243 info := hdr.Info() 244 info.Name = string(nameBuf[:hdr.NameLength-1]) 245 246 recLen := uint64(r.pos - recPos) 247 filePos := r.pos 248 content := io.NewSectionReader(r.r, r.pos, int64(hdr.FileSize)) 249 r.pos = round4(r.pos + int64(hdr.FileSize)) 250 return Record{ 251 Info: info, 252 ReaderAt: content, 253 RecLen: recLen, 254 RecPos: recPos, 255 FilePos: filePos, 256 }, nil 257 } 258 259 func init() { 260 formatMap["newc"] = Newc 261 }