github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/pkg/cpio/cpio.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 implements utilities for reading and writing cpio archives.
     6  //
     7  // Currently, only newc-formatted cpio archives are supported through cpio.Newc.
     8  //
     9  // Reading from or writing to a file:
    10  //
    11  //    f, err := os.Open(...)
    12  //    if err ...
    13  //    recReader := cpio.Newc.Reader(f)
    14  //    err := ForEachRecord(recReader, func(r cpio.Record) error {
    15  //
    16  //    })
    17  //
    18  //    // Or...
    19  //    recWriter := cpio.Newc.Writer(f)
    20  //
    21  //
    22  // Reading from or writing to an in-memory archive:
    23  //
    24  //    a := cpio.InMemArchive()
    25  //    err := a.WriteRecord(...)
    26  //
    27  //    recReader := a.Reader() // Reads from the "beginning."
    28  //
    29  //    if a.Contains("bar/foo") {
    30  //
    31  //    }
    32  package cpio
    33  
    34  import (
    35  	"fmt"
    36  	"io"
    37  	"os"
    38  	"syscall"
    39  	"time"
    40  
    41  	"github.com/u-root/u-root/pkg/ls"
    42  	"github.com/u-root/u-root/pkg/uio"
    43  	"golang.org/x/sys/unix"
    44  )
    45  
    46  var (
    47  	formatMap = make(map[string]RecordFormat)
    48  	Debug     = func(string, ...interface{}) {}
    49  )
    50  
    51  // Record represents a CPIO record, which represents a Unix file.
    52  type Record struct {
    53  	// ReaderAt contains the content of this CPIO record.
    54  	io.ReaderAt
    55  
    56  	// Info is metadata describing the CPIO record.
    57  	Info
    58  
    59  	// metadata about this item's place in the file
    60  	RecPos  int64  // Where in the file this record is
    61  	RecLen  uint64 // How big the record is.
    62  	FilePos int64  // Where in the CPIO the file's contents are.
    63  }
    64  
    65  // String implements a fmt.Stringer for Record.
    66  //
    67  // String returns a string formatted like `ls` would format it.
    68  func (r Record) String() string {
    69  	s := ls.LongStringer{
    70  		Human: true,
    71  		Name:  ls.NameStringer{},
    72  	}
    73  	return s.FileString(LSInfoFromRecord(r))
    74  }
    75  
    76  // Info holds metadata about files.
    77  type Info struct {
    78  	Ino      uint64
    79  	Mode     uint64
    80  	UID      uint64
    81  	GID      uint64
    82  	NLink    uint64
    83  	MTime    uint64
    84  	FileSize uint64
    85  	Dev      uint64
    86  	Major    uint64
    87  	Minor    uint64
    88  	Rmajor   uint64
    89  	Rminor   uint64
    90  	Name     string
    91  }
    92  
    93  func (i Info) String() string {
    94  	return fmt.Sprintf("%s: Ino %d Mode %#o UID %d GID %d NLink %d MTime %v FileSize %d Major %d Minor %d Rmajor %d Rminor %d",
    95  		i.Name,
    96  		i.Ino,
    97  		i.Mode,
    98  		i.UID,
    99  		i.GID,
   100  		i.NLink,
   101  		time.Unix(int64(i.MTime), 0).UTC(),
   102  		i.FileSize,
   103  		i.Major,
   104  		i.Minor,
   105  		i.Rmajor,
   106  		i.Rminor)
   107  }
   108  
   109  // A RecordReader reads one record from an archive.
   110  type RecordReader interface {
   111  	ReadRecord() (Record, error)
   112  }
   113  
   114  // A RecordWriter writes one record to an archive.
   115  type RecordWriter interface {
   116  	WriteRecord(Record) error
   117  }
   118  
   119  // A RecordFormat gives readers and writers for dealing with archives from io
   120  // objects.
   121  //
   122  // CPIO files have a number of records, of which newc is the most widely used
   123  // today.
   124  type RecordFormat interface {
   125  	Reader(r io.ReaderAt) RecordReader
   126  	Writer(w io.Writer) RecordWriter
   127  }
   128  
   129  // Format returns the RecordFormat with that name, if it exists.
   130  func Format(name string) (RecordFormat, error) {
   131  	op, ok := formatMap[name]
   132  	if !ok {
   133  		return nil, fmt.Errorf("%q is not in cpio format map %v", name, formatMap)
   134  	}
   135  	return op, nil
   136  }
   137  
   138  func modeFromLinux(mode uint64) os.FileMode {
   139  	m := os.FileMode(mode & 0777)
   140  	switch mode & syscall.S_IFMT {
   141  	case syscall.S_IFBLK:
   142  		m |= os.ModeDevice
   143  	case syscall.S_IFCHR:
   144  		m |= os.ModeDevice | os.ModeCharDevice
   145  	case syscall.S_IFDIR:
   146  		m |= os.ModeDir
   147  	case syscall.S_IFIFO:
   148  		m |= os.ModeNamedPipe
   149  	case syscall.S_IFLNK:
   150  		m |= os.ModeSymlink
   151  	case syscall.S_IFREG:
   152  		// nothing to do
   153  	case syscall.S_IFSOCK:
   154  		m |= os.ModeSocket
   155  	}
   156  	if mode&syscall.S_ISGID != 0 {
   157  		m |= os.ModeSetgid
   158  	}
   159  	if mode&syscall.S_ISUID != 0 {
   160  		m |= os.ModeSetuid
   161  	}
   162  	if mode&syscall.S_ISVTX != 0 {
   163  		m |= os.ModeSticky
   164  	}
   165  	return m
   166  }
   167  
   168  // LSInfoFromRecord converts a Record to be usable with the ls package for
   169  // listing files.
   170  func LSInfoFromRecord(rec Record) ls.FileInfo {
   171  	var target string
   172  
   173  	mode := modeFromLinux(rec.Mode)
   174  	if mode&os.ModeType == os.ModeSymlink {
   175  		if l, err := uio.ReadAll(rec); err != nil {
   176  			target = err.Error()
   177  		} else {
   178  			target = string(l)
   179  		}
   180  	}
   181  
   182  	return ls.FileInfo{
   183  		Name:          rec.Name,
   184  		Mode:          mode,
   185  		Rdev:          unix.Mkdev(uint32(rec.Rmajor), uint32(rec.Rminor)),
   186  		UID:           uint32(rec.UID),
   187  		GID:           uint32(rec.GID),
   188  		Size:          int64(rec.FileSize),
   189  		MTime:         time.Unix(int64(rec.MTime), 0).UTC(),
   190  		SymlinkTarget: target,
   191  	}
   192  }