github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+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  
    49  	// Debug can be set e.g. to log.Printf to enable debug prints from
    50  	// marshaling/unmarshaling cpio archives.
    51  	Debug = func(string, ...interface{}) {}
    52  )
    53  
    54  // Record represents a CPIO record, which represents a Unix file.
    55  type Record struct {
    56  	// ReaderAt contains the content of this CPIO record.
    57  	io.ReaderAt
    58  
    59  	// Info is metadata describing the CPIO record.
    60  	Info
    61  
    62  	// metadata about this item's place in the file
    63  	RecPos  int64  // Where in the file this record is
    64  	RecLen  uint64 // How big the record is.
    65  	FilePos int64  // Where in the CPIO the file's contents are.
    66  }
    67  
    68  // String implements a fmt.Stringer for Record.
    69  //
    70  // String returns a string long-formatted like `ls` would format it.
    71  func (r Record) String() string {
    72  	s := ls.LongStringer{
    73  		Human: true,
    74  		Name:  ls.NameStringer{},
    75  	}
    76  	return s.FileString(LSInfoFromRecord(r))
    77  }
    78  
    79  // Info holds metadata about files.
    80  type Info struct {
    81  	Ino      uint64
    82  	Mode     uint64
    83  	UID      uint64
    84  	GID      uint64
    85  	NLink    uint64
    86  	MTime    uint64
    87  	FileSize uint64
    88  	Dev      uint64
    89  	Major    uint64
    90  	Minor    uint64
    91  	Rmajor   uint64
    92  	Rminor   uint64
    93  	Name     string
    94  }
    95  
    96  func (i Info) String() string {
    97  	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",
    98  		i.Name,
    99  		i.Ino,
   100  		i.Mode,
   101  		i.UID,
   102  		i.GID,
   103  		i.NLink,
   104  		time.Unix(int64(i.MTime), 0).UTC(),
   105  		i.FileSize,
   106  		i.Major,
   107  		i.Minor,
   108  		i.Rmajor,
   109  		i.Rminor)
   110  }
   111  
   112  // A RecordReader reads one record from an archive.
   113  type RecordReader interface {
   114  	ReadRecord() (Record, error)
   115  }
   116  
   117  // A RecordWriter writes one record to an archive.
   118  type RecordWriter interface {
   119  	WriteRecord(Record) error
   120  }
   121  
   122  // A RecordFormat gives readers and writers for dealing with archives from io
   123  // objects.
   124  //
   125  // CPIO files have a number of records, of which newc is the most widely used
   126  // today.
   127  type RecordFormat interface {
   128  	Reader(r io.ReaderAt) RecordReader
   129  	Writer(w io.Writer) RecordWriter
   130  }
   131  
   132  // Format returns the RecordFormat with that name, if it exists.
   133  func Format(name string) (RecordFormat, error) {
   134  	op, ok := formatMap[name]
   135  	if !ok {
   136  		return nil, fmt.Errorf("%q is not in cpio format map %v", name, formatMap)
   137  	}
   138  	return op, nil
   139  }
   140  
   141  func modeFromLinux(mode uint64) os.FileMode {
   142  	m := os.FileMode(mode & 0777)
   143  	switch mode & syscall.S_IFMT {
   144  	case syscall.S_IFBLK:
   145  		m |= os.ModeDevice
   146  	case syscall.S_IFCHR:
   147  		m |= os.ModeDevice | os.ModeCharDevice
   148  	case syscall.S_IFDIR:
   149  		m |= os.ModeDir
   150  	case syscall.S_IFIFO:
   151  		m |= os.ModeNamedPipe
   152  	case syscall.S_IFLNK:
   153  		m |= os.ModeSymlink
   154  	case syscall.S_IFREG:
   155  		// nothing to do
   156  	case syscall.S_IFSOCK:
   157  		m |= os.ModeSocket
   158  	}
   159  	if mode&syscall.S_ISGID != 0 {
   160  		m |= os.ModeSetgid
   161  	}
   162  	if mode&syscall.S_ISUID != 0 {
   163  		m |= os.ModeSetuid
   164  	}
   165  	if mode&syscall.S_ISVTX != 0 {
   166  		m |= os.ModeSticky
   167  	}
   168  	return m
   169  }
   170  
   171  // LSInfoFromRecord converts a Record to be usable with the ls package for
   172  // listing files.
   173  func LSInfoFromRecord(rec Record) ls.FileInfo {
   174  	var target string
   175  
   176  	mode := modeFromLinux(rec.Mode)
   177  	if mode&os.ModeType == os.ModeSymlink {
   178  		if l, err := uio.ReadAll(rec); err != nil {
   179  			target = err.Error()
   180  		} else {
   181  			target = string(l)
   182  		}
   183  	}
   184  
   185  	return ls.FileInfo{
   186  		Name:          rec.Name,
   187  		Mode:          mode,
   188  		Rdev:          unix.Mkdev(uint32(rec.Rmajor), uint32(rec.Rminor)),
   189  		UID:           uint32(rec.UID),
   190  		GID:           uint32(rec.GID),
   191  		Size:          int64(rec.FileSize),
   192  		MTime:         time.Unix(int64(rec.MTime), 0).UTC(),
   193  		SymlinkTarget: target,
   194  	}
   195  }