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