On a train ride to Bruxelles, brought out my axe and directly attacked run_init(8). run_init(8) is dead, long live switch_root(8). The next run on switch_root(8) involves fdopendir, so another push for the upcoming stdio 1.6 branch. The following is boot tested with initramfs-tools, kinit(8) tests would very much be appreciated!? Michal Suchanek (1): [klibc] switch_root: Fix single file mounts maximilian attems (7): [klibc] switch_root: rename from run-init [klibc] switch_root: checkpatch cleanup [klibc] switch_root: let the utility verify passed args [klibc] switch_root: add + enhance -h help invocation [klibc] switch_root: check that init is executable. [klibc] switch_root: Don't fail due to stray mount [klibc] switch_root: Move mount /dev, /proc, /sys to new root usr/kinit/Kbuild | 6 +- usr/kinit/kinit.c | 36 +---- usr/kinit/nfsmount/README.locking | 2 +- usr/kinit/run-init/Kbuild | 29 ---- usr/kinit/run-init/run-init.c | 93 ------------- usr/kinit/run-init/run-init.h | 34 ----- usr/kinit/run-init/runinitlib.c | 209 ----------------------------- usr/kinit/switch_root/Kbuild | 29 ++++ usr/kinit/switch_root/switch_root.c | 98 ++++++++++++++ usr/kinit/switch_root/switch_root.h | 34 +++++ usr/kinit/switch_root/switch_rootlib.c | 230 ++++++++++++++++++++++++++++++++ 11 files changed, 402 insertions(+), 398 deletions(-) delete mode 100644 usr/kinit/run-init/Kbuild delete mode 100644 usr/kinit/run-init/run-init.c delete mode 100644 usr/kinit/run-init/run-init.h delete mode 100644 usr/kinit/run-init/runinitlib.c create mode 100644 usr/kinit/switch_root/Kbuild create mode 100644 usr/kinit/switch_root/switch_root.c create mode 100644 usr/kinit/switch_root/switch_root.h create mode 100644 usr/kinit/switch_root/switch_rootlib.c -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 1/8] switch_root: rename from run-init
This seems the name people have settled in. This is the first step to enhance switch_root() (No code changes itself). Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/Kbuild | 6 +- usr/kinit/kinit.c | 6 +- usr/kinit/nfsmount/README.locking | 2 +- usr/kinit/run-init/Kbuild | 29 ----- usr/kinit/run-init/run-init.c | 93 -------------- usr/kinit/run-init/run-init.h | 34 ----- usr/kinit/run-init/runinitlib.c | 209 -------------------------------- usr/kinit/switch_root/Kbuild | 29 +++++ usr/kinit/switch_root/switch_root.c | 93 ++++++++++++++ usr/kinit/switch_root/switch_root.h | 34 +++++ usr/kinit/switch_root/switch_rootlib.c | 209 ++++++++++++++++++++++++++++++++ 11 files changed, 372 insertions(+), 372 deletions(-) delete mode 100644 usr/kinit/run-init/Kbuild delete mode 100644 usr/kinit/run-init/run-init.c delete mode 100644 usr/kinit/run-init/run-init.h delete mode 100644 usr/kinit/run-init/runinitlib.c create mode 100644 usr/kinit/switch_root/Kbuild create mode 100644 usr/kinit/switch_root/switch_root.c create mode 100644 usr/kinit/switch_root/switch_root.h create mode 100644 usr/kinit/switch_root/switch_rootlib.c diff --git a/usr/kinit/Kbuild b/usr/kinit/Kbuild index ff1d449..4b2b2fe 100644 --- a/usr/kinit/Kbuild +++ b/usr/kinit/Kbuild @@ -13,7 +13,7 @@ kinit-y += do_mounts_md.o do_mounts_mtd.o nfsroot.o kinit-y += ipconfig/ kinit-y += nfsmount/ -kinit-y += run-init/ +kinit-y += switch_root/ kinit-y += fstype/ kinit-y += resume/ @@ -26,11 +26,11 @@ KLIBCCFLAGS += -I$(srctree)/$(src)/fstype \ -I$(srctree)/$(src)/ipconfig \ -I$(srctree)/$(src)/nfsmount \ -I$(srctree)/$(src)/resume \ - -I$(srctree)/$(src)/run-init + -I$(srctree)/$(src)/switch_root # Cleaning targets += kinit kinit.g kinit.shared kinit.shared.g -subdir- := fstype ipconfig nfsmount resume run-init +subdir- := fstype ipconfig nfsmount resume switch_root # install binary diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c index 4a1f40b..9495d89 100644 --- a/usr/kinit/kinit.c +++ b/usr/kinit/kinit.c @@ -12,7 +12,7 @@ #include "kinit.h" #include "ipconfig.h" -#include "run-init.h" +#include "switch_root.h" #include "resume.h" const char *progname = "kinit"; @@ -307,9 +307,9 @@ int main(int argc, char *argv[]) init_argv[0] = strrchr(init_path, '/') + 1; - errmsg = run_init("/root", "/dev/console", init_path, init_argv); + errmsg = switch_root("/root", "/dev/console", init_path, init_argv); - /* If run_init returned, something went bad */ + /* If switch_root returned, something went bad */ fprintf(stderr, "%s: %s: %s\n", progname, errmsg, strerror(errno)); ret = 2; goto bail; diff --git a/usr/kinit/nfsmount/README.locking b/usr/kinit/nfsmount/README.locking index bf2e8e7..4576195 100644 --- a/usr/kinit/nfsmount/README.locking +++ b/usr/kinit/nfsmount/README.locking @@ -14,7 +14,7 @@ pmap_file can be /dev/null. b) Allow the kernel to bind to any port and use the file produced by nfsroot to feed to pmap_set (it should be directly compatible); this means the file needs to be transferred to a place where the "real -root" can find it before run-init. +root" can find it before switch_root. In either case, it is imperative that the real portmapper is launched before any program actually tries to create locks! diff --git a/usr/kinit/run-init/Kbuild b/usr/kinit/run-init/Kbuild deleted file mode 100644 index bf6e140..0000000 --- a/usr/kinit/run-init/Kbuild +++ /dev/null @@ -1,29 +0,0 @@ -# -# Kbuild file for run-init -# - -static-y := static/run-init -shared-y := shared/run-init - -# common .o files -objs := run-init.o runinitlib.o - -# TODO - do we want a stripped version -# TODO - do we want the static.g + shared.g directories? - -# Create built-in.o with all object files (used by kinit) -lib-y := $(objs) - -# force run-init to not have an executable stack (to keep READ_IMPLIES_EXEC -# personality(2) flag from getting set and passed to init). -EXTRA_KLIBCLDFLAGS += -z noexecstack - -# .o files used to built executables -static/run-init-y := $(objs) -shared/run-init-y := $(objs) - -# Cleaning -clean-dirs := static shared - -# install binary -install-y := $(shared-y) diff --git a/usr/kinit/run-init/run-init.c b/usr/kinit/run-init/run-init.c deleted file mode 100644 index 0f150dd..0000000 --- a/usr/kinit/run-init/run-init.c +++ /dev/null @@ -1,93 +0,0 @@ -/* ----------------------------------------------------------------------- * - * - * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom - * the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall - * be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * ----------------------------------------------------------------------- */ - -/* - * Usage: exec run-init [-c /dev/console] /real-root /sbin/init "$@" - * - * This program should be called as the last thing in a shell script - * acting as /init in an initramfs; it does the following: - * - * - Delete all files in the initramfs; - * - Remounts /real-root onto the root filesystem; - * - Chroots; - * - Opens /dev/console; - * - Spawns the specified init program (with arguments.) - */ - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include "run-init.h" - -static const char *program; - -static void __attribute__ ((noreturn)) usage(void) -{ - fprintf(stderr, - "Usage: exec %s [-c consoledev] /real-root /sbin/init [args]\n", - program); - exit(1); -} - -int main(int argc, char *argv[]) -{ - /* Command-line options and defaults */ - const char *console = "/dev/console"; - const char *realroot; - const char *init; - const char *error; - char **initargs; - - /* Variables... */ - int o; - - /* Parse the command line */ - program = argv[0]; - - while ((o = getopt(argc, argv, "c:")) != -1) { - if (o == 'c') { - console = optarg; - } else { - usage(); - } - } - - if (argc - optind < 2) - usage(); - - realroot = argv[optind]; - init = argv[optind + 1]; - initargs = argv + optind + 1; - - error = run_init(realroot, console, init, initargs); - - /* If run_init returns, something went wrong */ - fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno)); - return 1; -} diff --git a/usr/kinit/run-init/run-init.h b/usr/kinit/run-init/run-init.h deleted file mode 100644 index a95328e..0000000 --- a/usr/kinit/run-init/run-init.h +++ /dev/null @@ -1,34 +0,0 @@ -/* ----------------------------------------------------------------------- * - * - * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom - * the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall - * be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * ----------------------------------------------------------------------- */ - -#ifndef RUN_INIT_H -#define RUN_INIT_H - -const char *run_init(const char *realroot, const char *console, - const char *init, char **initargs); - -#endif diff --git a/usr/kinit/run-init/runinitlib.c b/usr/kinit/run-init/runinitlib.c deleted file mode 100644 index 8f1562f..0000000 --- a/usr/kinit/run-init/runinitlib.c +++ /dev/null @@ -1,209 +0,0 @@ -/* ----------------------------------------------------------------------- * - * - * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom - * the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall - * be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * ----------------------------------------------------------------------- */ - -/* - * run_init(consoledev, realroot, init, initargs) - * - * This function should be called as the last thing in kinit, - * from initramfs, it does the following: - * - * - Delete all files in the initramfs; - * - Remounts /real-root onto the root filesystem; - * - Chroots; - * - Opens /dev/console; - * - Spawns the specified init program (with arguments.) - * - * On failure, returns a human-readable error message. - */ - -#include <assert.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/vfs.h> -#include "run-init.h" - -/* Make it possible to compile on glibc by including constants that the - always-behind shipped glibc headers may not include. Classic example - on why the lack of ABI headers screw us up. */ -#ifndef TMPFS_MAGIC -# define TMPFS_MAGIC 0x01021994 -#endif -#ifndef RAMFS_MAGIC -# define RAMFS_MAGIC 0x858458f6 -#endif -#ifndef MS_MOVE -# define MS_MOVE 8192 -#endif - -static int nuke(const char *what); - -static int nuke_dirent(int len, const char *dir, const char *name, dev_t me) -{ - int bytes = len + strlen(name) + 2; - char path[bytes]; - int xlen; - struct stat st; - - xlen = snprintf(path, bytes, "%s/%s", dir, name); - assert(xlen < bytes); - - if (lstat(path, &st)) - return ENOENT; /* Return 0 since already gone? */ - - if (st.st_dev != me) - return 0; /* DO NOT recurse down mount points!!!!! */ - - return nuke(path); -} - -/* Wipe the contents of a directory, but not the directory itself */ -static int nuke_dir(const char *what) -{ - int len = strlen(what); - DIR *dir; - struct dirent *d; - int err = 0; - struct stat st; - - if (lstat(what, &st)) - return errno; - - if (!S_ISDIR(st.st_mode)) - return ENOTDIR; - - if (!(dir = opendir(what))) { - /* EACCES means we can't read it. Might be empty and removable; - if not, the rmdir() in nuke() will trigger an error. */ - return (errno == EACCES) ? 0 : errno; - } - - while ((d = readdir(dir))) { - /* Skip . and .. */ - if (d->d_name[0] == '.' && - (d->d_name[1] == '\0' || - (d->d_name[1] == '.' && d->d_name[2] == '\0'))) - continue; - - err = nuke_dirent(len, what, d->d_name, st.st_dev); - if (err) { - closedir(dir); - return err; - } - } - - closedir(dir); - - return 0; -} - -static int nuke(const char *what) -{ - int rv; - int err = 0; - - rv = unlink(what); - if (rv < 0) { - if (errno == EISDIR) { - /* It's a directory. */ - err = nuke_dir(what); - if (!err) - err = rmdir(what) ? errno : err; - } else { - err = errno; - } - } - - if (err) { - errno = err; - return err; - } else { - return 0; - } -} - -const char *run_init(const char *realroot, const char *console, - const char *init, char **initargs) -{ - struct stat rst, cst; - struct statfs sfs; - int confd; - - /* First, change to the new root directory */ - if (chdir(realroot)) - return "chdir to new root"; - - /* This is a potentially highly destructive program. Take some - extra precautions. */ - - /* Make sure the current directory is not on the same filesystem - as the root directory */ - if (stat("/", &rst) || stat(".", &cst)) - return "stat"; - - if (rst.st_dev == cst.st_dev) - return "current directory on the same filesystem as the root"; - - /* Make sure we're on a ramfs */ - if (statfs("/", &sfs)) - return "statfs /"; - if (sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC) - return "rootfs not a ramfs or tmpfs"; - - /* Okay, I think we should be safe... */ - - /* Delete rootfs contents */ - if (nuke_dir("/")) - return "nuking initramfs contents"; - - /* Overmount the root */ - if (mount(".", "/", NULL, MS_MOVE, NULL)) - return "overmounting root"; - - /* chroot, chdir */ - if (chroot(".") || chdir("/")) - return "chroot"; - - /* Open /dev/console */ - if ((confd = open(console, O_RDWR)) < 0) - return "opening console"; - dup2(confd, 0); - dup2(confd, 1); - dup2(confd, 2); - close(confd); - - /* Spawn init */ - execv(init, initargs); - return init; /* Failed to spawn init */ -} diff --git a/usr/kinit/switch_root/Kbuild b/usr/kinit/switch_root/Kbuild new file mode 100644 index 0000000..13f0a2a --- /dev/null +++ b/usr/kinit/switch_root/Kbuild @@ -0,0 +1,29 @@ +# +# Kbuild file for switch_root +# + +static-y := static/switch_root +shared-y := shared/switch_root + +# common .o files +objs := switch_root.o switch_rootlib.o + +# TODO - do we want a stripped version +# TODO - do we want the static.g + shared.g directories? + +# Create built-in.o with all object files (used by kinit) +lib-y := $(objs) + +# force switch_root to not have an executable stack (to keep READ_IMPLIES_EXEC +# personality(2) flag from getting set and passed to init). +EXTRA_KLIBCLDFLAGS += -z noexecstack + +# .o files used to built executables +static/switch_root-y := $(objs) +shared/switch_root-y := $(objs) + +# Cleaning +clean-dirs := static shared + +# install binary +install-y := $(shared-y) diff --git a/usr/kinit/switch_root/switch_root.c b/usr/kinit/switch_root/switch_root.c new file mode 100644 index 0000000..dbbbd9a --- /dev/null +++ b/usr/kinit/switch_root/switch_root.c @@ -0,0 +1,93 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * Usage: exec switch_root [-c /dev/console] /real-root /sbin/init "$@" + * + * This program should be called as the last thing in a shell script + * acting as /init in an initramfs; it does the following: + * + * - Delete all files in the initramfs; + * - Remounts /real-root onto the root filesystem; + * - Chroots; + * - Opens /dev/console; + * - Spawns the specified init program (with arguments.) + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include "switch_root.h" + +static const char *program; + +static void __attribute__ ((noreturn)) usage(void) +{ + fprintf(stderr, + "Usage: exec %s [-c consoledev] /real-root /sbin/init [args]\n", + program); + exit(1); +} + +int main(int argc, char *argv[]) +{ + /* Command-line options and defaults */ + const char *console = "/dev/console"; + const char *realroot; + const char *init; + const char *error; + char **initargs; + + /* Variables... */ + int o; + + /* Parse the command line */ + program = argv[0]; + + while ((o = getopt(argc, argv, "c:")) != -1) { + if (o == 'c') { + console = optarg; + } else { + usage(); + } + } + + if (argc - optind < 2) + usage(); + + realroot = argv[optind]; + init = argv[optind + 1]; + initargs = argv + optind + 1; + + error = switch_root(realroot, console, init, initargs); + + /* If switch_root returns, something went wrong */ + fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno)); + return 1; +} diff --git a/usr/kinit/switch_root/switch_root.h b/usr/kinit/switch_root/switch_root.h new file mode 100644 index 0000000..dfac8ac --- /dev/null +++ b/usr/kinit/switch_root/switch_root.h @@ -0,0 +1,34 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef SWITCH_ROOT_H +#define SWITCH_ROOT_H + +const char *switch_root(const char *realroot, const char *console, + const char *init, char **initargs); + +#endif /* SWITCH_ROOT_H */ diff --git a/usr/kinit/switch_root/switch_rootlib.c b/usr/kinit/switch_root/switch_rootlib.c new file mode 100644 index 0000000..23ce41f --- /dev/null +++ b/usr/kinit/switch_root/switch_rootlib.c @@ -0,0 +1,209 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * switch_root(consoledev, realroot, init, initargs) + * + * This function should be called as the last thing in kinit, + * from initramfs, it does the following: + * + * - Delete all files in the initramfs; + * - Remounts /real-root onto the root filesystem; + * - Chroots; + * - Opens /dev/console; + * - Spawns the specified init program (with arguments.) + * + * On failure, returns a human-readable error message. + */ + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/vfs.h> +#include "switch_root.h" + +/* Make it possible to compile on glibc by including constants that the + always-behind shipped glibc headers may not include. Classic example + on why the lack of ABI headers screw us up. */ +#ifndef TMPFS_MAGIC +# define TMPFS_MAGIC 0x01021994 +#endif +#ifndef RAMFS_MAGIC +# define RAMFS_MAGIC 0x858458f6 +#endif +#ifndef MS_MOVE +# define MS_MOVE 8192 +#endif + +static int nuke(const char *what); + +static int nuke_dirent(int len, const char *dir, const char *name, dev_t me) +{ + int bytes = len + strlen(name) + 2; + char path[bytes]; + int xlen; + struct stat st; + + xlen = snprintf(path, bytes, "%s/%s", dir, name); + assert(xlen < bytes); + + if (lstat(path, &st)) + return ENOENT; /* Return 0 since already gone? */ + + if (st.st_dev != me) + return 0; /* DO NOT recurse down mount points!!!!! */ + + return nuke(path); +} + +/* Wipe the contents of a directory, but not the directory itself */ +static int nuke_dir(const char *what) +{ + int len = strlen(what); + DIR *dir; + struct dirent *d; + int err = 0; + struct stat st; + + if (lstat(what, &st)) + return errno; + + if (!S_ISDIR(st.st_mode)) + return ENOTDIR; + + if (!(dir = opendir(what))) { + /* EACCES means we can't read it. Might be empty and removable; + if not, the rmdir() in nuke() will trigger an error. */ + return (errno == EACCES) ? 0 : errno; + } + + while ((d = readdir(dir))) { + /* Skip . and .. */ + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + + err = nuke_dirent(len, what, d->d_name, st.st_dev); + if (err) { + closedir(dir); + return err; + } + } + + closedir(dir); + + return 0; +} + +static int nuke(const char *what) +{ + int rv; + int err = 0; + + rv = unlink(what); + if (rv < 0) { + if (errno == EISDIR) { + /* It's a directory. */ + err = nuke_dir(what); + if (!err) + err = rmdir(what) ? errno : err; + } else { + err = errno; + } + } + + if (err) { + errno = err; + return err; + } else { + return 0; + } +} + +const char *switch_root(const char *realroot, const char *console, + const char *init, char **initargs) +{ + struct stat rst, cst; + struct statfs sfs; + int confd; + + /* First, change to the new root directory */ + if (chdir(realroot)) + return "chdir to new root"; + + /* This is a potentially highly destructive program. Take some + extra precautions. */ + + /* Make sure the current directory is not on the same filesystem + as the root directory */ + if (stat("/", &rst) || stat(".", &cst)) + return "stat"; + + if (rst.st_dev == cst.st_dev) + return "current directory on the same filesystem as the root"; + + /* Make sure we're on a ramfs */ + if (statfs("/", &sfs)) + return "statfs /"; + if (sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC) + return "rootfs not a ramfs or tmpfs"; + + /* Okay, I think we should be safe... */ + + /* Delete rootfs contents */ + if (nuke_dir("/")) + return "nuking initramfs contents"; + + /* Overmount the root */ + if (mount(".", "/", NULL, MS_MOVE, NULL)) + return "overmounting root"; + + /* chroot, chdir */ + if (chroot(".") || chdir("/")) + return "chroot"; + + /* Open /dev/console */ + if ((confd = open(console, O_RDWR)) < 0) + return "opening console"; + dup2(confd, 0); + dup2(confd, 1); + dup2(confd, 2); + close(confd); + + /* Spawn init */ + execv(init, initargs); + return init; /* Failed to spawn init */ +} -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 2/8] switch_root: checkpatch cleanup
Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/switch_root/switch_root.c | 5 ++--- usr/kinit/switch_root/switch_rootlib.c | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/usr/kinit/switch_root/switch_root.c b/usr/kinit/switch_root/switch_root.c index dbbbd9a..a422dc0 100644 --- a/usr/kinit/switch_root/switch_root.c +++ b/usr/kinit/switch_root/switch_root.c @@ -71,11 +71,10 @@ int main(int argc, char *argv[]) program = argv[0]; while ((o = getopt(argc, argv, "c:")) != -1) { - if (o == 'c') { + if (o == 'c') console = optarg; - } else { + else usage(); - } } if (argc - optind < 2) diff --git a/usr/kinit/switch_root/switch_rootlib.c b/usr/kinit/switch_root/switch_rootlib.c index 23ce41f..8c9599e 100644 --- a/usr/kinit/switch_root/switch_rootlib.c +++ b/usr/kinit/switch_root/switch_rootlib.c @@ -103,7 +103,8 @@ static int nuke_dir(const char *what) if (!S_ISDIR(st.st_mode)) return ENOTDIR; - if (!(dir = opendir(what))) { + dir = opendir(what); + if (!dir) { /* EACCES means we can't read it. Might be empty and removable; if not, the rmdir() in nuke() will trigger an error. */ return (errno == EACCES) ? 0 : errno; @@ -196,7 +197,8 @@ const char *switch_root(const char *realroot, const char *console, return "chroot"; /* Open /dev/console */ - if ((confd = open(console, O_RDWR)) < 0) + confd = open(console, O_RDWR); + if (confd < 0) return "opening console"; dup2(confd, 0); dup2(confd, 1); -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 3/8] switch_root: let the utility verify passed args
We don't want to execute null strings. Seen in util-linux switch_root(). Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/switch_root/switch_root.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/usr/kinit/switch_root/switch_root.c b/usr/kinit/switch_root/switch_root.c index a422dc0..f717637 100644 --- a/usr/kinit/switch_root/switch_root.c +++ b/usr/kinit/switch_root/switch_root.c @@ -84,6 +84,9 @@ int main(int argc, char *argv[]) init = argv[optind + 1]; initargs = argv + optind + 1; + if (!*realroot || !*init) + usage(); + error = switch_root(realroot, console, init, initargs); /* If switch_root returns, something went wrong */ -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 4/8] switch_root: add + enhance -h help invocation
Enhance help message by shortening it to explicit variables. The help invocation goes to stdout, the rest to stderr. Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/switch_root/switch_root.c | 18 ++++++++++-------- 1 files changed, 10 insertions(+), 8 deletions(-) diff --git a/usr/kinit/switch_root/switch_root.c b/usr/kinit/switch_root/switch_root.c index f717637..68ef62d 100644 --- a/usr/kinit/switch_root/switch_root.c +++ b/usr/kinit/switch_root/switch_root.c @@ -47,12 +47,12 @@ static const char *program; -static void __attribute__ ((noreturn)) usage(void) +static void __attribute__ ((noreturn)) usage(FILE *output) { - fprintf(stderr, - "Usage: exec %s [-c consoledev] /real-root /sbin/init [args]\n", + fprintf(output, + "Usage: exec %s [-c consoledev] <newroot> <init> [args]\n", program); - exit(1); + exit(output == stderr ? EXIT_FAILURE : EXIT_SUCCESS); } int main(int argc, char *argv[]) @@ -70,22 +70,24 @@ int main(int argc, char *argv[]) /* Parse the command line */ program = argv[0]; - while ((o = getopt(argc, argv, "c:")) != -1) { + while ((o = getopt(argc, argv, "c:h")) != -1) { if (o == 'c') console = optarg; + else if (o == 'h') + usage(stdout); else - usage(); + usage(stderr); } if (argc - optind < 2) - usage(); + usage(stderr); realroot = argv[optind]; init = argv[optind + 1]; initargs = argv + optind + 1; if (!*realroot || !*init) - usage(); + usage(stderr); error = switch_root(realroot, console, init, initargs); -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 5/8] switch_root: check that init is executable.
Minimal check for the passed init. This can be further enhanced to cleanup kinit and switch_root(8) callers. Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/switch_root/switch_rootlib.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/usr/kinit/switch_root/switch_rootlib.c b/usr/kinit/switch_root/switch_rootlib.c index 8c9599e..14c3e34 100644 --- a/usr/kinit/switch_root/switch_rootlib.c +++ b/usr/kinit/switch_root/switch_rootlib.c @@ -205,6 +205,9 @@ const char *switch_root(const char *realroot, const char *console, dup2(confd, 2); close(confd); + if (access(init, X_OK)) + fprintf(stderr, "cannot acces %s\n", init); + /* Spawn init */ execv(init, initargs); return init; /* Failed to spawn init */ -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 6/8] switch_root: Fix single file mounts
From: Michal Suchanek <michal.suchanek at ruk.cuni.cz> The root of the failure is that nuke cannot cope with file mounts (single files mounted, not directories). These are the result of using fuse to get to the root filesystem (httpfs, curlftpfs). This fixes http://bugs.debian.org/476268 Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/switch_root/switch_rootlib.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/usr/kinit/switch_root/switch_rootlib.c b/usr/kinit/switch_root/switch_rootlib.c index 14c3e34..372d2b3 100644 --- a/usr/kinit/switch_root/switch_rootlib.c +++ b/usr/kinit/switch_root/switch_rootlib.c @@ -80,7 +80,7 @@ static int nuke_dirent(int len, const char *dir, const char *name, dev_t me) assert(xlen < bytes); if (lstat(path, &st)) - return ENOENT; /* Return 0 since already gone? */ + return 0; /* Return 0 since already gone. */ if (st.st_dev != me) return 0; /* DO NOT recurse down mount points!!!!! */ -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 7/8] switch_root: Don't fail due to stray mount
Make switch_root more permissive and just print it's errors, so that it doesn't fail on a deep mount somewhere. This is analogue to switch_root(8) handling in util-linux. Failure in nuke_dir() is no longer fatal, but will print warning and move on. Fixes: https://bugs.launchpad.net/ubuntu/+source/klibc/+bug/31762 https://bugs.launchpad.net/ubuntu/+source/klibc/+bug/617426 Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/switch_root/switch_rootlib.c | 18 ++++++++---------- 1 files changed, 8 insertions(+), 10 deletions(-) diff --git a/usr/kinit/switch_root/switch_rootlib.c b/usr/kinit/switch_root/switch_rootlib.c index 372d2b3..b34f2b4 100644 --- a/usr/kinit/switch_root/switch_rootlib.c +++ b/usr/kinit/switch_root/switch_rootlib.c @@ -97,17 +97,18 @@ static int nuke_dir(const char *what) int err = 0; struct stat st; - if (lstat(what, &st)) - return errno; + if (lstat(what, &st)) { + fprintf(stderr, "failed to stat directory\n"); + return 0; + } if (!S_ISDIR(st.st_mode)) return ENOTDIR; dir = opendir(what); if (!dir) { - /* EACCES means we can't read it. Might be empty and removable; - if not, the rmdir() in nuke() will trigger an error. */ - return (errno == EACCES) ? 0 : errno; + fprintf(stderr, "failed to open directory\n"); + return 0; } while ((d = readdir(dir))) { @@ -118,14 +119,11 @@ static int nuke_dir(const char *what) continue; err = nuke_dirent(len, what, d->d_name, st.st_dev); - if (err) { - closedir(dir); - return err; - } + if (err) + fprintf(stderr, "failed to unlink %s\n", d->d_name); } closedir(dir); - return 0; } -- 1.7.5.4
maximilian attems
2011-Jul-13 13:48 UTC
[klibc] [PATCH 8/8] switch_root: Move mount /dev, /proc, /sys to new root
Further enhance switch_root() to take care of this. As a side effect allows to get rid of 2 globals in kinit(8). Signed-off-by: maximilian attems <max at stro.at> --- usr/kinit/kinit.c | 30 ++++-------------------------- usr/kinit/switch_root/switch_root.c | 1 + usr/kinit/switch_root/switch_rootlib.c | 20 ++++++++++++++++++-- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c index 9495d89..96d49c7 100644 --- a/usr/kinit/kinit.c +++ b/usr/kinit/kinit.c @@ -16,8 +16,6 @@ #include "resume.h" const char *progname = "kinit"; -int mnt_procfs; -int mnt_sysfs; #ifdef DEBUG void dump_args(int argc, char *argv[]) @@ -225,17 +223,13 @@ int main(int argc, char *argv[]) } } - mnt_procfs = mount_sys_fs("/proc/cmdline", "/proc", "proc") >= 0; - if (!mnt_procfs) { - ret = 1; + ret = mount_sys_fs("/proc/cmdline", "/proc", "proc") >= 0; + if (!ret) goto bail; - } - mnt_sysfs = mount_sys_fs("/sys/bus", "/sys", "sysfs") >= 0; - if (!mnt_sysfs) { - ret = 1; + ret = mount_sys_fs("/sys/bus", "/sys", "sysfs") >= 0; + if (!ret) goto bail; - } /* Construct the effective kernel command line. The effective kernel command line consists of /arch.cmd, if @@ -288,16 +282,6 @@ int main(int argc, char *argv[]) check_path("/root"); do_mounts(cmdc, cmdv); - if (mnt_procfs) { - umount2("/proc", 0); - mnt_procfs = 0; - } - - if (mnt_sysfs) { - umount2("/sys", 0); - mnt_sysfs = 0; - } - init_path = find_init("/root", get_arg(cmdc, cmdv, "init=")); if (!init_path) { fprintf(stderr, "%s: init not found!\n", progname); @@ -315,12 +299,6 @@ int main(int argc, char *argv[]) goto bail; bail: - if (mnt_procfs) - umount2("/proc", 0); - - if (mnt_sysfs) - umount2("/sys", 0); - /* * If we get here, something bad probably happened, and the kernel * will most likely panic. Drain console output so the user can diff --git a/usr/kinit/switch_root/switch_root.c b/usr/kinit/switch_root/switch_root.c index 68ef62d..efb9691 100644 --- a/usr/kinit/switch_root/switch_root.c +++ b/usr/kinit/switch_root/switch_root.c @@ -32,6 +32,7 @@ * acting as /init in an initramfs; it does the following: * * - Delete all files in the initramfs; + * - Move mounts /dev, /proc, /sys to /real-root; * - Remounts /real-root onto the root filesystem; * - Chroots; * - Opens /dev/console; diff --git a/usr/kinit/switch_root/switch_rootlib.c b/usr/kinit/switch_root/switch_rootlib.c index b34f2b4..7fdbdd2 100644 --- a/usr/kinit/switch_root/switch_rootlib.c +++ b/usr/kinit/switch_root/switch_rootlib.c @@ -155,11 +155,27 @@ static int nuke(const char *what) const char *switch_root(const char *realroot, const char *console, const char *init, char **initargs) { + const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; struct stat rst, cst; struct statfs sfs; - int confd; + int confd, i; - /* First, change to the new root directory */ + /* First, mount move sysdir from intramfs to new root */ + for (i = 0; umounts[i] != NULL; i++) { + char newmount[PATH_MAX]; + + snprintf(newmount, sizeof(newmount), "%s%s", realroot, + umounts[i]); + + if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { + fprintf(stderr, "failed to mount moving %s to %s", + umounts[i], newmount); + fprintf(stderr, "forcing unmount of %s", umounts[i]); + umount2(umounts[i], MNT_FORCE); + } + } + + /* Next, change to the new root directory */ if (chdir(realroot)) return "chdir to new root"; -- 1.7.5.4
On Wed, Jul 13, 2011 at 03:48:25PM +0200, maximilian attems wrote:> On a train ride to Bruxelles, brought out my axe and directly attacked > run-init(8). run-init(8) is dead, long live switch_root(8). > > The next run on switch_root(8) involves fdopendir, > so another push for the upcoming stdio 1.6 branch. > > The following is boot tested with initramfs-tools, > kinit(8) tests would very much be appreciated!?for easier testing the branch should appear soonest in master of http://git.kernel.org/?p=libs/klibc/klibc-queue.git;a=summary thank you. -- maks
Reasonably Related Threads
- [PATCH klibc] run-init: Add dry-run mode
- [PATCH klibc 0/4] Fixes from Debian and Ubuntu
- [PATCH] Allow the initramfs to be persisted across root changes
- [PATCH v1 0/2] Support dropping of capabilities from early userspace.
- [PATCH] run-init: add drop_capabilities support