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