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  }