Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1#define _GNU_SOURCE 2#include <stdio.h> 3#include <stdarg.h> 4#include <stdlib.h> 5#include <unistd.h> 6#include <dlfcn.h> 7#include <sys/types.h> 8#include <sys/stat.h> 9#include <fcntl.h> 10#include <limits.h> 11#include <string.h> 12#include <spawn.h> 13#include <dirent.h> 14 15#define MAX_REDIRECTS 128 16 17#ifdef __APPLE__ 18 struct dyld_interpose { 19 const void * replacement; 20 const void * replacee; 21 }; 22 #define WRAPPER(ret, name) static ret _libredirect_wrapper_##name 23 #define LOOKUP_REAL(name) &name 24 #define WRAPPER_DEF(name) \ 25 __attribute__((used)) static struct dyld_interpose _libredirect_interpose_##name \ 26 __attribute__((section("__DATA,__interpose"))) = { &_libredirect_wrapper_##name, &name }; 27#else 28 #define WRAPPER(ret, name) ret name 29 #define LOOKUP_REAL(name) dlsym(RTLD_NEXT, #name) 30 #define WRAPPER_DEF(name) 31#endif 32 33static int nrRedirects = 0; 34static char * from[MAX_REDIRECTS]; 35static char * to[MAX_REDIRECTS]; 36 37static int isInitialized = 0; 38 39// FIXME: might run too late. 40static void init() __attribute__((constructor)); 41 42static void init() 43{ 44 if (isInitialized) return; 45 46 char * spec = getenv("NIX_REDIRECTS"); 47 if (!spec) return; 48 49 // Ensure we only run this code once. 50 // We do not do `unsetenv("NIX_REDIRECTS")` to ensure that redirects 51 // also get initialized for subprocesses. 52 isInitialized = 1; 53 54 char * spec2 = malloc(strlen(spec) + 1); 55 strcpy(spec2, spec); 56 57 char * pos = spec2, * eq; 58 while ((eq = strchr(pos, '='))) { 59 *eq = 0; 60 from[nrRedirects] = pos; 61 pos = eq + 1; 62 to[nrRedirects] = pos; 63 nrRedirects++; 64 if (nrRedirects == MAX_REDIRECTS) break; 65 char * end = strchr(pos, ':'); 66 if (!end) break; 67 *end = 0; 68 pos = end + 1; 69 } 70 71} 72 73static const char * rewrite(const char * volatile path, char * buf) 74{ 75 // Marking the path volatile is needed so the the following check isn't 76 // optimized away by the compiler. 77 if (path == NULL) return path; 78 79 for (int n = 0; n < nrRedirects; ++n) { 80 int len = strlen(from[n]); 81 if (strncmp(path, from[n], len) != 0) continue; 82 if (snprintf(buf, PATH_MAX, "%s%s", to[n], path + len) >= PATH_MAX) 83 abort(); 84 return buf; 85 } 86 87 return path; 88} 89 90static char * rewrite_non_const(char * path, char * buf) 91{ 92 // as long as the argument `path` is non-const, we can consider discarding 93 // the const qualifier of the return value to be safe. 94 return (char *)rewrite(path, buf); 95} 96 97static int open_needs_mode(int flags) 98{ 99#ifdef O_TMPFILE 100 return (flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE; 101#else 102 return flags & O_CREAT; 103#endif 104} 105 106/* The following set of Glibc library functions is very incomplete - 107 it contains only what we needed for programs in Nixpkgs. Just add 108 more functions as needed. */ 109 110WRAPPER(int, open)(const char * path, int flags, ...) 111{ 112 int (*open_real) (const char *, int, ...) = LOOKUP_REAL(open); 113 mode_t mode = 0; 114 if (open_needs_mode(flags)) { 115 va_list ap; 116 va_start(ap, flags); 117 mode = va_arg(ap, mode_t); 118 va_end(ap); 119 } 120 char buf[PATH_MAX]; 121 return open_real(rewrite(path, buf), flags, mode); 122} 123WRAPPER_DEF(open) 124 125// In musl libc, open64 is simply a macro for open 126#if !defined(__APPLE__) && !defined(open64) 127WRAPPER(int, open64)(const char * path, int flags, ...) 128{ 129 int (*open64_real) (const char *, int, mode_t) = LOOKUP_REAL(open64); 130 mode_t mode = 0; 131 if (open_needs_mode(flags)) { 132 va_list ap; 133 va_start(ap, flags); 134 mode = va_arg(ap, mode_t); 135 va_end(ap); 136 } 137 char buf[PATH_MAX]; 138 return open64_real(rewrite(path, buf), flags, mode); 139} 140WRAPPER_DEF(open64) 141#endif 142 143WRAPPER(int, openat)(int dirfd, const char * path, int flags, ...) 144{ 145 int (*openat_real) (int, const char *, int, ...) = LOOKUP_REAL(openat); 146 mode_t mode = 0; 147 if (open_needs_mode(flags)) { 148 va_list ap; 149 va_start(ap, flags); 150 mode = va_arg(ap, mode_t); 151 va_end(ap); 152 } 153 char buf[PATH_MAX]; 154 return openat_real(dirfd, rewrite(path, buf), flags, mode); 155} 156WRAPPER_DEF(openat) 157 158WRAPPER(FILE *, fopen)(const char * path, const char * mode) 159{ 160 FILE * (*fopen_real) (const char *, const char *) = LOOKUP_REAL(fopen); 161 char buf[PATH_MAX]; 162 return fopen_real(rewrite(path, buf), mode); 163} 164WRAPPER_DEF(fopen) 165 166#ifdef __GLIBC__ 167WRAPPER(FILE *, __nss_files_fopen)(const char * path) 168{ 169 FILE * (*__nss_files_fopen_real) (const char *) = LOOKUP_REAL(__nss_files_fopen); 170 char buf[PATH_MAX]; 171 return __nss_files_fopen_real(rewrite(path, buf)); 172} 173WRAPPER_DEF(__nss_files_fopen) 174#endif 175 176// In musl libc, fopen64 is simply a macro for fopen 177#if !defined(__APPLE__) && !defined(fopen64) 178WRAPPER(FILE *, fopen64)(const char * path, const char * mode) 179{ 180 FILE * (*fopen64_real) (const char *, const char *) = LOOKUP_REAL(fopen64); 181 char buf[PATH_MAX]; 182 return fopen64_real(rewrite(path, buf), mode); 183} 184WRAPPER_DEF(fopen64) 185#endif 186 187#ifdef __linux__ 188WRAPPER(int, __xstat)(int ver, const char * path, struct stat * st) 189{ 190 int (*__xstat_real) (int ver, const char *, struct stat *) = LOOKUP_REAL(__xstat); 191 char buf[PATH_MAX]; 192 return __xstat_real(ver, rewrite(path, buf), st); 193} 194WRAPPER_DEF(__xstat) 195#endif 196 197#ifdef __linux__ 198WRAPPER(int, __xstat64)(int ver, const char * path, struct stat64 * st) 199{ 200 int (*__xstat64_real) (int ver, const char *, struct stat64 *) = LOOKUP_REAL(__xstat64); 201 char buf[PATH_MAX]; 202 return __xstat64_real(ver, rewrite(path, buf), st); 203} 204WRAPPER_DEF(__xstat64) 205#endif 206 207#if defined(__linux__) && defined(STATX_TYPE) 208WRAPPER(int, statx)(int dirfd, const char * restrict pathname, int flags, 209 unsigned int mask, struct statx * restrict statxbuf) 210{ 211 int (*statx_real) (int, const char * restrict, int, 212 unsigned int, struct statx * restrict) = LOOKUP_REAL(statx); 213 char buf[PATH_MAX]; 214 return statx_real(dirfd, rewrite(pathname, buf), flags, mask, statxbuf); 215} 216WRAPPER_DEF(statx) 217#endif 218 219WRAPPER(int, fstatat)(int dirfd, const char * pathname, struct stat * statbuf, int flags) 220{ 221 int (*fstatat_real) (int, const char *, struct stat *, int) = LOOKUP_REAL(fstatat); 222 char buf[PATH_MAX]; 223 return fstatat_real(dirfd, rewrite(pathname, buf), statbuf, flags); 224} 225WRAPPER_DEF(fstatat); 226 227// In musl libc, fstatat64 is simply a macro for fstatat 228#if !defined(__APPLE__) && !defined(fstatat64) 229WRAPPER(int, fstatat64)(int dirfd, const char * pathname, struct stat64 * statbuf, int flags) 230{ 231 int (*fstatat64_real) (int, const char *, struct stat64 *, int) = LOOKUP_REAL(fstatat64); 232 char buf[PATH_MAX]; 233 return fstatat64_real(dirfd, rewrite(pathname, buf), statbuf, flags); 234} 235WRAPPER_DEF(fstatat64); 236#endif 237 238WRAPPER(int, stat)(const char * path, struct stat * st) 239{ 240 int (*__stat_real) (const char *, struct stat *) = LOOKUP_REAL(stat); 241 char buf[PATH_MAX]; 242 return __stat_real(rewrite(path, buf), st); 243} 244WRAPPER_DEF(stat) 245 246// In musl libc, stat64 is simply a macro for stat 247#if !defined(__APPLE__) && !defined(stat64) 248WRAPPER(int, stat64)(const char * path, struct stat64 * st) 249{ 250 int (*stat64_real) (const char *, struct stat64 *) = LOOKUP_REAL(stat64); 251 char buf[PATH_MAX]; 252 return stat64_real(rewrite(path, buf), st); 253} 254WRAPPER_DEF(stat64) 255#endif 256 257WRAPPER(int, access)(const char * path, int mode) 258{ 259 int (*access_real) (const char *, int mode) = LOOKUP_REAL(access); 260 char buf[PATH_MAX]; 261 return access_real(rewrite(path, buf), mode); 262} 263WRAPPER_DEF(access) 264 265WRAPPER(int, posix_spawn)(pid_t * pid, const char * path, 266 const posix_spawn_file_actions_t * file_actions, 267 const posix_spawnattr_t * attrp, 268 char * const argv[], char * const envp[]) 269{ 270 int (*posix_spawn_real) (pid_t *, const char *, 271 const posix_spawn_file_actions_t *, 272 const posix_spawnattr_t *, 273 char * const argv[], char * const envp[]) = LOOKUP_REAL(posix_spawn); 274 char buf[PATH_MAX]; 275 return posix_spawn_real(pid, rewrite(path, buf), file_actions, attrp, argv, envp); 276} 277WRAPPER_DEF(posix_spawn) 278 279WRAPPER(int, posix_spawnp)(pid_t * pid, const char * file, 280 const posix_spawn_file_actions_t * file_actions, 281 const posix_spawnattr_t * attrp, 282 char * const argv[], char * const envp[]) 283{ 284 int (*posix_spawnp_real) (pid_t *, const char *, 285 const posix_spawn_file_actions_t *, 286 const posix_spawnattr_t *, 287 char * const argv[], char * const envp[]) = LOOKUP_REAL(posix_spawnp); 288 char buf[PATH_MAX]; 289 return posix_spawnp_real(pid, rewrite(file, buf), file_actions, attrp, argv, envp); 290} 291WRAPPER_DEF(posix_spawnp) 292 293WRAPPER(int, execv)(const char * path, char * const argv[]) 294{ 295 int (*execv_real) (const char * path, char * const argv[]) = LOOKUP_REAL(execv); 296 char buf[PATH_MAX]; 297 return execv_real(rewrite(path, buf), argv); 298} 299WRAPPER_DEF(execv) 300 301WRAPPER(int, execvp)(const char * path, char * const argv[]) 302{ 303 int (*_execvp) (const char *, char * const argv[]) = LOOKUP_REAL(execvp); 304 char buf[PATH_MAX]; 305 return _execvp(rewrite(path, buf), argv); 306} 307WRAPPER_DEF(execvp) 308 309WRAPPER(int, execve)(const char * path, char * const argv[], char * const envp[]) 310{ 311 int (*_execve) (const char *, char * const argv[], char * const envp[]) = LOOKUP_REAL(execve); 312 char buf[PATH_MAX]; 313 return _execve(rewrite(path, buf), argv, envp); 314} 315WRAPPER_DEF(execve) 316 317WRAPPER(DIR *, opendir)(const char * path) 318{ 319 char buf[PATH_MAX]; 320 DIR * (*_opendir) (const char*) = LOOKUP_REAL(opendir); 321 322 return _opendir(rewrite(path, buf)); 323} 324WRAPPER_DEF(opendir) 325 326#define SYSTEM_CMD_MAX 512 327 328static char * replace_substring(char * source, char * buf, char * replace_string, char * start_ptr, char * suffix_ptr) { 329 char head[SYSTEM_CMD_MAX] = {0}; 330 strncpy(head, source, start_ptr - source); 331 332 char tail[SYSTEM_CMD_MAX] = {0}; 333 if(suffix_ptr < source + strlen(source)) { 334 strcpy(tail, suffix_ptr); 335 } 336 337 sprintf(buf, "%s%s%s", head, replace_string, tail); 338 return buf; 339} 340 341static char * replace_string(char * buf, char * from, char * to) { 342 int num_matches = 0; 343 char * matches[SYSTEM_CMD_MAX]; 344 int from_len = strlen(from); 345 for(int i=0; i<strlen(buf); i++){ 346 char *cmp_start = buf + i; 347 if(strncmp(from, cmp_start, from_len) == 0){ 348 matches[num_matches] = cmp_start; 349 num_matches++; 350 } 351 } 352 int len_diff = strlen(to) - strlen(from); 353 for(int n = 0; n < num_matches; n++) { 354 char replaced[SYSTEM_CMD_MAX]; 355 replace_substring(buf, replaced, to, matches[n], matches[n]+from_len); 356 strcpy(buf, replaced); 357 for(int nn = n+1; nn < num_matches; nn++) { 358 matches[nn] += len_diff; 359 } 360 } 361 return buf; 362} 363 364static void rewriteSystemCall(const char * command, char * buf) { 365 char * p = buf; 366 367 #ifdef __APPLE__ 368 // The dyld environment variable is not inherited by the subprocess spawned 369 // by system(), so this hack redefines it. 370 Dl_info info; 371 dladdr(&rewriteSystemCall, &info); 372 p = stpcpy(p, "export DYLD_INSERT_LIBRARIES="); 373 p = stpcpy(p, info.dli_fname); 374 p = stpcpy(p, ";"); 375 #endif 376 377 stpcpy(p, command); 378 379 for (int n = 0; n < nrRedirects; ++n) { 380 replace_string(buf, from[n], to[n]); 381 } 382} 383 384WRAPPER(int, system)(const char *command) 385{ 386 int (*_system) (const char*) = LOOKUP_REAL(system); 387 388 char newCommand[SYSTEM_CMD_MAX]; 389 rewriteSystemCall(command, newCommand); 390 return _system(newCommand); 391} 392WRAPPER_DEF(system) 393 394WRAPPER(int, chdir)(const char *path) 395{ 396 int (*chdir_real) (const char *) = LOOKUP_REAL(chdir); 397 char buf[PATH_MAX]; 398 return chdir_real(rewrite(path, buf)); 399} 400WRAPPER_DEF(chdir); 401 402WRAPPER(int, mkdir)(const char *path, mode_t mode) 403{ 404 int (*mkdir_real) (const char *path, mode_t mode) = LOOKUP_REAL(mkdir); 405 char buf[PATH_MAX]; 406 return mkdir_real(rewrite(path, buf), mode); 407} 408WRAPPER_DEF(mkdir) 409 410WRAPPER(int, mkdirat)(int dirfd, const char *path, mode_t mode) 411{ 412 int (*mkdirat_real) (int dirfd, const char *path, mode_t mode) = LOOKUP_REAL(mkdirat); 413 char buf[PATH_MAX]; 414 return mkdirat_real(dirfd, rewrite(path, buf), mode); 415} 416WRAPPER_DEF(mkdirat) 417 418WRAPPER(int, unlink)(const char *path) 419{ 420 int (*unlink_real) (const char *path) = LOOKUP_REAL(unlink); 421 char buf[PATH_MAX]; 422 return unlink_real(rewrite(path, buf)); 423} 424WRAPPER_DEF(unlink) 425 426WRAPPER(int, unlinkat)(int dirfd, const char *path, int flags) 427{ 428 int (*unlinkat_real) (int dirfd, const char *path, int flags) = LOOKUP_REAL(unlinkat); 429 char buf[PATH_MAX]; 430 return unlinkat_real(dirfd, rewrite(path, buf), flags); 431} 432WRAPPER_DEF(unlinkat) 433 434WRAPPER(int, rmdir)(const char *path) 435{ 436 int (*rmdir_real) (const char *path) = LOOKUP_REAL(rmdir); 437 char buf[PATH_MAX]; 438 return rmdir_real(rewrite(path, buf)); 439} 440WRAPPER_DEF(rmdir) 441 442static void copy_temp_wildcard(char * dest, char * src, int suffixlen) { 443 int dest_len = strnlen(dest, PATH_MAX); 444 int src_len = strnlen(src, PATH_MAX); 445 memcpy(dest + dest_len - (6 + suffixlen), src + src_len - (6 + suffixlen), 6); 446} 447 448WRAPPER(int, mkstemp)(char *template) 449{ 450 int (*mkstemp_real) (char *template) = LOOKUP_REAL(mkstemp); 451 char buf[PATH_MAX]; 452 char * rewritten = rewrite_non_const(template, buf); 453 int retval = mkstemp_real(rewritten); 454 if (retval >= 0 && rewritten != template) { 455 copy_temp_wildcard(template, rewritten, 0); 456 } 457 return retval; 458} 459WRAPPER_DEF(mkstemp) 460 461WRAPPER(int, mkostemp)(char *template, int flags) 462{ 463 int (*mkostemp_real) (char *template, int flags) = LOOKUP_REAL(mkostemp); 464 char buf[PATH_MAX]; 465 char * rewritten = rewrite_non_const(template, buf); 466 int retval = mkostemp_real(rewritten, flags); 467 if (retval >= 0 && rewritten != template) { 468 copy_temp_wildcard(template, rewritten, 0); 469 } 470 return retval; 471} 472WRAPPER_DEF(mkostemp) 473 474WRAPPER(int, mkstemps)(char *template, int suffixlen) 475{ 476 int (*mkstemps_real) (char *template, int suffixlen) = LOOKUP_REAL(mkstemps); 477 char buf[PATH_MAX]; 478 char * rewritten = rewrite_non_const(template, buf); 479 int retval = mkstemps_real(rewritten, suffixlen); 480 if (retval >= 0 && rewritten != template) { 481 copy_temp_wildcard(template, rewritten, suffixlen); 482 } 483 return retval; 484} 485WRAPPER_DEF(mkstemps) 486 487WRAPPER(int, mkostemps)(char *template, int suffixlen, int flags) 488{ 489 int (*mkostemps_real) (char *template, int suffixlen, int flags) = LOOKUP_REAL(mkostemps); 490 char buf[PATH_MAX]; 491 char * rewritten = rewrite_non_const(template, buf); 492 int retval = mkostemps_real(rewritten, suffixlen, flags); 493 if (retval >= 0 && rewritten != template) { 494 copy_temp_wildcard(template, rewritten, suffixlen); 495 } 496 return retval; 497} 498WRAPPER_DEF(mkostemps) 499 500WRAPPER(char *, mkdtemp)(char *template) 501{ 502 char * (*mkdtemp_real) (char *template) = LOOKUP_REAL(mkdtemp); 503 char buf[PATH_MAX]; 504 char * rewritten = rewrite_non_const(template, buf); 505 char * retval = mkdtemp_real(rewritten); 506 if (retval == NULL) { 507 return retval; 508 }; 509 if (rewritten != template) { 510 copy_temp_wildcard(template, rewritten, 0); 511 } 512 return template; 513} 514WRAPPER_DEF(mkdtemp) 515 516WRAPPER(char *, mktemp)(char *template) 517{ 518 char * (*mktemp_real) (char *template) = LOOKUP_REAL(mktemp); 519 char buf[PATH_MAX]; 520 char * rewritten = rewrite_non_const(template, buf); 521 char * retval = mktemp_real(rewritten); 522 if (retval == NULL) { 523 return retval; 524 }; 525 if (rewritten != template) { 526 copy_temp_wildcard(template, rewritten, 0); 527 } 528 return template; 529} 530WRAPPER_DEF(mktemp)