gopkg.in/hugelgupf/u-root.v2@v2.0.0-20180831055005-3f8fdb0ce09d/pkg/uroot/initramfs/archive.go (about)

     1  // Copyright 2015-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 initramfs
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  
    11  	"github.com/u-root/u-root/pkg/cpio"
    12  )
    13  
    14  var (
    15  	// Archivers are the supported initramfs archivers at the moment.
    16  	//
    17  	// - cpio: writes the initramfs to a cpio.
    18  	// - dir:  writes the initramfs relative to a specified directory.
    19  	Archivers = map[string]Archiver{
    20  		"cpio": CPIOArchiver{
    21  			RecordFormat: cpio.Newc,
    22  		},
    23  		"dir": DirArchiver{},
    24  	}
    25  )
    26  
    27  // Archiver is an archive format that builds an archive using a given set of
    28  // files.
    29  type Archiver interface {
    30  	// OpenWriter opens an archive writer at `path`.
    31  	//
    32  	// If `path` is unspecified, implementations may choose an arbitrary
    33  	// default location, potentially based on `goos` and `goarch`.
    34  	OpenWriter(path, goos, goarch string) (Writer, error)
    35  
    36  	// Reader returns a Reader that allows reading files from a file.
    37  	Reader(file io.ReaderAt) Reader
    38  }
    39  
    40  // GetArchiver finds a registered initramfs archiver by name.
    41  //
    42  // Good to use with command-line arguments.
    43  func GetArchiver(name string) (Archiver, error) {
    44  	archiver, ok := Archivers[name]
    45  	if !ok {
    46  		return nil, fmt.Errorf("couldn't find archival format %q", name)
    47  	}
    48  	return archiver, nil
    49  }
    50  
    51  // Writer is an initramfs archive that files can be written to.
    52  type Writer interface {
    53  	cpio.RecordWriter
    54  
    55  	// Finish finishes the archive.
    56  	Finish() error
    57  }
    58  
    59  // Reader is an object that files can be read from.
    60  type Reader cpio.RecordReader
    61  
    62  // Opts are options for building an initramfs archive.
    63  type Opts struct {
    64  	// Files are the files to be included.
    65  	//
    66  	// Files here generally have priority over files in DefaultRecords or
    67  	// BaseArchive.
    68  	Files
    69  
    70  	// DefaultRecords is a set of files to be included in the initramfs.
    71  	DefaultRecords []cpio.Record
    72  
    73  	// OutputFile is the file to write to.
    74  	OutputFile Writer
    75  
    76  	// BaseArchive is an existing archive to add files to.
    77  	//
    78  	// BaseArchive may be nil.
    79  	BaseArchive Reader
    80  
    81  	// UseExistingInit determines whether the init from BaseArchive is used
    82  	// or not, if BaseArchive is specified.
    83  	//
    84  	// If this is false, the "init" file in BaseArchive will be renamed
    85  	// "inito" in the output archive.
    86  	UseExistingInit bool
    87  }
    88  
    89  // Write uses the given options to determine which files need to be written to
    90  // the output initramfs archiver using the archive format `a` and writes them.
    91  func Write(opts *Opts) error {
    92  	// Add default records.
    93  	for _, rec := range opts.DefaultRecords {
    94  		// Ignore if it doesn't get added. Probably means the user
    95  		// included something for this file or directory already.
    96  		//
    97  		// TODO: ignore only when it already exists in archive.
    98  		opts.Files.AddRecord(rec)
    99  	}
   100  
   101  	// Write base archive.
   102  	if opts.BaseArchive != nil {
   103  		transform := cpio.MakeReproducible
   104  
   105  		// Rename init to inito if user doesn't want the existing init.
   106  		if !opts.UseExistingInit && opts.Contains("init") {
   107  			transform = func(r cpio.Record) cpio.Record {
   108  				if r.Name == "init" {
   109  					r.Name = "inito"
   110  				}
   111  				return cpio.MakeReproducible(r)
   112  			}
   113  		}
   114  		// If user wants the base archive init, but specified another
   115  		// init, make the other one inito.
   116  		if opts.UseExistingInit && opts.Contains("init") {
   117  			opts.Rename("init", "inito")
   118  		}
   119  
   120  		for {
   121  			f, err := opts.BaseArchive.ReadRecord()
   122  			if err == io.EOF {
   123  				break
   124  			}
   125  			if err != nil {
   126  				return err
   127  			}
   128  			// TODO: ignore only the error where it already exists
   129  			// in archive.
   130  			opts.Files.AddRecord(transform(f))
   131  		}
   132  	}
   133  
   134  	if err := opts.Files.WriteTo(opts.OutputFile); err != nil {
   135  		return err
   136  	}
   137  	return opts.OutputFile.Finish()
   138  }