github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/libinit/root_linux.go (about)

     1  // Copyright 2014-2019 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 libinit creates the environment and root file system for u-root.
     6  package libinit
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"runtime"
    12  	"strconv"
    13  	"syscall"
    14  
    15  	"github.com/u-root/u-root/pkg/cmdline"
    16  	"github.com/u-root/u-root/pkg/ulog"
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  type creator interface {
    21  	create() error
    22  	fmt.Stringer
    23  }
    24  
    25  type dir struct {
    26  	Name string
    27  	Mode os.FileMode
    28  }
    29  
    30  func (d dir) create() error {
    31  	return os.MkdirAll(d.Name, d.Mode)
    32  }
    33  
    34  func (d dir) String() string {
    35  	return fmt.Sprintf("dir %q (mode %#o)", d.Name, d.Mode)
    36  }
    37  
    38  type symlink struct {
    39  	Target  string
    40  	NewPath string
    41  }
    42  
    43  func (s symlink) create() error {
    44  	os.Remove(s.NewPath)
    45  	return os.Symlink(s.Target, s.NewPath)
    46  }
    47  
    48  func (s symlink) String() string {
    49  	return fmt.Sprintf("symlink %q -> %q", s.NewPath, s.Target)
    50  }
    51  
    52  type dev struct {
    53  	Name string
    54  	Mode uint32
    55  	Dev  int
    56  }
    57  
    58  func (d dev) create() error {
    59  	os.Remove(d.Name)
    60  	return syscall.Mknod(d.Name, d.Mode, d.Dev)
    61  }
    62  
    63  func (d dev) String() string {
    64  	return fmt.Sprintf("dev %q (mode %#o; magic %d)", d.Name, d.Mode, d.Dev)
    65  }
    66  
    67  type mount struct {
    68  	Source string
    69  	Target string
    70  	FSType string
    71  	Flags  uintptr
    72  	Opts   string
    73  }
    74  
    75  func (m mount) create() error {
    76  	return syscall.Mount(m.Source, m.Target, m.FSType, m.Flags, m.Opts)
    77  }
    78  
    79  func (m mount) String() string {
    80  	return fmt.Sprintf("mount -t %q -o %s %q %q flags %#x", m.FSType, m.Opts, m.Source, m.Target, m.Flags)
    81  }
    82  
    83  var (
    84  	// These have to be created / mounted first, so that the logging works correctly.
    85  	preNamespace = []creator{
    86  		dir{Name: "/dev", Mode: 0777},
    87  
    88  		// Kernel must be compiled with CONFIG_DEVTMPFS.
    89  		mount{Source: "devtmpfs", Target: "/dev", FSType: "devtmpfs"},
    90  	}
    91  	namespace = []creator{
    92  		dir{Name: "/buildbin", Mode: 0777},
    93  		dir{Name: "/ubin", Mode: 0777},
    94  		dir{Name: "/tmp", Mode: 0777},
    95  		dir{Name: "/env", Mode: 0777},
    96  		dir{Name: "/tcz", Mode: 0777},
    97  		dir{Name: "/lib", Mode: 0777},
    98  		dir{Name: "/usr/lib", Mode: 0777},
    99  		dir{Name: "/var/log", Mode: 0777},
   100  		dir{Name: "/go/pkg/linux_amd64", Mode: 0777},
   101  
   102  		dir{Name: "/etc", Mode: 0777},
   103  
   104  		dir{Name: "/proc", Mode: 0555},
   105  		mount{Source: "proc", Target: "/proc", FSType: "proc"},
   106  		mount{Source: "tmpfs", Target: "/tmp", FSType: "tmpfs"},
   107  
   108  		dev{Name: "/dev/tty", Mode: syscall.S_IFCHR | 0666, Dev: 0x0500},
   109  		dev{Name: "/dev/urandom", Mode: syscall.S_IFCHR | 0444, Dev: 0x0109},
   110  		dev{Name: "/dev/port", Mode: syscall.S_IFCHR | 0640, Dev: 0x0104},
   111  
   112  		dir{Name: "/dev/pts", Mode: 0777},
   113  		mount{Source: "devpts", Target: "/dev/pts", FSType: "devpts", Opts: "newinstance,ptmxmode=666,gid=5,mode=620"},
   114  		// Note: if we mount /dev/pts with "newinstance", we *must* make "/dev/ptmx" a symlink to "/dev/pts/ptmx"
   115  		symlink{NewPath: "/dev/ptmx", Target: "/dev/pts/ptmx"},
   116  		// Note: shm is required at least for Chrome. If you don't mount
   117  		// it chrome throws a bogus "out of memory" error, not the more
   118  		// useful "I can't open /dev/shm/whatever". SAD!
   119  		dir{Name: "/dev/shm", Mode: 0777},
   120  		mount{Source: "tmpfs", Target: "/dev/shm", FSType: "tmpfs"},
   121  
   122  		dir{Name: "/sys", Mode: 0555},
   123  		mount{Source: "sysfs", Target: "/sys", FSType: "sysfs"},
   124  		mount{Source: "securityfs", Target: "/sys/kernel/security", FSType: "securityfs"},
   125  	}
   126  	cgroupsnamespace = []creator{
   127  		mount{Source: "cgroup", Target: "/sys/fs/cgroup", FSType: "tmpfs"},
   128  		dir{Name: "/sys/fs/cgroup/memory", Mode: 0555},
   129  		dir{Name: "/sys/fs/cgroup/freezer", Mode: 0555},
   130  		dir{Name: "/sys/fs/cgroup/devices", Mode: 0555},
   131  		dir{Name: "/sys/fs/cgroup/cpu,cpuacct", Mode: 0555},
   132  		dir{Name: "/sys/fs/cgroup/blkio", Mode: 0555},
   133  		dir{Name: "/sys/fs/cgroup/cpuset", Mode: 0555},
   134  		dir{Name: "/sys/fs/cgroup/pids", Mode: 0555},
   135  		dir{Name: "/sys/fs/cgroup/net_cls,net_prio", Mode: 0555},
   136  		dir{Name: "/sys/fs/cgroup/hugetlb", Mode: 0555},
   137  		dir{Name: "/sys/fs/cgroup/perf_event", Mode: 0555},
   138  		symlink{NewPath: "/sys/fs/cgroup/cpu", Target: "/sys/fs/cgroup/cpu,cpuacct"},
   139  		symlink{NewPath: "/sys/fs/cgroup/cpuacct", Target: "/sys/fs/cgroup/cpu,cpuacct"},
   140  		symlink{NewPath: "/sys/fs/cgroup/net_cls", Target: "/sys/fs/cgroup/net_cls,net_prio"},
   141  		symlink{NewPath: "/sys/fs/cgroup/net_prio", Target: "/sys/fs/cgroup/net_cls,net_prio"},
   142  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/memory", FSType: "cgroup", Opts: "memory"},
   143  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/freezer", FSType: "cgroup", Opts: "freezer"},
   144  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/devices", FSType: "cgroup", Opts: "devices"},
   145  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/cpu,cpuacct", FSType: "cgroup", Opts: "cpu,cpuacct"},
   146  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/blkio", FSType: "cgroup", Opts: "blkio"},
   147  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/cpuset", FSType: "cgroup", Opts: "cpuset"},
   148  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/pids", FSType: "cgroup", Opts: "pids"},
   149  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/net_cls,net_prio", FSType: "cgroup", Opts: "net_cls,net_prio"},
   150  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/hugetlb", FSType: "cgroup", Opts: "hugetlb"},
   151  		mount{Source: "cgroup", Target: "/sys/fs/cgroup/perf_event", FSType: "cgroup", Opts: "perf_event"},
   152  	}
   153  )
   154  
   155  func goBin() string {
   156  	return fmt.Sprintf("/go/bin/%s_%s:/go/bin:/go/pkg/tool/%s_%s", runtime.GOOS, runtime.GOARCH, runtime.GOOS, runtime.GOARCH)
   157  }
   158  
   159  func create(namespace []creator) {
   160  	// Clear umask bits so that we get stuff like ptmx right.
   161  	m := unix.Umask(0)
   162  	defer unix.Umask(m)
   163  	for _, c := range namespace {
   164  		if err := c.create(); err != nil {
   165  			ulog.KernelLog.Printf("u-root init: error creating %s: %v", c, err)
   166  		}
   167  	}
   168  }
   169  
   170  // SetEnv sets the default u-root environment.
   171  func SetEnv() {
   172  	env := map[string]string{
   173  		"LD_LIBRARY_PATH": "/usr/local/lib",
   174  		"GOROOT":          "/go",
   175  		"GOPATH":          "/",
   176  		"GOBIN":           "/ubin",
   177  		"CGO_ENABLED":     "0",
   178  	}
   179  
   180  	// Not all these paths may be populated or even exist but OTOH they might.
   181  	path := "/ubin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin:/buildbin:/bbin"
   182  
   183  	env["PATH"] = fmt.Sprintf("%v:%v", goBin(), path)
   184  	for k, v := range env {
   185  		os.Setenv(k, v)
   186  	}
   187  }
   188  
   189  // CreateRootfs creates the default u-root file system.
   190  func CreateRootfs() {
   191  	// Mount devtmpfs, then open /dev/kmsg with Reinit.
   192  	create(preNamespace)
   193  	ulog.KernelLog.Reinit()
   194  
   195  	create(namespace)
   196  
   197  	// systemd gets upset when it discovers something has already setup cgroups
   198  	// We have to do this after the base namespace is created, so we have /proc
   199  	initFlags := cmdline.GetInitFlagMap()
   200  	systemd, present := initFlags["systemd"]
   201  	systemdEnabled, boolErr := strconv.ParseBool(systemd)
   202  	if !present || boolErr != nil || !systemdEnabled {
   203  		create(cgroupsnamespace)
   204  	}
   205  }