1/* switch_root.c - Switch from rootfs/initramfs to another filesystem 2 * 3 * Copyright 2005 Rob Landley <rob@landley.net> 4 5USE_SWITCH_ROOT(NEWTOY(switch_root, "<2c:h", TOYFLAG_SBIN)) 6 7config SWITCH_ROOT 8 bool "switch_root" 9 default y 10 help 11 usage: switch_root [-c /dev/console] NEW_ROOT NEW_INIT... 12 13 Use from PID 1 under initramfs to free initramfs, chroot to NEW_ROOT, 14 and exec NEW_INIT. 15 16 -c Redirect console to device in NEW_ROOT 17 -h Hang instead of exiting on failure (avoids kernel panic) 18*/ 19 20#define FOR_switch_root 21#include "toys.h" 22#include <sys/vfs.h> 23 24GLOBALS( 25 char *console; 26 27 dev_t rootdev; 28) 29 30static int del_node(struct dirtree *node) 31{ 32 if (node->st.st_dev == TT.rootdev && dirtree_notdotdot(node)) { 33 int flag = 0; 34 if (S_ISDIR(node->st.st_mode)) { 35 if (!node->again) return DIRTREE_COMEAGAIN; 36 flag = AT_REMOVEDIR; 37 } 38 unlinkat(dirtree_parentfd(node), node->name, flag); 39 } 40 41 return 0; 42} 43 44void switch_root_main(void) 45{ 46 char *newroot = *toys.optargs, **cmdline = toys.optargs+1; 47 struct stat st1, st2; 48 struct statfs stfs; 49 int console = console; // gcc's "may be used" warnings are broken. 50 51 if (getpid() != 1) error_exit("not pid 1"); 52 53 // Root filesystem we're leaving must be ramfs or tmpfs 54 if (statfs("/", &stfs) || 55 (stfs.f_type != 0x858458f6 && stfs.f_type != 0x01021994)) 56 { 57 error_msg("not ramfs"); 58 goto panic; 59 } 60 61 // New directory must be different filesystem instance 62 if (chdir(newroot) || stat(".", &st1) || stat("/", &st2) || 63 st1.st_dev == st2.st_dev) 64 { 65 error_msg("bad newroot '%s'", newroot); 66 goto panic; 67 } 68 TT.rootdev=st2.st_dev; 69 70 // trim any / characters from the init cmdline, as we want to test it with 71 // stat(), relative to newroot. *cmdline is also used below, but by that 72 // point we are in the chroot, so a relative path is still OK. 73 while (**cmdline == '/') (*cmdline)++; 74 75 // init program must exist and be an executable file 76 if (stat(*cmdline, &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) { 77 error_msg("bad init"); 78 goto panic; 79 } 80 81 if (TT.console && -1 == (console = open(TT.console, O_RDWR))) { 82 perror_msg("bad console '%s'", TT.console); 83 goto panic; 84 } 85 86 // Ok, enough safety checks: wipe root partition. 87 dirtree_read("/", del_node); 88 89 // Fix the appearance of the mount table in the newroot chroot 90 if (mount(".", "/", NULL, MS_MOVE, NULL)) { 91 perror_msg("mount"); 92 goto panic; 93 } 94 95 // Enter the new root before starting init 96 if (chroot(".")) { 97 perror_msg("chroot"); 98 goto panic; 99 } 100 101 // Make sure cwd does not point outside of the chroot 102 if (chdir("/")) { 103 perror_msg("chdir"); 104 goto panic; 105 } 106 107 if (TT.console) { 108 int i; 109 for (i=0; i<3; i++) if (console != i) dup2(console, i); 110 if (console>2) close(console); 111 } 112 execv(*cmdline, cmdline); 113 perror_msg("Failed to exec '%s'", *cmdline); 114panic: 115 if (toys.optflags & FLAG_h) for (;;) wait(NULL); 116} 117