github.com/rck/u-root@v0.0.0-20180106144920-7eb602e381bb/pkg/ramfs/ramfs.go (about)

     1  // Copyright 2015-2017 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 ramfs
     6  
     7  import (
     8  	"os"
     9  	"path/filepath"
    10  	"syscall"
    11  
    12  	"github.com/u-root/u-root/pkg/cpio"
    13  	_ "github.com/u-root/u-root/pkg/cpio/newc"
    14  )
    15  
    16  const (
    17  	d = syscall.S_IFDIR
    18  	c = syscall.S_IFCHR
    19  	b = syscall.S_IFBLK
    20  	f = syscall.S_IFREG
    21  
    22  	// This is the literal timezone file for GMT-0. Given that we have no idea
    23  	// where we will be running, GMT seems a reasonable guess. If it matters,
    24  	// setup code should download and change this to something else.
    25  	gmt0       = "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x04\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00GMT\x00\x00\x00\nGMT0\n"
    26  	nameserver = "nameserver 8.8.8.8\n"
    27  )
    28  
    29  // devCPIOrecords are cpio records as defined in the uroot cpio package.
    30  // Most of the bits can be left unspecified: these all have one link,
    31  // they are mostly root:root, for example.
    32  var DevCPIO = []cpio.Record{
    33  	{Info: cpio.Info{Name: "tcz", Mode: d | 0755}},
    34  	{Info: cpio.Info{Name: "etc", Mode: d | 0755}},
    35  	{Info: cpio.Info{Name: "dev", Mode: d | 0755}},
    36  	{Info: cpio.Info{Name: "ubin", Mode: d | 0755}},
    37  	{Info: cpio.Info{Name: "usr", Mode: d | 0755}},
    38  	{Info: cpio.Info{Name: "usr/lib", Mode: d | 0755}},
    39  	{Info: cpio.Info{Name: "lib64", Mode: d | 0755}},
    40  	{Info: cpio.Info{Name: "bin", Mode: d | 0755}},
    41  	{Info: cpio.Info{Name: "dev/console", Mode: c | 0600, Rmajor: 5, Rminor: 1}},
    42  	{Info: cpio.Info{Name: "dev/tty", Mode: c | 0666, Rmajor: 5, Rminor: 0}},
    43  	{Info: cpio.Info{Name: "dev/null", Mode: c | 0666, Rmajor: 1, Rminor: 3}},
    44  	{Info: cpio.Info{Name: "dev/port", Mode: c | 0640, Rmajor: 1, Rminor: 4}},
    45  	{Info: cpio.Info{Name: "dev/urandom", Mode: c | 0666, Rmajor: 1, Rminor: 9}},
    46  	{Info: cpio.Info{Name: "etc/resolv.conf", Mode: f | 0644, FileSize: uint64(len(nameserver))}, ReadCloser: cpio.NewBytesReadCloser([]byte(nameserver))},
    47  	{Info: cpio.Info{Name: "etc/localtime", Mode: f | 0644, FileSize: uint64(len(gmt0))}, ReadCloser: cpio.NewBytesReadCloser([]byte(gmt0))},
    48  }
    49  
    50  type Initramfs struct {
    51  	cpio.Writer
    52  	files map[string]struct{}
    53  }
    54  
    55  func NewInitramfs(w cpio.Writer) (*Initramfs, error) {
    56  	// Write devtmpfs records.
    57  	dcpio := DevCPIO[:]
    58  	cpio.MakeAllReproducible(dcpio)
    59  	if err := w.WriteRecords(dcpio); err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	return &Initramfs{
    64  		Writer: w,
    65  		files:  make(map[string]struct{}),
    66  	}, nil
    67  }
    68  
    69  func (i *Initramfs) WriteRecord(r cpio.Record) error {
    70  	if r.Name == "." || r.Name == "/" {
    71  		return nil
    72  	}
    73  
    74  	// Create record for parent directory if needed.
    75  	dir := filepath.Dir(r.Name)
    76  	if _, ok := i.files[dir]; dir != "/" && dir != "." && !ok {
    77  		if err := i.WriteRecord(cpio.Record{
    78  			Info: cpio.Info{
    79  				Name: dir,
    80  				Mode: syscall.S_IFDIR | 0755,
    81  			},
    82  		}); err != nil {
    83  			return err
    84  		}
    85  	}
    86  
    87  	i.files[r.Name] = struct{}{}
    88  	return i.Writer.WriteRecord(r)
    89  }
    90  
    91  func (i *Initramfs) WriteFile(src string, dest string) error {
    92  	record, err := cpio.GetRecord(src)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	if record.Info.Mode&^0777 == syscall.S_IFDIR {
    98  		return children(src, func(name string) error {
    99  			return i.WriteFile(filepath.Join(src, name), filepath.Join(dest, name))
   100  		})
   101  	} else {
   102  		// Fix the name.
   103  		record.Name = dest
   104  		return i.WriteRecord(cpio.MakeReproducible(record))
   105  	}
   106  }
   107  
   108  func children(dir string, fn func(name string) error) error {
   109  	f, err := os.Open(dir)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	names, err := f.Readdirnames(-1)
   114  	f.Close()
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	for _, name := range names {
   120  		if err := fn(name); os.IsNotExist(err) {
   121  			// File was deleted in the meantime.
   122  			continue
   123  		} else if err != nil {
   124  			return err
   125  		}
   126  	}
   127  	return nil
   128  }
   129  
   130  // Copy all files relative to `srcDir` to `destDir` in the cpio archive.
   131  func (i *Initramfs) WriteFiles(srcDir string, destDir string, files []string) error {
   132  	for _, file := range files {
   133  		srcPath := filepath.Join(srcDir, file)
   134  		destPath := filepath.Join(destDir, file)
   135  		if err := i.WriteFile(srcPath, destPath); err != nil {
   136  			return err
   137  		}
   138  	}
   139  	return nil
   140  }