Index: test/cmd-subst-pwd.sh =================================================================== --- test/cmd-subst-pwd.sh (revision 0) +++ test/cmd-subst-pwd.sh (revision 0) @@ -0,0 +1,3 @@ +#!/bin/sh + +echo substituted Property changes on: test/cmd-subst-pwd.sh ___________________________________________________________________ Added: svn:executable + * Index: test/cmd-subst.t =================================================================== --- test/cmd-subst.t (revision 0) +++ test/cmd-subst.t (revision 0) @@ -0,0 +1,37 @@ +#!/bin/sh + +. ./tap.sh + +plan 5 + +rm -rf testtree + +./testtree.sh testtree +test "`cat testtree/CHROOT`" = "testtree" || not +ok "testtree" + +t=`./fakechroot.sh testtree /bin/pwd` +test "$t" = "/" || not +ok "fakechroot pwd is /" + +export FAKECHROOT_CMD_SUBST="/bin/pwd=$(pwd)/cmd-subst-pwd.sh" + +t=`./fakechroot.sh testtree /bin/pwd` +test "$t" = "substituted" || not +ok "fakechroot substituted pwd (1)" + +export FAKECHROOT_CMD_SUBST="/no/file=foo:/bin/pwd=$(pwd)/cmd-subst-pwd.sh" + +t=`./fakechroot.sh testtree /bin/pwd` +test "$t" = "substituted" || not +ok "fakechroot substituted pwd (2)" + +export FAKECHROOT_CMD_SUBST="/no/file=foo:/other/file=bar" + +t=`./fakechroot.sh testtree /bin/pwd` +test "$t" = "/" || not +ok "fakechroot not substituted pwd is /" + +rm -rf testtree + +end Property changes on: test/cmd-subst.t ___________________________________________________________________ Added: svn:executable + * Index: test/Makefile.am =================================================================== --- test/Makefile.am (revision 323) +++ test/Makefile.am (working copy) @@ -1,6 +1,6 @@ SUBDIRS = src -TESTS = chroot.t escape-nested-chroot.t pwd.t touch.t +TESTS = chroot.t cmd-subst.t escape-nested-chroot.t pwd.t touch.t suffix = Index: src/libfakechroot.c =================================================================== --- src/libfakechroot.c (revision 323) +++ src/libfakechroot.c (working copy) @@ -1467,7 +1467,38 @@ return execve (path, argv, environ); } +/* Parse the FAKECHROOT_CMD_SUBST environment variable (the first + * parameter) and if there is a match with filename, return the + * substitution in cmd_subst. Returns non-zero if there was a match. + * + * FAKECHROOT_CMD_SUBST=cmd=subst:cmd=subst:... + */ +static int +try_cmd_subst (char *env, const char *filename, char *cmd_subst) +{ + int len = strlen (filename), len2; + char *p; + if (env == NULL) return 0; + + do { + p = strchrnul (env, ':'); + + if (strncmp (env, filename, len) == 0 && env[len] == '=') { + len2 = p - &env[len+1]; + if (len2 >= FAKECHROOT_MAXPATH) + len2 = FAKECHROOT_MAXPATH - 1; + strncpy (cmd_subst, &env[len+1], len2); + cmd_subst[len2] = '\0'; + return 1; + } + + env = p; + } while (*env++ != '\0'); + + return 0; +} + /* #include */ int execve (const char *filename, char *const argv [], char *const envp[]) { @@ -1479,32 +1510,16 @@ char *env; char tmp[FAKECHROOT_MAXPATH], newfilename[FAKECHROOT_MAXPATH], argv0[FAKECHROOT_MAXPATH]; char *ptr; - unsigned int i, j, n, len; + unsigned int i, j, n, len, r, newenvppos; size_t sizeenvp; char c; char *fakechroot_path, *fakechroot_ptr, fakechroot_buf[FAKECHROOT_MAXPATH]; char *envkey[] = { "FAKECHROOT", "FAKECHROOT_BASE", "FAKECHROOT_VERSION", "FAKECHROOT_EXCLUDE_PATH", + "FAKECHROOT_CMD_SUBST", "LD_LIBRARY_PATH", "LD_PRELOAD" }; + const int nr_envkey = sizeof envkey / sizeof envkey[0]; - strncpy(argv0, filename, FAKECHROOT_MAXPATH); - - expand_chroot_path(filename, fakechroot_path, fakechroot_ptr, fakechroot_buf); - strcpy(tmp, filename); - filename = tmp; - - if ((file = open(filename, O_RDONLY)) == -1) { - errno = ENOENT; - return -1; - } - - i = read(file, hashbang, FAKECHROOT_MAXPATH-2); - close(file); - if (i == -1) { - errno = ENOENT; - return -1; - } - if (next_execve == NULL) fakechroot_init(); /* Scan envp and check its size */ @@ -1514,39 +1529,69 @@ } /* Copy envp to newenvp */ - newenvp = malloc( sizeenvp * sizeof (char *) + sizeof(envkey) ); + newenvp = malloc( (sizeenvp + 1) * sizeof (char *) ); if (newenvp == NULL) { errno = ENOMEM; return -1; } - for (ep = envp, i = 0; *ep != NULL; ++ep) { - for (j = 0; j < sizeof (envkey) / sizeof (char *); j++) { + for (ep = envp, newenvppos = 0; *ep != NULL; ++ep) { + for (j = 0; j < nr_envkey; j++) { len = strlen (envkey[j]); if (strncmp (*ep, envkey[j], len) == 0 && (*ep)[len] == '=') goto skip; } - newenvp[i] = *ep; - i++; + newenvp[newenvppos] = *ep; + newenvppos++; skip: ; } + newenvp[newenvppos] = NULL; + strncpy(argv0, filename, FAKECHROOT_MAXPATH); + + r = try_cmd_subst (getenv ("FAKECHROOT_CMD_SUBST"), filename, tmp); + if (r) { + filename = tmp; + + /* FAKECHROOT_CMD_SUBST escapes the chroot. newenvp here does + * not contain LD_PRELOAD and the other special environment + * variables. + */ + return next_execve(filename, argv, newenvp); + } + + expand_chroot_path(filename, fakechroot_path, fakechroot_ptr, fakechroot_buf); + strcpy(tmp, filename); + filename = tmp; + + if ((file = open(filename, O_RDONLY)) == -1) { + errno = ENOENT; + return -1; + } + + i = read(file, hashbang, FAKECHROOT_MAXPATH-2); + close(file); + if (i == -1) { + errno = ENOENT; + return -1; + } + /* Add our variables to newenvp */ - newenvp = realloc( newenvp, i * sizeof(char *) + sizeof(envkey) ); + newenvp = realloc( newenvp, (newenvppos + nr_envkey + 1) * sizeof(char *) ); if (newenvp == NULL) { errno = ENOMEM; return -1; } - for (j = 0; j < sizeof(envkey) / sizeof(char *); j++) { + for (j = 0; j < nr_envkey; j++) { env = getenv(envkey[j]); if (env != NULL) { - newenvp[i] = malloc(strlen(envkey[j]) + strlen(env) + 3); - strcpy(newenvp[i], envkey[j]); - strcat(newenvp[i], "="); - strcat(newenvp[i], env); - i++; + newenvp[newenvppos] = malloc(strlen(envkey[j]) + strlen(env) + 3); + strcpy(newenvp[newenvppos], envkey[j]); + strcat(newenvp[newenvppos], "="); + strcat(newenvp[newenvppos], env); + newenvppos++; } } - newenvp[i] = NULL; + newenvp[newenvppos] = NULL; /* No hashbang in argv */ if (hashbang[0] != '#' || hashbang[1] != '!') Index: man/fakechroot.pod =================================================================== --- man/fakechroot.pod (revision 323) +++ man/fakechroot.pod (working copy) @@ -139,6 +139,21 @@ The list of directories which are excluded from being chrooted. The elements of list are separated with colon. +=item B + +A list of command substitutions. If a program tries to execute one of +the commands given (path relative to the chroot) then the substitute +command runs instead (path to substitute command is not chrooted). + +For example: + + FAKECHROOT_CMD_SUBST=/sbin/ldconfig=/tmp/ldconfig-wrapper + +will substitute C for C. + +Give as many substitute commands as you want, separated by C<:> +(colon) characters. + =item B, B Fakechroot is implemented by wrapping system calls. This is accomplished by