summaryrefslogtreecommitdiff
path: root/src/chroot.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2015-07-04 17:13:50 +0300
committerIgor Pashev <pashev.igor@gmail.com>2015-07-04 17:13:50 +0300
commit71cd8e3a743046573744123777061b64881bf372 (patch)
tree82522befe647f4fff186a5630cad0cad33f8ef53 /src/chroot.c
parentc18578632fd3c9e513e613a86ba2b7c4ebee6c45 (diff)
downloadcoreutils-upstream.tar.gz
Imported Upstream version 8.24upstream/8.24upstream
Diffstat (limited to 'src/chroot.c')
-rw-r--r--src/chroot.c78
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;
}