github.com/jlowellwofford/u-root@v1.0.0/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 c, ok := f.ReaderAt.(io.Closer); ok { 167 if err := c.Close(); err != nil { 168 return err 169 } 170 } 171 if m > 0 { 172 return w.pad() 173 } 174 return nil 175 } 176 177 type reader struct { 178 n newc 179 r io.ReaderAt 180 pos int64 181 } 182 183 // Reader implements RecordFormat.Reader. 184 func (n newc) Reader(r io.ReaderAt) RecordReader { 185 return EOFReader{&reader{n: n, r: r}} 186 } 187 188 func (r *reader) read(p []byte) error { 189 n, err := r.r.ReadAt(p, r.pos) 190 if err == io.EOF { 191 return io.EOF 192 } 193 if err != nil || n != len(p) { 194 return fmt.Errorf("ReadAt(pos = %d): got %d, want %d bytes; error %v", r.pos, n, len(p), err) 195 } 196 r.pos += int64(n) 197 return nil 198 } 199 200 func (r *reader) readAligned(p []byte) error { 201 err := r.read(p) 202 r.pos = round4(r.pos) 203 return err 204 } 205 206 // ReadRecord implements RecordReader for the newc cpio format. 207 func (r *reader) ReadRecord() (Record, error) { 208 hdr := header{} 209 210 Debug("Next record: pos is %d\n", r.pos) 211 212 buf := make([]byte, hex.EncodedLen(binary.Size(hdr))+magicLen) 213 if err := r.read(buf); err != nil { 214 return Record{}, err 215 } 216 217 // Check the magic. 218 if magic := string(buf[:magicLen]); magic != r.n.magic { 219 return Record{}, fmt.Errorf("reader: magic got %q, want %q", magic, r.n.magic) 220 } 221 Debug("Header is %v\n", buf) 222 223 // Decode hex header fields. 224 dst := make([]byte, binary.Size(hdr)) 225 if _, err := hex.Decode(dst, buf[magicLen:]); err != nil { 226 return Record{}, fmt.Errorf("reader: error decoding hex: %v", err) 227 } 228 if err := binary.Read(bytes.NewReader(dst), binary.BigEndian, &hdr); err != nil { 229 return Record{}, err 230 } 231 Debug("Decoded header is %s\n", hdr) 232 233 // Get the name. 234 nameBuf := make([]byte, hdr.NameLength) 235 if err := r.readAligned(nameBuf); err != nil { 236 return Record{}, err 237 } 238 239 info := hdr.Info() 240 info.Name = string(nameBuf[:hdr.NameLength-1]) 241 242 content := io.NewSectionReader(r.r, r.pos, int64(hdr.FileSize)) 243 r.pos = round4(r.pos + int64(hdr.FileSize)) 244 return Record{ 245 Info: info, 246 ReaderAt: content, 247 }, nil 248 } 249 250 func init() { 251 formatMap["newc"] = Newc 252 }