github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/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 "bufio" 10 "errors" 11 "fmt" 12 "log" 13 "os" 14 "path/filepath" 15 "runtime" 16 "strconv" 17 "strings" 18 19 "github.com/mvdan/u-root-coreutils/pkg/cmdline" 20 "github.com/mvdan/u-root-coreutils/pkg/cp" 21 "github.com/mvdan/u-root-coreutils/pkg/kmodule" 22 "github.com/mvdan/u-root-coreutils/pkg/ulog" 23 "golang.org/x/sys/unix" 24 ) 25 26 type Creator interface { 27 Create() error 28 fmt.Stringer 29 } 30 31 type Dir struct { 32 Name string 33 Mode os.FileMode 34 } 35 36 func (d Dir) Create() error { 37 return os.MkdirAll(d.Name, d.Mode) 38 } 39 40 func (d Dir) String() string { 41 return fmt.Sprintf("dir %q (mode %#o)", d.Name, d.Mode) 42 } 43 44 type Symlink struct { 45 Target string 46 NewPath string 47 } 48 49 func (s Symlink) Create() error { 50 os.Remove(s.NewPath) 51 return os.Symlink(s.Target, s.NewPath) 52 } 53 54 func (s Symlink) String() string { 55 return fmt.Sprintf("symlink %q -> %q", s.NewPath, s.Target) 56 } 57 58 type Dev struct { 59 Name string 60 Mode uint32 61 Dev int 62 } 63 64 func (d Dev) Create() error { 65 os.Remove(d.Name) 66 return unix.Mknod(d.Name, d.Mode, d.Dev) 67 } 68 69 func (d Dev) String() string { 70 return fmt.Sprintf("dev %q (mode %#o; magic %d)", d.Name, d.Mode, d.Dev) 71 } 72 73 type Mount struct { 74 Source string 75 Target string 76 FSType string 77 Flags uintptr 78 Opts string 79 } 80 81 func (m Mount) Create() error { 82 return unix.Mount(m.Source, m.Target, m.FSType, m.Flags, m.Opts) 83 } 84 85 func (m Mount) String() string { 86 return fmt.Sprintf("mount -t %q -o %s %q %q flags %#x", m.FSType, m.Opts, m.Source, m.Target, m.Flags) 87 } 88 89 type CpDir struct { 90 Source string 91 Target string 92 } 93 94 func (c CpDir) Create() error { 95 return cp.CopyTree(c.Source, c.Target) 96 } 97 98 func (c CpDir) String() string { 99 return fmt.Sprintf("cp -a %q %q", c.Source, c.Target) 100 } 101 102 var ( 103 // These have to be created / mounted first, so that the logging works correctly. 104 PreNamespace = []Creator{ 105 Dir{Name: "/dev", Mode: 0o777}, 106 107 // Kernel must be compiled with CONFIG_DEVTMPFS. 108 Mount{Source: "devtmpfs", Target: "/dev", FSType: "devtmpfs"}, 109 } 110 Namespace = []Creator{ 111 Dir{Name: "/buildbin", Mode: 0o777}, 112 Dir{Name: "/ubin", Mode: 0o777}, 113 Dir{Name: "/tmp", Mode: 0o777}, 114 Dir{Name: "/env", Mode: 0o777}, 115 Dir{Name: "/tcz", Mode: 0o777}, 116 Dir{Name: "/lib", Mode: 0o777}, 117 Dir{Name: "/usr/lib", Mode: 0o777}, 118 Dir{Name: "/var/log", Mode: 0o777}, 119 Dir{Name: "/go/pkg/linux_amd64", Mode: 0o777}, 120 121 Dir{Name: "/etc", Mode: 0o777}, 122 123 Dir{Name: "/proc", Mode: 0o555}, 124 Mount{Source: "proc", Target: "/proc", FSType: "proc"}, 125 Mount{Source: "tmpfs", Target: "/tmp", FSType: "tmpfs"}, 126 127 Dev{Name: "/dev/tty", Mode: unix.S_IFCHR | 0o666, Dev: 0x0500}, 128 Dev{Name: "/dev/urandom", Mode: unix.S_IFCHR | 0o444, Dev: 0x0109}, 129 Dev{Name: "/dev/port", Mode: unix.S_IFCHR | 0o640, Dev: 0x0104}, 130 Dev{Name: "/dev/ttyhvc0", Mode: unix.S_IFCHR | 0o666, Dev: 0xe500}, 131 132 Dir{Name: "/dev/pts", Mode: 0o777}, 133 Mount{Source: "devpts", Target: "/dev/pts", FSType: "devpts", Opts: "newinstance,ptmxmode=666,gid=5,mode=620"}, 134 // Note: if we mount /dev/pts with "newinstance", we *must* make "/dev/ptmx" a symlink to "/dev/pts/ptmx" 135 Symlink{NewPath: "/dev/ptmx", Target: "/dev/pts/ptmx"}, 136 // Note: shm is required at least for Chrome. If you don't mount 137 // it chrome throws a bogus "out of memory" error, not the more 138 // useful "I can't open /dev/shm/whatever". SAD! 139 Dir{Name: "/dev/shm", Mode: 0o777}, 140 Mount{Source: "tmpfs", Target: "/dev/shm", FSType: "tmpfs"}, 141 142 Dir{Name: "/sys", Mode: 0o555}, 143 Mount{Source: "sysfs", Target: "/sys", FSType: "sysfs"}, 144 Mount{Source: "securityfs", Target: "/sys/kernel/security", FSType: "securityfs"}, 145 Mount{Source: "efivarfs", Target: "/sys/firmware/efi/efivars", FSType: "efivarfs"}, 146 Mount{Source: "debugfs", Target: "/sys/kernel/debug", FSType: "debugfs"}, 147 148 CpDir{Source: "/etc", Target: "/tmp/etc"}, 149 Mount{Source: "/tmp/etc", Target: "/etc", FSType: "tmpfs", Flags: unix.MS_BIND}, 150 } 151 152 // cgroups are optional for most u-root users, especially 153 // LinuxBoot/NERF. Some users use u-root for container stuff. 154 CgroupsNamespace = []Creator{ 155 Mount{Source: "cgroup", Target: "/sys/fs/cgroup", FSType: "tmpfs"}, 156 Dir{Name: "/sys/fs/cgroup/memory", Mode: 0o555}, 157 Dir{Name: "/sys/fs/cgroup/freezer", Mode: 0o555}, 158 Dir{Name: "/sys/fs/cgroup/devices", Mode: 0o555}, 159 Dir{Name: "/sys/fs/cgroup/cpu,cpuacct", Mode: 0o555}, 160 Dir{Name: "/sys/fs/cgroup/blkio", Mode: 0o555}, 161 Dir{Name: "/sys/fs/cgroup/cpuset", Mode: 0o555}, 162 Dir{Name: "/sys/fs/cgroup/pids", Mode: 0o555}, 163 Dir{Name: "/sys/fs/cgroup/net_cls,net_prio", Mode: 0o555}, 164 Dir{Name: "/sys/fs/cgroup/hugetlb", Mode: 0o555}, 165 Dir{Name: "/sys/fs/cgroup/perf_event", Mode: 0o555}, 166 Symlink{NewPath: "/sys/fs/cgroup/cpu", Target: "/sys/fs/cgroup/cpu,cpuacct"}, 167 Symlink{NewPath: "/sys/fs/cgroup/cpuacct", Target: "/sys/fs/cgroup/cpu,cpuacct"}, 168 Symlink{NewPath: "/sys/fs/cgroup/net_cls", Target: "/sys/fs/cgroup/net_cls,net_prio"}, 169 Symlink{NewPath: "/sys/fs/cgroup/net_prio", Target: "/sys/fs/cgroup/net_cls,net_prio"}, 170 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/memory", FSType: "cgroup", Opts: "memory"}, 171 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/freezer", FSType: "cgroup", Opts: "freezer"}, 172 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/devices", FSType: "cgroup", Opts: "devices"}, 173 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/cpu,cpuacct", FSType: "cgroup", Opts: "cpu,cpuacct"}, 174 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/blkio", FSType: "cgroup", Opts: "blkio"}, 175 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/cpuset", FSType: "cgroup", Opts: "cpuset"}, 176 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/pids", FSType: "cgroup", Opts: "pids"}, 177 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/net_cls,net_prio", FSType: "cgroup", Opts: "net_cls,net_prio"}, 178 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/hugetlb", FSType: "cgroup", Opts: "hugetlb"}, 179 Mount{Source: "cgroup", Target: "/sys/fs/cgroup/perf_event", FSType: "cgroup", Opts: "perf_event"}, 180 } 181 ) 182 183 func goBin() string { 184 return fmt.Sprintf("/go/bin/%s_%s:/go/bin:/go/pkg/tool/%s_%s", runtime.GOOS, runtime.GOARCH, runtime.GOOS, runtime.GOARCH) 185 } 186 187 func Create(namespace []Creator, optional bool) { 188 // Clear umask bits so that we get stuff like ptmx right. 189 m := unix.Umask(0) 190 defer unix.Umask(m) 191 for _, c := range namespace { 192 if err := c.Create(); err != nil { 193 if optional { 194 ulog.KernelLog.Printf("u-root init [optional]: warning creating %s: %v", c, err) 195 } else { 196 ulog.KernelLog.Printf("u-root init: error creating %s: %v", c, err) 197 } 198 } 199 } 200 } 201 202 // SetEnv sets the default u-root environment. 203 func SetEnv() { 204 env := map[string]string{ 205 "LD_LIBRARY_PATH": "/usr/local/lib", 206 "GOROOT": "/go", 207 "GOPATH": "/", 208 "GOBIN": "/ubin", 209 "CGO_ENABLED": "0", 210 "USER": "root", 211 } 212 213 // Not all these paths may be populated or even exist but OTOH they might. 214 path := "/ubin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin:/buildbin:/bbin" 215 216 env["PATH"] = fmt.Sprintf("%v:%v", goBin(), path) 217 for k, v := range env { 218 os.Setenv(k, v) 219 } 220 } 221 222 // CreateRootfs creates the default u-root file system. 223 func CreateRootfs() { 224 // Mount devtmpfs, then open /dev/kmsg with Reinit. 225 Create(PreNamespace, false) 226 ulog.KernelLog.Reinit() 227 228 Create(Namespace, false) 229 230 // systemd gets upset when it discovers something has already setup cgroups 231 // We have to do this after the base namespace is created, so we have /proc 232 initFlags := cmdline.GetInitFlagMap() 233 systemd, present := initFlags["systemd"] 234 systemdEnabled, boolErr := strconv.ParseBool(systemd) 235 if !present || boolErr != nil || !systemdEnabled { 236 Create(CgroupsNamespace, true) 237 } 238 } 239 240 // InitModuleLoader wraps the resources we need for early module loading 241 type InitModuleLoader struct { 242 Cmdline *cmdline.CmdLine 243 Prober func(name string, modParameters string) error 244 ExcludedMods map[string]bool 245 } 246 247 func (i *InitModuleLoader) IsExcluded(mod string) bool { 248 return i.ExcludedMods[mod] 249 } 250 251 func (i *InitModuleLoader) LoadModule(mod string) error { 252 flags := i.Cmdline.FlagsForModule(mod) 253 if err := i.Prober(mod, flags); err != nil { 254 return fmt.Errorf("failed to load module: %s", err) 255 } 256 return nil 257 } 258 259 func NewInitModuleLoader() *InitModuleLoader { 260 return &InitModuleLoader{ 261 Cmdline: cmdline.NewCmdLine(), 262 Prober: kmodule.Probe, 263 ExcludedMods: map[string]bool{ 264 "idpf": true, 265 "idpf_imc": true, 266 }, 267 } 268 } 269 270 // InstallAllModules installs kernel modules form the following locations in order: 271 // - .ko files from /lib/modules 272 // - modules found in .conf files from /lib/modules-load.d/ 273 // - modules found in the cmdline argument modules_load= separated by , 274 // Useful for modules that need to be loaded for boot (ie a network 275 // driver needed for netboot). It skips over blacklisted modules in 276 // excludedMods. 277 func InstallAllModules() error { 278 loader := NewInitModuleLoader() 279 modulePattern := "/lib/modules/*.ko" 280 if err := InstallModulesFromDir(modulePattern, loader); !errors.Is(err, ErrNoModulesFound) { 281 return err 282 } 283 var allModules []string 284 moduleConfPattern := "/lib/modules-load.d/*.conf" 285 modules, err := GetModulesFromConf(moduleConfPattern) 286 if err != nil { 287 return err 288 } 289 allModules = append(allModules, modules...) 290 modules, err = GetModulesFromCmdline(loader) 291 if err != nil { 292 return err 293 } 294 allModules = append(allModules, modules...) 295 InstallModules(loader, allModules) 296 return nil 297 } 298 299 // InstallModules installs the passed modules using the InitModuleLoader 300 func InstallModules(m *InitModuleLoader, modules []string) { 301 for _, moduleName := range modules { 302 if m.IsExcluded(moduleName) { 303 log.Printf("Skipping module %q", moduleName) 304 continue 305 } 306 if err := m.LoadModule(moduleName); err != nil { 307 log.Printf("InstallModulesFromModulesLoad: can't install %q: %v", moduleName, err) 308 } 309 } 310 } 311 312 // ErrNoModulesFound is the error returned when InstallModulesFromDir does not 313 // find any valid modules in the path. 314 var ErrNoModulesFound = fmt.Errorf("no modules found") 315 316 // InstallModulesFromDir installs kernel modules (.ko files) from /lib/modules that 317 // match the given pattern, skipping those in the exclude list. 318 func InstallModulesFromDir(pattern string, loader *InitModuleLoader) error { 319 files, err := filepath.Glob(pattern) 320 if err != nil { 321 return err 322 } 323 if len(files) == 0 { 324 return ErrNoModulesFound 325 } 326 327 for _, filename := range files { 328 f, err := os.Open(filename) 329 if err != nil { 330 log.Printf("InstallModules: can't open %q: %v", filename, err) 331 continue 332 } 333 defer f.Close() 334 // Module flags are passed to the command line in the from modulename.flag=val 335 // And must be passed to FileInit as flag=val to be installed properly 336 moduleName := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)) 337 if loader.IsExcluded(moduleName) { 338 log.Printf("Skipping module %q", moduleName) 339 continue 340 } 341 342 flags := cmdline.FlagsForModule(moduleName) 343 if err = kmodule.FileInit(f, flags, 0); err != nil { 344 log.Printf("InstallModules: can't install %q: %v", filename, err) 345 } 346 } 347 348 return nil 349 } 350 351 func readModules(f *os.File) []string { 352 scanner := bufio.NewScanner(f) 353 modules := []string{} 354 for scanner.Scan() { 355 i := scanner.Text() 356 i = strings.TrimSpace(i) 357 if i == "" || strings.HasPrefix(i, "#") { 358 continue 359 } 360 modules = append(modules, i) 361 } 362 if err := scanner.Err(); err != nil { 363 log.Println("error on reading:", err) 364 } 365 return modules 366 } 367 368 // GetModulesFromConf finds kernel modules from .conf files in /lib/modules-load.d/ 369 func GetModulesFromConf(pattern string) ([]string, error) { 370 var ret []string 371 files, err := filepath.Glob(pattern) 372 if err != nil { 373 return nil, err 374 } 375 for _, filename := range files { 376 f, err := os.Open(filename) 377 if err != nil { 378 log.Printf("InstallModulesFromModulesLoad: can't open %q: %v", filename, err) 379 continue 380 } 381 defer f.Close() 382 modules := readModules(f) 383 ret = append(ret, modules...) 384 } 385 return ret, nil 386 } 387 388 // GetModulesFromCmdline finds kernel modules from the modules_load kernel parameter 389 func GetModulesFromCmdline(m *InitModuleLoader) ([]string, error) { 390 var ret []string 391 modules, present := m.Cmdline.Flag("modules_load") 392 if !present { 393 return nil, nil 394 } 395 396 for _, moduleName := range strings.Split(modules, ",") { 397 moduleName = strings.TrimSpace(moduleName) 398 ret = append(ret, moduleName) 399 } 400 return ret, nil 401 }