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  }