aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/tools/namespace-sandbox.c
diff options
context:
space:
mode:
authorGravatar Brian Silverman <bsilver16384@gmail.com>2015-10-13 12:02:55 +0000
committerGravatar Florian Weikert <fwe@google.com>2015-10-13 12:16:10 +0000
commit40ea964ca987667485b69011d414dad302025c60 (patch)
treee78369de977e0d1f70fd4b5d9387d9aee7e818ae /src/main/tools/namespace-sandbox.c
parentb10157f88e8c2903d73712f0d5887b8b204d2bd9 (diff)
Make the sandbox handle an arbitrary number of arguments
I was hitting argument length restrictions when trying to run rules which take 2 GCC installs and a Clang install as inputs. -- Change-Id: Iee27bb6fb50fe94bff4b2500bbcfa9381b05d63d Reviewed-on: https://bazel-review.googlesource.com/#/c/2090 MOS_MIGRATED_REVID=105300670
Diffstat (limited to 'src/main/tools/namespace-sandbox.c')
-rw-r--r--src/main/tools/namespace-sandbox.c135
1 files changed, 112 insertions, 23 deletions
diff --git a/src/main/tools/namespace-sandbox.c b/src/main/tools/namespace-sandbox.c
index ef13213674..e362f370c9 100644
--- a/src/main/tools/namespace-sandbox.c
+++ b/src/main/tools/namespace-sandbox.c
@@ -17,6 +17,7 @@
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
+#include <limits.h>
#include <pwd.h>
#include <sched.h>
#include <signal.h>
@@ -63,6 +64,7 @@ struct Options {
const char *working_dir; // Working directory (-W)
char **mount_sources; // Map of directories to mount, from (-M)
char **mount_targets; // sources -> targets (-m)
+ size_t mount_map_sizes; // How many elements in mount_{sources,targets}
int num_mounts; // How many mounts were specified
char **create_dirs; // empty dirs to create (-d)
int num_create_dirs; // How many empty dirs to create were specified
@@ -142,10 +144,97 @@ static void Usage(int argc, char *const *argv, const char *fmt, ...) {
" -D if set, debug info will be printed\n"
" -l <file> redirect stdout to a file\n"
" -L <file> redirect stderr to a file\n"
- );
+ " @FILE read newline-separated arguments from FILE\n");
exit(EXIT_FAILURE);
}
+// Deals with an unfinished (source but no target) mapping in opt.
+// Also adds a new unfinished mapping if source is not NULL.
+static void AddMountSource(char *source, struct Options *opt) {
+ // The last -M flag wasn't followed by an -m flag, so assume that the source
+ // should be mounted in the sandbox in the same path as outside.
+ if (opt->mount_sources[opt->num_mounts] != NULL) {
+ opt->mount_targets[opt->num_mounts] = opt->mount_sources[opt->num_mounts];
+ opt->num_mounts++;
+ }
+ if (source != NULL) {
+ if (opt->num_mounts >= opt->mount_map_sizes - 1) {
+ opt->mount_sources = realloc(opt->mount_sources,
+ opt->mount_map_sizes * sizeof(char *) * 2);
+ if (opt->mount_sources == NULL) {
+ DIE("realloc failed\n");
+ }
+ memset(opt->mount_sources + opt->mount_map_sizes, 0,
+ opt->mount_map_sizes * sizeof(char *));
+ opt->mount_targets = realloc(opt->mount_targets,
+ opt->mount_map_sizes * sizeof(char *) * 2);
+ if (opt->mount_targets == NULL) {
+ DIE("realloc failed\n");
+ }
+ memset(opt->mount_targets + opt->mount_map_sizes, 0,
+ opt->mount_map_sizes * sizeof(char *));
+ opt->mount_map_sizes *= 2;
+ }
+ opt->mount_sources[opt->num_mounts] = source;
+ }
+}
+
+static void ParseCommandLine(int argc, char *const *argv, struct Options *opt);
+
+// Parses command line flags from a file named filename.
+// Expects optind to be initialized to 0 before being called.
+static void ParseOptionsFile(const char *filename, struct Options *opt) {
+ FILE *const options_file = fopen(filename, "rb");
+ if (options_file == NULL) {
+ DIE("opening argument file %s failed\n", filename);
+ }
+ size_t sub_argv_size = 20;
+ char **sub_argv = malloc(sizeof(char *) * sub_argv_size);
+ sub_argv[0] = "";
+ int sub_argc = 1;
+
+ bool done = false;
+ while (!done) {
+ // This buffer determines the maximum size of arguments we can handle out of
+ // the file. We DIE down below if it's ever too short.
+ // 4096 is a common value for PATH_MAX. However, many filesystems support
+ // arbitrarily long pathnames, so this might not be long enough to handle an
+ // arbitrary filename no matter what. Twice the usual PATH_MAX seems
+ // reasonable for now.
+ char argument[8192];
+ if (fgets(argument, sizeof(argument), options_file) == NULL) {
+ if (feof(options_file)) {
+ done = true;
+ continue;
+ } else {
+ DIE("reading from argument file %s failed\n", filename);
+ }
+ }
+ const size_t length = strlen(argument);
+ if (length == 0) continue;
+ if (length == sizeof(argument)) {
+ DIE("argument from file %s is too long (> %zu)\n", filename,
+ sizeof(argument));
+ }
+ if (argument[length - 1] == '\n') {
+ argument[length - 1] = '\0';
+ } else {
+ done = true;
+ }
+ if (sub_argv_size == sub_argc + 1) {
+ sub_argv_size *= 2;
+ sub_argv = realloc(sub_argv, sizeof(char *) * sub_argv_size);
+ }
+ sub_argv[sub_argc++] = strdup(argument);
+ }
+ if (fclose(options_file) != 0) {
+ DIE("closing options file %s failed\n", filename);
+ }
+ sub_argv[sub_argc] = NULL;
+
+ ParseCommandLine(sub_argc, sub_argv, opt);
+}
+
// Parse the command line flags and return the result in an Options structure
// passed as argument.
static void ParseCommandLine(int argc, char *const *argv, struct Options *opt) {
@@ -209,14 +298,7 @@ static void ParseCommandLine(int argc, char *const *argv, struct Options *opt) {
Usage(argc, argv,
"The -M option must be used with absolute paths only.");
}
- // The last -M flag wasn't followed by an -m flag, so assume that the
- // source should be mounted in the sandbox in the same path as outside.
- if (opt->mount_sources[opt->num_mounts] != NULL) {
- opt->mount_targets[opt->num_mounts] =
- opt->mount_sources[opt->num_mounts];
- opt->num_mounts++;
- }
- opt->mount_sources[opt->num_mounts] = optarg;
+ AddMountSource(optarg, opt);
break;
case 'm':
if (optarg[0] != '/') {
@@ -262,21 +344,22 @@ static void ParseCommandLine(int argc, char *const *argv, struct Options *opt) {
}
}
- if (opt->sandbox_root == NULL) {
- Usage(argc, argv, "Sandbox root (-S) must be specified");
- }
+ AddMountSource(NULL, opt);
- // The last -M flag wasn't followed by an -m flag, assume that the source
- // should be mounted in the sandbox in the same path as outside.
- if (opt->mount_sources[opt->num_mounts] != NULL &&
- opt->mount_targets[opt->num_mounts] == NULL) {
- opt->mount_targets[opt->num_mounts] = opt->mount_sources[opt->num_mounts];
- opt->num_mounts++;
+ while (optind < argc && argv[optind][0] == '@') {
+ const char *filename = argv[optind] + 1;
+ const int old_optind = optind;
+ optind = 0;
+ ParseOptionsFile(filename, opt);
+ optind = old_optind + 1;
}
- opt->args = argv + optind;
- if (argc <= optind) {
- Usage(argc, argv, "No command specified.");
+ if (argc > optind) {
+ if (opt->args == NULL) {
+ opt->args = argv + optind;
+ } else {
+ Usage(argc, argv, "Merging commands not supported.");
+ }
}
}
@@ -290,8 +373,7 @@ static void CreateNamespaces(int create_netns) {
const int max_tries = 100;
while (tries++ < max_tries) {
if (unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
- (create_netns ? CLONE_NEWNET : 0)) ==
- 0) {
+ (create_netns ? CLONE_NEWNET : 0)) == 0) {
PRINT_DEBUG("unshare succeeded after %d tries\n", tries);
return;
} else {
@@ -584,11 +666,18 @@ int main(int argc, char *const argv[]) {
memset(&opt, 0, sizeof(opt));
opt.mount_sources = calloc(argc, sizeof(char *));
opt.mount_targets = calloc(argc, sizeof(char *));
+ opt.mount_map_sizes = argc;
// Reserve two extra slots for homedir_from_env and homedir.
opt.create_dirs = calloc(argc + 2, sizeof(char *));
ParseCommandLine(argc, argv, &opt);
+ if (opt.args == NULL) {
+ Usage(argc, argv, "No command specified.");
+ }
+ if (opt.sandbox_root == NULL) {
+ Usage(argc, argv, "Sandbox root (-S) must be specified");
+ }
global_kill_delay = opt.kill_delay_secs;
int uid = SwitchToEuid();