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 }