github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/pkg/uroot/util/root.go (about)

     1  // Copyright 2014-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  // +build linux
     6  
     7  // Package util contains various u-root utility functions.
     8  package util
     9  
    10  import (
    11  	"bufio"
    12  	"errors"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"log"
    16  	"os"
    17  	"runtime"
    18  	"strconv"
    19  	"strings"
    20  	"syscall"
    21  
    22  	"github.com/u-root/u-root/pkg/cmdline"
    23  	"golang.org/x/sys/unix"
    24  )
    25  
    26  const (
    27  	// Not all these paths may be populated or even exist but OTOH they might.
    28  	PATHHEAD = "/ubin"
    29  	PATHMID  = "/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin"
    30  	PATHTAIL = "/buildbin:/bbin"
    31  )
    32  
    33  type Creator interface {
    34  	Create() error
    35  	fmt.Stringer
    36  }
    37  
    38  type Dir struct {
    39  	Name string
    40  	Mode os.FileMode
    41  }
    42  
    43  func (d Dir) Create() error {
    44  	return os.MkdirAll(d.Name, d.Mode)
    45  }
    46  
    47  func (d Dir) String() string {
    48  	return fmt.Sprintf("dir %q (mode %#o)", d.Name, d.Mode)
    49  }
    50  
    51  type File struct {
    52  	Name     string
    53  	Contents string
    54  	Mode     os.FileMode
    55  }
    56  
    57  func (f File) Create() error {
    58  	return ioutil.WriteFile(f.Name, []byte(f.Contents), f.Mode)
    59  }
    60  
    61  func (f File) String() string {
    62  	return fmt.Sprintf("file %q (mode %#o)", f.Name, f.Mode)
    63  }
    64  
    65  type Symlink struct {
    66  	Target  string
    67  	NewPath string
    68  }
    69  
    70  func (s Symlink) Create() error {
    71  	os.Remove(s.NewPath)
    72  	return os.Symlink(s.Target, s.NewPath)
    73  }
    74  
    75  func (s Symlink) String() string {
    76  	return fmt.Sprintf("symlink %q -> %q", s.NewPath, s.Target)
    77  }
    78  
    79  type Link struct {
    80  	OldPath string
    81  	NewPath string
    82  }
    83  
    84  func (s Link) Create() error {
    85  	os.Remove(s.NewPath)
    86  	return os.Link(s.OldPath, s.NewPath)
    87  }
    88  
    89  func (s Link) String() string {
    90  	return fmt.Sprintf("link %q -> %q", s.NewPath, s.OldPath)
    91  }
    92  
    93  type Dev struct {
    94  	Name string
    95  	Mode uint32
    96  	Dev  int
    97  }
    98  
    99  func (d Dev) Create() error {
   100  	os.Remove(d.Name)
   101  	return syscall.Mknod(d.Name, d.Mode, d.Dev)
   102  }
   103  
   104  func (d Dev) String() string {
   105  	return fmt.Sprintf("dev %q (mode %#o; magic %d)", d.Name, d.Mode, d.Dev)
   106  }
   107  
   108  type Mount struct {
   109  	Source string
   110  	Target string
   111  	FSType string
   112  	Flags  uintptr
   113  	Opts   string
   114  }
   115  
   116  func (m Mount) Create() error {
   117  	return syscall.Mount(m.Source, m.Target, m.FSType, m.Flags, m.Opts)
   118  }
   119  
   120  func (m Mount) String() string {
   121  	return fmt.Sprintf("mount -t %q -o %s %q %q flags %#x", m.FSType, m.Opts, m.Source, m.Target, m.Flags)
   122  }
   123  
   124  var (
   125  	namespace = []Creator{
   126  		Dir{Name: "/buildbin", Mode: 0777},
   127  		Dir{Name: "/ubin", Mode: 0777},
   128  		Dir{Name: "/tmp", Mode: 0777},
   129  		Dir{Name: "/env", Mode: 0777},
   130  		Dir{Name: "/tcz", Mode: 0777},
   131  		Dir{Name: "/lib", Mode: 0777},
   132  		Dir{Name: "/usr/lib", Mode: 0777},
   133  		Dir{Name: "/var/log", Mode: 0777},
   134  		Dir{Name: "/go/pkg/linux_amd64", Mode: 0777},
   135  
   136  		Dir{Name: "/etc", Mode: 0777},
   137  
   138  		Dir{Name: "/proc", Mode: 0555},
   139  		Mount{Source: "proc", Target: "/proc", FSType: "proc"},
   140  		Mount{Source: "tmpfs", Target: "/tmp", FSType: "tmpfs"},
   141  
   142  		Dir{Name: "/dev", Mode: 0777},
   143  		Dev{Name: "/dev/tty", Mode: syscall.S_IFCHR | 0666, Dev: 0x0500},
   144  		Dev{Name: "/dev/urandom", Mode: syscall.S_IFCHR | 0444, Dev: 0x0109},
   145  		Dev{Name: "/dev/port", Mode: syscall.S_IFCHR | 0640, Dev: 0x0104},
   146  
   147  		// Kernel must be compiled with CONFIG_DEVTMPFS.
   148  		// Note that things kind of work even if this mount fails.
   149  		// TODO: move the Dir commands above below this line?
   150  		Mount{Source: "devtmpfs", Target: "/dev", FSType: "devtmpfs"},
   151  
   152  		Dir{Name: "/dev/pts", Mode: 0777},
   153  		Mount{Source: "devpts", Target: "/dev/pts", FSType: "devpts", Opts: "newinstance,ptmxmode=666,gid=5,mode=620"},
   154  		Dev{Name: "/dev/ptmx", Mode: syscall.S_IFCHR | 0666, Dev: 0x0502},
   155  		// Note: shm is required at least for Chrome. If you don't mount
   156  		// it chrome throws a bogus "out of memory" error, not the more
   157  		// useful "I can't open /dev/shm/whatever". SAD!
   158  		Dir{Name: "/dev/shm", Mode: 0777},
   159  		Mount{Source: "tmpfs", Target: "/dev/shm", FSType: "tmpfs"},
   160  
   161  		Dir{Name: "/sys", Mode: 0555},
   162  		Mount{Source: "sysfs", Target: "/sys", FSType: "sysfs"},
   163  	}
   164  	cgroupsnamespace = []Creator{
   165  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup", FSType: "tmpfs"},
   166  		Dir{Name: "/sys/fs/cgroup/memory", Mode: 0555},
   167  		Dir{Name: "/sys/fs/cgroup/freezer", Mode: 0555},
   168  		Dir{Name: "/sys/fs/cgroup/devices", Mode: 0555},
   169  		Dir{Name: "/sys/fs/cgroup/cpu,cpuacct", Mode: 0555},
   170  		Dir{Name: "/sys/fs/cgroup/blkio", Mode: 0555},
   171  		Dir{Name: "/sys/fs/cgroup/cpuset", Mode: 0555},
   172  		Dir{Name: "/sys/fs/cgroup/pids", Mode: 0555},
   173  		Dir{Name: "/sys/fs/cgroup/net_cls,net_prio", Mode: 0555},
   174  		Dir{Name: "/sys/fs/cgroup/hugetlb", Mode: 0555},
   175  		Dir{Name: "/sys/fs/cgroup/perf_event", Mode: 0555},
   176  		Symlink{NewPath: "/sys/fs/cgroup/cpu", Target: "/sys/fs/cgroup/cpu,cpuacct"},
   177  		Symlink{NewPath: "/sys/fs/cgroup/cpuacct", Target: "/sys/fs/cgroup/cpu,cpuacct"},
   178  		Symlink{NewPath: "/sys/fs/cgroup/net_cls", Target: "/sys/fs/cgroup/net_cls,net_prio"},
   179  		Symlink{NewPath: "/sys/fs/cgroup/net_prio", Target: "/sys/fs/cgroup/net_cls,net_prio"},
   180  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/memory", FSType: "cgroup", Opts: "memory"},
   181  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/freezer", FSType: "cgroup", Opts: "freezer"},
   182  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/devices", FSType: "cgroup", Opts: "devices"},
   183  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/cpu,cpuacct", FSType: "cgroup", Opts: "cpu,cpuacct"},
   184  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/blkio", FSType: "cgroup", Opts: "blkio"},
   185  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/cpuset", FSType: "cgroup", Opts: "cpuset"},
   186  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/pids", FSType: "cgroup", Opts: "pids"},
   187  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/net_cls,net_prio", FSType: "cgroup", Opts: "net_cls,net_prio"},
   188  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/hugetlb", FSType: "cgroup", Opts: "hugetlb"},
   189  		Mount{Source: "cgroup", Target: "/sys/fs/cgroup/perf_event", FSType: "cgroup", Opts: "perf_event"},
   190  	}
   191  
   192  	Env = map[string]string{
   193  		"LD_LIBRARY_PATH": "/usr/local/lib",
   194  		"GOROOT":          "/go",
   195  		"GOPATH":          "/",
   196  		"GOBIN":           "/ubin",
   197  		"CGO_ENABLED":     "0",
   198  	}
   199  )
   200  
   201  func GoBin() string {
   202  	return fmt.Sprintf("/go/bin/%s_%s:/go/bin:/go/pkg/tool/%s_%s", runtime.GOOS, runtime.GOARCH, runtime.GOOS, runtime.GOARCH)
   203  }
   204  
   205  func create(namespace []Creator) {
   206  	// Clear umask bits so that we get stuff like ptmx right.
   207  	m := unix.Umask(0)
   208  	defer unix.Umask(m)
   209  	for _, c := range namespace {
   210  		if err := c.Create(); err != nil {
   211  			log.Printf("Error creating %s: %v", c, err)
   212  		} else {
   213  			log.Printf("Created %v", c)
   214  		}
   215  	}
   216  }
   217  
   218  // build the root file system.
   219  func Rootfs() {
   220  	Env["PATH"] = fmt.Sprintf("%v:%v:%v:%v", GoBin(), PATHHEAD, PATHMID, PATHTAIL)
   221  	for k, v := range Env {
   222  		os.Setenv(k, v)
   223  	}
   224  	create(namespace)
   225  
   226  	// systemd gets upset when it discovers something has already setup cgroups
   227  	// We have to do this after the base namespace is created, so we have /proc
   228  	initFlags := cmdline.GetInitFlagMap()
   229  	systemd, present := initFlags["systemd"]
   230  	systemdEnabled, boolErr := strconv.ParseBool(systemd)
   231  	if !present || boolErr != nil || systemdEnabled == false {
   232  		create(cgroupsnamespace)
   233  	}
   234  
   235  }
   236  
   237  func GetRSDP() (string, error) {
   238  	file, err := os.Open("/sys/firmware/efi/systab")
   239  	if err != nil {
   240  		return "", err
   241  	}
   242  	defer file.Close()
   243  
   244  	const (
   245  		acpi20 = "ACPI20="
   246  		acpi   = "ACPI="
   247  	)
   248  
   249  	scanner := bufio.NewScanner(file)
   250  	for scanner.Scan() {
   251  		line := scanner.Text()
   252  		if strings.HasPrefix(line, acpi20) {
   253  			return strings.TrimPrefix(line, acpi20), nil
   254  		}
   255  		if strings.HasPrefix(line, acpi) {
   256  			return strings.TrimPrefix(line, acpi), nil
   257  		}
   258  	}
   259  	if err := scanner.Err(); err != nil {
   260  		log.Printf("error while reading EFI systab: %v", err)
   261  	}
   262  	return "", errors.New("invalid efi/systab file")
   263  }