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