diff --git a/pkgs/build-support/libredirect/default.nix b/pkgs/build-support/libredirect/default.nix index 5545c93c19e1..4c344a24b93b 100644 --- a/pkgs/build-support/libredirect/default.nix +++ b/pkgs/build-support/libredirect/default.nix @@ -57,7 +57,9 @@ else stdenv.mkDerivation rec { ''} if [ -n "$doInstallCheck" ]; then - $CC -Wall -std=c99 -O3 test.c -o test + $CC -Wall -std=c99 \ + ${lib.optionalString (!stdenv.isDarwin) "-D_GNU_SOURCE"} \ + -O3 test.c -o test fi runHook postBuild @@ -91,7 +93,7 @@ else stdenv.mkDerivation rec { installCheckPhase = '' ( source "$hook/nix-support/setup-hook" - NIX_REDIRECTS="/foo/bar/test=${coreutils}/bin/true" ./test + NIX_REDIRECTS="/foo/bar/test=${coreutils}/bin/true:/bar/baz=$(mktemp -d)" ./test ) ''; diff --git a/pkgs/build-support/libredirect/libredirect.c b/pkgs/build-support/libredirect/libredirect.c index 0dc7feb382c3..1222d2ee75c6 100644 --- a/pkgs/build-support/libredirect/libredirect.c +++ b/pkgs/build-support/libredirect/libredirect.c @@ -84,6 +84,13 @@ static const char * rewrite(const char * path, char * buf) return path; } +static char * rewrite_non_const(char * path, char * buf) +{ + // as long as the argument `path` is non-const, we can consider discarding + // the const qualifier of the return value to be safe. + return (char *)rewrite(path, buf); +} + static int open_needs_mode(int flags) { #ifdef O_TMPFILE @@ -354,3 +361,117 @@ WRAPPER(int, mkdirat)(int dirfd, const char *path, mode_t mode) return mkdirat_real(dirfd, rewrite(path, buf), mode); } WRAPPER_DEF(mkdirat) + +WRAPPER(int, unlink)(const char *path) +{ + int (*unlink_real) (const char *path) = LOOKUP_REAL(unlink); + char buf[PATH_MAX]; + return unlink_real(rewrite(path, buf)); +} +WRAPPER_DEF(unlink) + +WRAPPER(int, unlinkat)(int dirfd, const char *path, int flags) +{ + int (*unlinkat_real) (int dirfd, const char *path, int flags) = LOOKUP_REAL(unlinkat); + char buf[PATH_MAX]; + return unlinkat_real(dirfd, rewrite(path, buf), flags); +} +WRAPPER_DEF(unlinkat) + +WRAPPER(int, rmdir)(const char *path) +{ + int (*rmdir_real) (const char *path) = LOOKUP_REAL(rmdir); + char buf[PATH_MAX]; + return rmdir_real(rewrite(path, buf)); +} +WRAPPER_DEF(rmdir) + +static void copy_temp_wildcard(char * dest, char * src, int suffixlen) { + int dest_len = strnlen(dest, PATH_MAX); + int src_len = strnlen(src, PATH_MAX); + memcpy(dest + dest_len - (6 + suffixlen), src + src_len - (6 + suffixlen), 6); +} + +WRAPPER(int, mkstemp)(char *template) +{ + int (*mkstemp_real) (char *template) = LOOKUP_REAL(mkstemp); + char buf[PATH_MAX]; + char * rewritten = rewrite_non_const(template, buf); + int retval = mkstemp_real(rewritten); + if (retval >= 0 && rewritten != template) { + copy_temp_wildcard(template, rewritten, 0); + } + return retval; +} +WRAPPER_DEF(mkstemp) + +WRAPPER(int, mkostemp)(char *template, int flags) +{ + int (*mkostemp_real) (char *template, int flags) = LOOKUP_REAL(mkostemp); + char buf[PATH_MAX]; + char * rewritten = rewrite_non_const(template, buf); + int retval = mkostemp_real(rewritten, flags); + if (retval >= 0 && rewritten != template) { + copy_temp_wildcard(template, rewritten, 0); + } + return retval; +} +WRAPPER_DEF(mkostemp) + +WRAPPER(int, mkstemps)(char *template, int suffixlen) +{ + int (*mkstemps_real) (char *template, int suffixlen) = LOOKUP_REAL(mkstemps); + char buf[PATH_MAX]; + char * rewritten = rewrite_non_const(template, buf); + int retval = mkstemps_real(rewritten, suffixlen); + if (retval >= 0 && rewritten != template) { + copy_temp_wildcard(template, rewritten, suffixlen); + } + return retval; +} +WRAPPER_DEF(mkstemps) + +WRAPPER(int, mkostemps)(char *template, int suffixlen, int flags) +{ + int (*mkostemps_real) (char *template, int suffixlen, int flags) = LOOKUP_REAL(mkostemps); + char buf[PATH_MAX]; + char * rewritten = rewrite_non_const(template, buf); + int retval = mkostemps_real(rewritten, suffixlen, flags); + if (retval >= 0 && rewritten != template) { + copy_temp_wildcard(template, rewritten, suffixlen); + } + return retval; +} +WRAPPER_DEF(mkostemps) + +WRAPPER(char *, mkdtemp)(char *template) +{ + char * (*mkdtemp_real) (char *template) = LOOKUP_REAL(mkdtemp); + char buf[PATH_MAX]; + char * rewritten = rewrite_non_const(template, buf); + char * retval = mkdtemp_real(rewritten); + if (retval == NULL) { + return retval; + }; + if (rewritten != template) { + copy_temp_wildcard(template, rewritten, 0); + } + return template; +} +WRAPPER_DEF(mkdtemp) + +WRAPPER(char *, mktemp)(char *template) +{ + char * (*mktemp_real) (char *template) = LOOKUP_REAL(mktemp); + char buf[PATH_MAX]; + char * rewritten = rewrite_non_const(template, buf); + char * retval = mktemp_real(rewritten); + if (retval == NULL) { + return retval; + }; + if (rewritten != template) { + copy_temp_wildcard(template, rewritten, 0); + } + return template; +} +WRAPPER_DEF(mktemp) diff --git a/pkgs/build-support/libredirect/test.c b/pkgs/build-support/libredirect/test.c index 853f26bb5209..144e6d3d66dd 100644 --- a/pkgs/build-support/libredirect/test.c +++ b/pkgs/build-support/libredirect/test.c @@ -1,14 +1,18 @@ #include +#include #include +#include #include #include #include +#include #include #include #include #include +#define TESTDIR "/bar/baz" #define TESTPATH "/foo/bar/test" #define SUBTEST "./test sub" @@ -41,11 +45,25 @@ void test_subprocess(void) { assert(system(SUBTEST) == 0); } +void assert_mktemp_path( + const char * orig_prefix, + const char * orig_suffix, + const char * updated +) { + // prefix unchanged + assert(strncmp(updated, orig_prefix, strlen(orig_prefix)) == 0); + // wildcards replaced + assert(strcmp(updated + strlen(orig_prefix), "XXXXXX") != 0); + // suffix unchanged + assert(strcmp(updated + strlen(orig_prefix) + 6, orig_suffix) == 0); +} + int main(int argc, char *argv[]) { FILE *testfp; int testfd; struct stat testsb; + char buf[PATH_MAX]; testfp = fopen(TESTPATH, "r"); assert(testfp != NULL); @@ -59,6 +77,54 @@ int main(int argc, char *argv[]) assert(stat(TESTPATH, &testsb) != -1); + assert(mkdir(TESTDIR "/dir-mkdir", 0777) == 0); + assert(unlink(TESTDIR "/dir-mkdir") == -1); // it's a directory! +#ifndef __APPLE__ + assert(errno == EISDIR); +#endif + assert(rmdir(TESTDIR "/dir-mkdir") == 0); + assert(unlink(TESTDIR "/dir-mkdir") == -1); + assert(errno == ENOENT); + + assert(mkdirat(123, TESTDIR "/dir-mkdirat", 0777) == 0); + assert(unlinkat(123, TESTDIR "/dir-mkdirat", 0) == -1); // it's a directory! +#ifndef __APPLE__ + assert(errno == EISDIR); +#endif + assert(unlinkat(123, TESTDIR "/dir-mkdirat", AT_REMOVEDIR) == 0); + + strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX); + testfd = mkstemp(buf); + assert(testfd > 0); + assert_mktemp_path(TESTDIR "/temp", "", buf); + close(testfd); + + strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX); + testfd = mkostemp(buf, 0); + assert(testfd > 0); + assert_mktemp_path(TESTDIR "/temp", "", buf); + close(testfd); + + strncpy(buf, TESTDIR "/tempXXXXXX.test", PATH_MAX); + testfd = mkstemps(buf, strlen(".test")); + assert(testfd > 0); + assert_mktemp_path(TESTDIR "/temp", ".test", buf); + close(testfd); + + strncpy(buf, TESTDIR "/tempXXXXXX.test", PATH_MAX); + testfd = mkostemps(buf, strlen(".test"), 0); + assert(testfd > 0); + assert_mktemp_path(TESTDIR "/temp", ".test", buf); + close(testfd); + + strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX); + assert(mkdtemp(buf) == buf); + assert_mktemp_path(TESTDIR "/temp", "", buf); + + strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX); + assert(mktemp(buf) == buf); + assert_mktemp_path(TESTDIR "/temp", "", buf); + test_spawn(); test_system();