github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/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  	recPos := r.pos
   210  
   211  	Debug("Next record: pos is %d\n", r.pos)
   212  
   213  	buf := make([]byte, hex.EncodedLen(binary.Size(hdr))+magicLen)
   214  	if err := r.read(buf); err != nil {
   215  		return Record{}, err
   216  	}
   217  
   218  	// Check the magic.
   219  	if magic := string(buf[:magicLen]); magic != r.n.magic {
   220  		return Record{}, fmt.Errorf("reader: magic got %q, want %q", magic, r.n.magic)
   221  	}
   222  	Debug("Header is %v\n", buf)
   223  
   224  	// Decode hex header fields.
   225  	dst := make([]byte, binary.Size(hdr))
   226  	if _, err := hex.Decode(dst, buf[magicLen:]); err != nil {
   227  		return Record{}, fmt.Errorf("reader: error decoding hex: %v", err)
   228  	}
   229  	if err := binary.Read(bytes.NewReader(dst), binary.BigEndian, &hdr); err != nil {
   230  		return Record{}, err
   231  	}
   232  	Debug("Decoded header is %v\n", hdr)
   233  
   234  	// Get the name.
   235  	nameBuf := make([]byte, hdr.NameLength)
   236  	if err := r.readAligned(nameBuf); err != nil {
   237  		return Record{}, err
   238  	}
   239  
   240  	info := hdr.Info()
   241  	info.Name = string(nameBuf[:hdr.NameLength-1])
   242  
   243  	recLen := uint64(r.pos - recPos)
   244  	filePos := r.pos
   245  	content := io.NewSectionReader(r.r, r.pos, int64(hdr.FileSize))
   246  	r.pos = round4(r.pos + int64(hdr.FileSize))
   247  	return Record{
   248  		Info:     info,
   249  		ReaderAt: content,
   250  		RecLen:   recLen,
   251  		RecPos:   recPos,
   252  		FilePos:  filePos,
   253  	}, nil
   254  }
   255  
   256  func init() {
   257  	formatMap["newc"] = Newc
   258  }