github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/madeye/madeye.go (about)

     1  // Copyright 2013-2020 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  // madeye merges multiple architecture u-root initramfs to form a single
     6  // universal initramfs.
     7  //
     8  // Synopsis:
     9  //     madeye initramfs [initramfs...]
    10  //
    11  // u-root was intended to be capable of function as a universal root, i.e. a
    12  // root file system that you could boot from different architectures. We call
    13  // this ability Multiple Architecture Device Image, or MADI, pronounced
    14  // Mad-Eye. (Apologies to Harry Potter.)
    15  //
    16  // Given a set of images, e.g. initramfs.linux_<arch>.cpio, madeye derives the
    17  // architecture from the name.  It then reads the cpio in. For a distinguished
    18  // set of directories, it relocates them from / to /<arch>/, a la Plan 9. If
    19  // there is a /init, it moves to /<arch>/init. It adjusts absolute path
    20  // symlinks.
    21  //
    22  // To boot a kernel with a MadEye, one must adjust the init= arg to prepend the
    23  // architecture. For example, on arm it would be init=/arm/init. For now, this
    24  // only works for bb mode.
    25  //
    26  // TODO: look for conflicting dev entries, and write them out.
    27  //
    28  // TODO: derive arch from the ELF file of bb instead of the name.
    29  package main
    30  
    31  import (
    32  	"bytes"
    33  	"flag"
    34  	"fmt"
    35  	"io"
    36  	"io/ioutil"
    37  	"log"
    38  	"os"
    39  	"path/filepath"
    40  
    41  	"github.com/u-root/u-root/pkg/cpio"
    42  	"github.com/u-root/u-root/pkg/uio"
    43  	"golang.org/x/sys/unix"
    44  )
    45  
    46  var (
    47  	debug = func(string, ...interface{}) {}
    48  	d     = flag.Bool("v", false, "Debug prints")
    49  	arch  = map[string]string{
    50  		"initramfs.linux_amd64.cpio":   "amd64",
    51  		"initramfs.linux_arm.cpio":     "arm",
    52  		"initramfs.linux_aarch64.cpio": "aarch64",
    53  	}
    54  	out = map[string]*cpio.Record{}
    55  )
    56  
    57  func usage() {
    58  	log.Fatalf("Usage: madeye initramfs [initramfs...]")
    59  }
    60  
    61  func file(archiver cpio.RecordFormat, n string, f io.ReaderAt) ([]cpio.Record, error) {
    62  	var r []cpio.Record
    63  	rr := archiver.Reader(f)
    64  	a, ok := arch[filepath.Base(n)]
    65  	if !ok {
    66  		return r, fmt.Errorf("%s: don't know about this", n)
    67  	}
    68  	debug("arch is %s", a)
    69  	for {
    70  		rec, err := rr.ReadRecord()
    71  		if err == io.EOF {
    72  			break
    73  		}
    74  		debug("Read %v", rec)
    75  		if err != nil {
    76  			log.Fatalf("error reading records: %v", err)
    77  		}
    78  		d := filepath.Dir(rec.Name)
    79  		switch d {
    80  		case "bbin", "bin":
    81  			rec.Name = filepath.Join(a, rec.Name)
    82  			debug("Change to %v", rec)
    83  		default:
    84  			debug("dir is %v, ignore", d)
    85  		}
    86  		switch rec.Name {
    87  		case "init", "bbin", "bin":
    88  			rec.Name = filepath.Join(a, rec.Name)
    89  		}
    90  		// TODO: make this use os constants, not unix constants.
    91  		switch rec.Mode & unix.S_IFMT {
    92  		case unix.S_IFLNK:
    93  			content, err := ioutil.ReadAll(uio.Reader(rec))
    94  			if err != nil {
    95  				return nil, err
    96  			}
    97  			switch string(content) {
    98  			case "bbin", "bin":
    99  				content = []byte(filepath.Join(a, string(content)))
   100  				debug("Change to %v", rec)
   101  			default:
   102  				debug("dir is %v, ignore", d)
   103  			}
   104  			rec.ReaderAt = bytes.NewReader(content)
   105  		}
   106  
   107  		if _, ok := out[rec.Name]; ok {
   108  			continue
   109  		}
   110  		out[rec.Name] = &rec
   111  		r = append(r, rec)
   112  	}
   113  	return r, nil
   114  }
   115  
   116  func main() {
   117  	flag.Parse()
   118  	if *d {
   119  		debug = log.Printf
   120  	}
   121  
   122  	a := flag.Args()
   123  	debug("Args %v", a)
   124  	if len(a) < 1 {
   125  		usage()
   126  	}
   127  
   128  	archiver, err := cpio.Format("newc")
   129  	if err != nil {
   130  		log.Fatal(err)
   131  	}
   132  
   133  	var rr []cpio.Record
   134  	for _, a := range flag.Args() {
   135  		f, err := os.Open(a)
   136  		if err != nil {
   137  			log.Fatal(err)
   138  		}
   139  		r, err := file(archiver, a, f)
   140  		if err != nil {
   141  			log.Fatal(err)
   142  		}
   143  		// Why not a defer? Because that would happen
   144  		// outside the for loop. Not that it really matters:
   145  		// any kind of explicit close is a bit silly here, we're
   146  		// never going to have more than MAXFD arguments anyway,
   147  		// but better safe than sorry.
   148  		if err := f.Close(); err != nil {
   149  			log.Fatal(err)
   150  		}
   151  		rr = append(rr, r...)
   152  	}
   153  	// process ...
   154  	archiver, err = cpio.Format("newc")
   155  	if err != nil {
   156  		log.Fatal(err)
   157  	}
   158  	rw := archiver.Writer(os.Stdout)
   159  	for _, r := range rr {
   160  		if *d {
   161  			log.Printf("%s", r)
   162  			continue
   163  		}
   164  		if err := rw.WriteRecord(r); err != nil {
   165  			log.Fatal(err)
   166  		}
   167  	}
   168  	if !*d {
   169  		if err := cpio.WriteTrailer(rw); err != nil {
   170  			log.Fatalf("Error writing trailer record: %v", err)
   171  		}
   172  	}
   173  }