github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/linux.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 "errors" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "log" 13 "os" 14 15 "github.com/u-root/u-root/pkg/boot/kexec" 16 "github.com/u-root/u-root/pkg/boot/util" 17 "github.com/u-root/u-root/pkg/uio" 18 ) 19 20 // LinuxImage implements OSImage for a Linux kernel + initramfs. 21 type LinuxImage struct { 22 Name string 23 24 Kernel io.ReaderAt 25 Initrd io.ReaderAt 26 Cmdline string 27 } 28 29 var _ OSImage = &LinuxImage{} 30 31 // named is satisifed by both *os.File and *vfile.File. Hack hack hack. 32 type named interface { 33 Name() string 34 } 35 36 func stringer(mod io.ReaderAt) string { 37 if s, ok := mod.(fmt.Stringer); ok { 38 return s.String() 39 } 40 if f, ok := mod.(named); ok { 41 return f.Name() 42 } 43 return fmt.Sprintf("%T", mod) 44 } 45 46 // Label returns either the Name or a short description. 47 func (li *LinuxImage) Label() string { 48 if len(li.Name) > 0 { 49 return li.Name 50 } 51 return fmt.Sprintf("Linux(kernel=%s initrd=%s)", stringer(li.Kernel), stringer(li.Initrd)) 52 } 53 54 // String prints a human-readable version of this linux image. 55 func (li *LinuxImage) String() string { 56 return fmt.Sprintf("LinuxImage(\n Name: %s\n Kernel: %s\n Initrd: %s\n Cmdline: %s\n)\n", li.Name, stringer(li.Kernel), stringer(li.Initrd), li.Cmdline) 57 } 58 59 func copyToFile(r io.Reader) (*os.File, error) { 60 f, err := ioutil.TempFile("", "nerf-netboot") 61 if err != nil { 62 return nil, err 63 } 64 defer f.Close() 65 if _, err := io.Copy(f, r); err != nil { 66 return nil, err 67 } 68 if err := f.Sync(); err != nil { 69 return nil, err 70 } 71 72 readOnlyF, err := os.Open(f.Name()) 73 if err != nil { 74 return nil, err 75 } 76 return readOnlyF, nil 77 } 78 79 // Load implements OSImage.Load and kexec_load's the kernel with its initramfs. 80 func (li *LinuxImage) Load(verbose bool) error { 81 if li.Kernel == nil { 82 return errors.New("LinuxImage.Kernel must be non-nil") 83 } 84 85 kernel, initrd := uio.Reader(util.TryGzipFilter(li.Kernel)), uio.Reader(li.Initrd) 86 if verbose { 87 // In verbose mode, print a dot every 5MiB. It is not pretty, 88 // but it at least proves the files are still downloading. 89 progress := func(r io.Reader) io.Reader { 90 return &uio.ProgressReader{ 91 R: r, 92 Symbol: ".", 93 Interval: 5 * 1024 * 1024, 94 W: os.Stdout, 95 } 96 } 97 kernel = progress(kernel) 98 initrd = progress(initrd) 99 } 100 101 // It seams inefficient to always copy, in particular when the reader 102 // is an io.File but that's not sufficient, os.File could be a socket, 103 // a pipe or some other strange thing. Also kexec_file_load will fail 104 // (similar to execve) if anything as the file opened for writing. 105 // That's unfortunately something we can't guarantee here - unless we 106 // make a copy of the file and dump it somewhere. 107 k, err := copyToFile(kernel) 108 if err != nil { 109 return err 110 } 111 defer k.Close() 112 113 var i *os.File 114 if li.Initrd != nil { 115 i, err = copyToFile(initrd) 116 if err != nil { 117 return err 118 } 119 defer i.Close() 120 } 121 122 log.Printf("Kernel: %s", k.Name()) 123 if i != nil { 124 log.Printf("Initrd: %s", i.Name()) 125 } 126 log.Printf("Command line: %s", li.Cmdline) 127 return kexec.FileLoad(k, i, li.Cmdline) 128 }