github.com/jlowellwofford/u-root@v1.0.0/pkg/boot/package.go (about)

     1  // Copyright 2018 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 boot
     6  
     7  import (
     8  	"crypto/rsa"
     9  	"errors"
    10  	"fmt"
    11  	"path"
    12  	"strings"
    13  
    14  	"github.com/u-root/u-root/pkg/cpio"
    15  	"github.com/u-root/u-root/pkg/uio"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  // Package is a netboot21 boot package.
    20  //
    21  // It contains an OSImage to boot as well as arbitrary metadata.
    22  type Package struct {
    23  	OSImage
    24  
    25  	// Metadata is a map of relative archive paths -> arbitrary metadata
    26  	// content.
    27  	Metadata map[string]string
    28  }
    29  
    30  // NewPackage returns a new package based on the given OSImage.
    31  func NewPackage(osi OSImage) *Package {
    32  	return &Package{
    33  		OSImage:  osi,
    34  		Metadata: make(map[string]string),
    35  	}
    36  }
    37  
    38  // AddMetadata adds metadata at a relative path.
    39  func (p *Package) AddMetadata(relPath string, content string) {
    40  	p.Metadata[relPath] = content
    41  }
    42  
    43  // Pack writes the boot package into archive w.
    44  //
    45  // TODO(hugelgupf): use a generic private key interface. No idea if we intend
    46  // to keep using RSA here. Make usable with TPM.
    47  func (p *Package) Pack(w cpio.RecordWriter, signer *rsa.PrivateKey) error {
    48  	sw := NewSigningWriter(w)
    49  
    50  	if len(p.Metadata) > 0 {
    51  		if err := sw.WriteRecord(cpio.Directory("metadata", 0700)); err != nil {
    52  			return err
    53  		}
    54  
    55  		for name, r := range p.Metadata {
    56  			if err := sw.WriteRecord(cpio.StaticFile(path.Join("metadata", name), r, 0700)); err != nil {
    57  				return err
    58  			}
    59  		}
    60  	}
    61  
    62  	if err := p.OSImage.Pack(sw); err != nil {
    63  		return err
    64  	}
    65  
    66  	if signer != nil {
    67  		return sw.WriteSignature(signer)
    68  	}
    69  	return nil
    70  }
    71  
    72  // Unpack unpacks a boot package in rr to p.
    73  //
    74  // TODO(hugelgupf): RSA? Generalize.
    75  func (p *Package) Unpack(rr cpio.RecordReader, pk *rsa.PublicKey) error {
    76  	*p = Package{
    77  		Metadata: make(map[string]string),
    78  	}
    79  
    80  	recs := NewMeasuringReader(rr)
    81  	a, err := cpio.ReadArchive(recs)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	if pk != nil {
    86  		if err := recs.Verify(pk); err != nil {
    87  			return err
    88  		}
    89  	}
    90  
    91  	for pth, content := range a.Files {
    92  		s := strings.Split(pth, "/")
    93  		if s[0] == "metadata" && content.Info.Mode&unix.S_IFMT == unix.S_IFREG {
    94  			c, err := uio.ReadAll(content)
    95  			if err != nil {
    96  				return err
    97  			}
    98  			p.Metadata[path.Join(s[1:]...)] = string(c)
    99  		}
   100  	}
   101  
   102  	typFile, ok := a.Files["package_type"]
   103  	if !ok {
   104  		return errors.New("file 'package_type' missing from boot package")
   105  	}
   106  
   107  	tb, err := uio.ReadAll(typFile)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	pkgType := strings.TrimSpace(string(tb))
   113  	imager, ok := osimageMap[pkgType]
   114  	if !ok {
   115  		return fmt.Errorf("invalid package type %q not supported", pkgType)
   116  	}
   117  	img, err := imager(a)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	p.OSImage = img
   122  	return nil
   123  }