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  }