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