diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2015-07-04 17:13:50 +0300 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2015-07-04 17:13:50 +0300 |
commit | 71cd8e3a743046573744123777061b64881bf372 (patch) | |
tree | 82522befe647f4fff186a5630cad0cad33f8ef53 /src/chroot.c | |
parent | c18578632fd3c9e513e613a86ba2b7c4ebee6c45 (diff) | |
download | coreutils-upstream.tar.gz |
Imported Upstream version 8.24upstream/8.24upstream
Diffstat (limited to 'src/chroot.c')
-rw-r--r-- | src/chroot.c | 78 |
1 files changed, 46 insertions, 32 deletions
diff --git a/src/chroot.c b/src/chroot.c index fff0b533..42b45017 100644 --- a/src/chroot.c +++ b/src/chroot.c @@ -1,5 +1,5 @@ /* chroot -- run command or shell with special root directory - Copyright (C) 1995-2014 Free Software Foundation, Inc. + Copyright (C) 1995-2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -49,13 +49,15 @@ static inline bool gid_unset (gid_t gid) { return gid == (gid_t) -1; } enum { GROUPS = UCHAR_MAX + 1, - USERSPEC + USERSPEC, + SKIP_CHDIR }; static struct option const long_opts[] = { {"groups", required_argument, NULL, GROUPS}, {"userspec", required_argument, NULL, USERSPEC}, + {"skip-chdir", no_argument, NULL, SKIP_CHDIR}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -160,20 +162,17 @@ parse_additional_groups (char const *groups, GETGROUPS_T **pgids, return ret; } +/* Return whether the passed path is equivalent to "/". + Note we don't compare against get_root_dev_ino() as "/" + could be bind mounted to a separate location. */ + static bool is_root (const char* dir) { - struct dev_ino root_ino; - if (! get_root_dev_ino (&root_ino)) - error (EXIT_CANCELED, errno, _("failed to get attributes of %s"), - quote ("/")); - - struct stat arg_st; - if (stat (dir, &arg_st) == -1) - error (EXIT_CANCELED, errno, _("failed to get attributes of %s"), - quote (dir)); - - return SAME_INODE (root_ino, arg_st); + char *resolved = canonicalize_file_name (dir); + bool is_res_root = resolved && STREQ ("/", resolved); + free (resolved); + return is_res_root; } void @@ -194,9 +193,14 @@ Run COMMAND with root directory set to NEWROOT.\n\ "), stdout); fputs (_("\ - --userspec=USER:GROUP specify user and group (ID or name) to use\n\ --groups=G_LIST specify supplementary groups as g1,g2,..,gN\n\ "), stdout); + fputs (_("\ + --userspec=USER:GROUP specify user and group (ID or name) to use\n\ +"), stdout); + printf (_("\ + --skip-chdir do not change working directory to %s\n\ +"), quote ("/")); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -204,7 +208,7 @@ Run COMMAND with root directory set to NEWROOT.\n\ \n\ If no command is given, run '${SHELL} -i' (default: '/bin/sh -i').\n\ "), stdout); - emit_ancillary_info (); + emit_ancillary_info (PROGRAM_NAME); } exit (status); } @@ -218,6 +222,7 @@ main (int argc, char **argv) char *userspec = NULL; char const *username = NULL; char const *groups = NULL; + bool skip_chdir = false; /* Parsed user and group IDs. */ uid_t uid = -1; @@ -254,6 +259,10 @@ main (int argc, char **argv) groups = optarg; break; + case SKIP_CHDIR: + skip_chdir = true; + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -269,9 +278,17 @@ main (int argc, char **argv) usage (EXIT_CANCELED); } - /* Only do chroot specific actions if actually changing root. - The main difference here is that we don't change working dir. */ - if (! is_root (argv[optind])) + char const *newroot = argv[optind]; + bool is_oldroot = is_root (newroot); + + if (! is_oldroot && skip_chdir) + { + error (0, 0, _("option --skip-chdir only permitted if NEWROOT is old %s"), + quote ("/")); + usage (EXIT_CANCELED); + } + + if (! is_oldroot) { /* We have to look up users and groups twice. - First, outside the chroot to load potentially necessary passwd/group @@ -306,14 +323,14 @@ main (int argc, char **argv) n_gids = ngroups; } #endif + } - if (chroot (argv[optind]) != 0) - error (EXIT_CANCELED, errno, _("cannot change root directory to %s"), - argv[optind]); + if (chroot (newroot) != 0) + error (EXIT_CANCELED, errno, _("cannot change root directory to %s"), + quote (newroot)); - if (chdir ("/")) - error (EXIT_CANCELED, errno, _("cannot chdir to root directory")); - } + if (! skip_chdir && chdir ("/")) + error (EXIT_CANCELED, errno, _("cannot chdir to root directory")); if (argc == optind + 1) { @@ -366,7 +383,7 @@ main (int argc, char **argv) if (parse_additional_groups (groups, &in_gids, &n_gids, !n_gids) != 0) { if (! n_gids) - exit (EXIT_CANCELED); + return EXIT_CANCELED; /* else look-up outside the chroot worked, then go with those. */ } else @@ -392,8 +409,7 @@ main (int argc, char **argv) #endif if ((uid_set (uid) || groups) && setgroups (n_gids, gids) != 0) - error (EXIT_CANCELED, errno, _("failed to %s supplemental groups"), - gids ? "set" : "clear"); + error (EXIT_CANCELED, errno, _("failed to set supplemental groups")); free (in_gids); free (out_gids); @@ -407,9 +423,7 @@ main (int argc, char **argv) /* Execute the given command. */ execvp (argv[0], argv); - { - int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE); - error (0, errno, _("failed to run command %s"), quote (argv[0])); - exit (exit_status); - } + int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE; + error (0, errno, _("failed to run command %s"), quote (argv[0])); + return exit_status; } |