make sure that /linuxrc has std{in,out,err}
[linux-3.10.git] / init / do_mounts_initrd.c
index a6b4c0c..f9acf71 100644 (file)
@@ -1,19 +1,28 @@
+/*
+ * Many of the syscalls used in this file expect some of the arguments
+ * to be __user pointers not __kernel pointers.  To limit the sparse
+ * noise, turn off sparse checking for this file.
+ */
+#ifdef __CHECKER__
+#undef __CHECKER__
+#warning "Sparse checking disabled for this file"
+#endif
+
 #include <linux/unistd.h>
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/minix_fs.h>
-#include <linux/ext2_fs.h>
 #include <linux/romfs_fs.h>
 #include <linux/initrd.h>
 #include <linux/sched.h>
 #include <linux/freezer.h>
+#include <linux/kmod.h>
 
 #include "do_mounts.h"
 
 unsigned long initrd_start, initrd_end;
 int initrd_below_start_ok;
 unsigned int real_root_dev;    /* do_proc_dointvec cannot handle kdev_t */
-static int __initdata old_fd, root_fd;
 static int __initdata mount_initrd = 1;
 
 static int __init no_initrd(char *str)
@@ -24,56 +33,56 @@ static int __init no_initrd(char *str)
 
 __setup("noinitrd", no_initrd);
 
-static int __init do_linuxrc(void * shell)
+static int init_linuxrc(struct subprocess_info *info, struct cred *new)
 {
-       static char *argv[] = { "linuxrc", NULL, };
-       extern char * envp_init[];
-
-       sys_close(old_fd);sys_close(root_fd);
-       sys_close(0);sys_close(1);sys_close(2);
+       sys_unshare(CLONE_FS | CLONE_FILES);
+       /* stdin/stdout/stderr for /linuxrc */
+       sys_open("/dev/console", O_RDWR, 0);
+       sys_dup(0);
+       sys_dup(0);
+       /* move initrd over / and chdir/chroot in initrd root */
+       sys_chdir("/root");
+       sys_mount(".", "/", NULL, MS_MOVE, NULL);
+       sys_chroot(".");
        sys_setsid();
-       (void) sys_open("/dev/console",O_RDWR,0);
-       (void) sys_dup(0);
-       (void) sys_dup(0);
-       return kernel_execve(shell, argv, envp_init);
+       return 0;
 }
 
 static void __init handle_initrd(void)
 {
+       static char *argv[] = { "linuxrc", NULL, };
+       extern char *envp_init[];
        int error;
-       int pid;
 
        real_root_dev = new_encode_dev(ROOT_DEV);
        create_dev("/dev/root.old", Root_RAM0);
        /* mount initrd on rootfs' /root */
        mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
        sys_mkdir("/old", 0700);
-       root_fd = sys_open("/", 0, 0);
-       old_fd = sys_open("/old", 0, 0);
-       /* move initrd over / and chdir/chroot in initrd root */
-       sys_chdir("/root");
-       sys_mount(".", "/", NULL, MS_MOVE, NULL);
-       sys_chroot(".");
+       sys_chdir("/old");
+
+       /*
+        * In case that a resume from disk is carried out by linuxrc or one of
+        * its children, we need to tell the freezer not to wait for us.
+        */
+       current->flags |= PF_FREEZER_SKIP;
 
-       pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
-       if (pid > 0)
-               while (pid != sys_wait4(-1, NULL, 0, NULL))
-                       yield();
+       call_usermodehelper_fns("/linuxrc", argv, envp_init, UMH_WAIT_PROC,
+                       init_linuxrc, NULL, NULL);
+
+       current->flags &= ~PF_FREEZER_SKIP;
 
        /* move initrd to rootfs' /old */
-       sys_fchdir(old_fd);
-       sys_mount("/", ".", NULL, MS_MOVE, NULL);
+       sys_mount("..", ".", NULL, MS_MOVE, NULL);
        /* switch root and cwd back to / of rootfs */
-       sys_fchdir(root_fd);
-       sys_chroot(".");
-       sys_close(old_fd);
-       sys_close(root_fd);
+       sys_chroot("..");
 
        if (new_decode_dev(real_root_dev) == Root_RAM0) {
                sys_chdir("/old");
                return;
        }
 
+       sys_chdir("/");
        ROOT_DEV = new_decode_dev(real_root_dev);
        mount_root();