]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/switch_root.c
Make the ways of using output stream consistent in usage()
[thirdparty/util-linux.git] / sys-utils / switch_root.c
index 5c4095966585692ec245dbe7c48550ea106fe56d..ab8c2a141c4795ab984255b3abbebd970c888d66 100644 (file)
@@ -63,7 +63,6 @@ static int recursiveRemove(int fd)
 
        /* fdopendir() precludes us from continuing to use the input fd */
        dfd = dirfd(dir);
-
        if (fstat(dfd, &rb)) {
                warn(_("stat failed"));
                goto done;
@@ -104,10 +103,8 @@ static int recursiveRemove(int fd)
                                int cfd;
 
                                cfd = openat(dfd, d->d_name, O_RDONLY);
-                               if (cfd >= 0) {
-                                       recursiveRemove(cfd);
-                                       close(cfd);
-                               }
+                               if (cfd >= 0)
+                                       recursiveRemove(cfd);   /* it closes cfd too */
                                isdir = 1;
                        }
                }
@@ -117,10 +114,11 @@ static int recursiveRemove(int fd)
        }
 
        rc = 0; /* success */
-
 done:
        if (dir)
                closedir(dir);
+       else
+               close(fd);
        return rc;
 }
 
@@ -129,9 +127,13 @@ static int switchroot(const char *newroot)
        /*  Don't try to unmount the old "/", there's no way to do it. */
        const char *umounts[] = { "/dev", "/proc", "/sys", "/run", NULL };
        int i;
-       int cfd;
-       pid_t pid;
-       struct stat newroot_stat, sb;
+       int cfd = -1;
+       struct stat newroot_stat, oldroot_stat, sb;
+
+       if (stat("/", &oldroot_stat) != 0) {
+               warn(_("stat of %s failed"), "/");
+               return -1;
+       }
 
        if (stat(newroot, &newroot_stat) != 0) {
                warn(_("stat of %s failed"), newroot);
@@ -143,6 +145,11 @@ static int switchroot(const char *newroot)
 
                snprintf(newmount, sizeof(newmount), "%s%s", newroot, umounts[i]);
 
+               if ((stat(umounts[i], &sb) == 0) && sb.st_dev == oldroot_stat.st_dev) {
+                       /* mount point to move seems to be a normal directory or stat failed */
+                       continue;
+               }
+
                if ((stat(newmount, &sb) != 0) || (sb.st_dev != newroot_stat.st_dev)) {
                        /* mount point seems to be mounted already or stat failed */
                        umount2(umounts[i], MNT_DETACH);
@@ -165,37 +172,51 @@ static int switchroot(const char *newroot)
        cfd = open("/", O_RDONLY);
        if (cfd < 0) {
                warn(_("cannot open %s"), "/");
-               return -1;
+               goto fail;
        }
 
        if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) {
-               close(cfd);
                warn(_("failed to mount moving %s to /"), newroot);
-               return -1;
+               goto fail;
        }
 
        if (chroot(".")) {
-               close(cfd);
                warn(_("failed to change root"));
-               return -1;
+               goto fail;
        }
 
-       pid = fork();
-       if (pid <= 0) {
+       if (chdir("/")) {
+               warn(_("cannot change directory to %s"), "/");
+               goto fail;
+       }
+
+       switch (fork()) {
+       case 0: /* child */
+       {
                struct statfs stfs;
 
                if (fstatfs(cfd, &stfs) == 0 &&
                    (F_TYPE_EQUAL(stfs.f_type, STATFS_RAMFS_MAGIC) ||
                     F_TYPE_EQUAL(stfs.f_type, STATFS_TMPFS_MAGIC)))
                        recursiveRemove(cfd);
-               else
+               else {
                        warn(_("old root filesystem is not an initramfs"));
-               if (pid == 0)
-                       exit(EXIT_SUCCESS);
+                       close(cfd);
+               }
+               exit(EXIT_SUCCESS);
        }
+       case -1: /* error */
+               break;
 
-       close(cfd);
-       return 0;
+       default: /* parent */
+               close(cfd);
+               return 0;
+       }
+
+fail:
+       if (cfd >= 0)
+               close(cfd);
+       return -1;
 }
 
 static void __attribute__((__noreturn__)) usage(void)
@@ -209,7 +230,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_("Switch to another filesystem as the root of the mount tree.\n"), output);
 
        fputs(USAGE_OPTIONS, output);
-       print_usage_help_options(16);
+       fprintf(output, USAGE_HELP_OPTIONS(16));
        fprintf(output, USAGE_MAN_TAIL("switch_root(8)"));
 
        exit(EXIT_SUCCESS);
@@ -225,13 +246,12 @@ int main(int argc, char *argv[])
                {NULL, 0, NULL, 0}
        };
 
-       atexit(close_stdout);
+       close_stdout_atexit();
 
-       while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+       while ((c = getopt_long(argc, argv, "+Vh", longopts, NULL)) != -1)
                switch (c) {
                case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       return EXIT_SUCCESS;
+                       print_version(EXIT_SUCCESS);
                case 'h':
                        usage();
                default:
@@ -258,6 +278,5 @@ int main(int argc, char *argv[])
                warn(_("cannot access %s"), init);
 
        execv(init, initargs);
-       err(EXIT_FAILURE, _("failed to execute %s"), init);
+       errexec(init);
 }
-