| #define open Oopen |
| #define open64 Oopen64 |
| #define openat Oopenat |
| #define openat64 Oopenat64 |
| #define rename Orename |
| #define unlink Ounlink |
| #define fopen Ofopen |
| #define fopen64 Ofopen64 |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/mman.h> |
| #include <limits.h> |
| #include <stdbool.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <dlfcn.h> |
| |
| #include "../emit.h" |
| #include "../fsatrace.h" |
| #include "../proc.h" |
| |
| #undef open |
| #undef open64 |
| #undef openat |
| #undef openat64 |
| #undef rename |
| #undef unlink |
| #undef fopen |
| #undef fopen64 |
| |
| static const int wmode = O_RDWR | O_WRONLY | O_APPEND | O_CREAT | O_TRUNC; |
| static const bool debug = false; |
| |
| #define D \ |
| do { \ |
| char b[PATH_MAX]; \ |
| if (!debug) \ |
| break; \ |
| procPath(b); \ |
| fprintf(stderr, "%s:%d %s\n", b, getpid(), __FUNCTION__); \ |
| fflush(stderr); \ |
| } while (0) |
| #define DD \ |
| do { \ |
| char b[PATH_MAX]; \ |
| if (!debug) \ |
| break; \ |
| procPath(b); \ |
| fprintf(stderr, "%s:%d /%s ->%lld\n", b, getpid(), \ |
| __FUNCTION__, (long long)r); \ |
| fflush(stderr); \ |
| } while (0) |
| #define DP \ |
| do { \ |
| char b[PATH_MAX]; \ |
| if (!debug) \ |
| break; \ |
| procPath(b); \ |
| fprintf( \ |
| stderr, "%s:%d %s %s\n", b, getpid(), __FUNCTION__, p); \ |
| fflush(stderr); \ |
| } while (0) |
| |
| #define SE int _oerrno = errno |
| #define RE errno = _oerrno |
| |
| static void |
| err(const char *msg, int err) |
| { |
| extern const char *__progname; |
| if (debug) |
| fprintf(stderr, "%s %s error: %x\n", __progname, msg, err); |
| } |
| |
| static void |
| emit(int c, const char *p1) |
| { |
| char ap[PATH_MAX]; |
| SE; |
| emitOp(c, realpath(p1, ap), 0); |
| RE; |
| } |
| |
| static int |
| fdpath(int fd, char *ap, size_t sz) |
| { |
| int ok; |
| #ifdef F_GETPATH |
| ok = -1 != fcntl(fd, F_GETPATH, ap); |
| #else |
| ssize_t written; |
| char fdp[100]; |
| snprintf(fdp, sizeof(fdp), "/proc/self/fd/%d", fd); |
| ap[0] = 0; |
| written = readlink(fdp, ap, sz); |
| ok = written >= 0 && written < sz; |
| if (ok) |
| ap[written] = 0; |
| #endif |
| if (!ok) |
| ap[0] = 0; |
| return ok; |
| } |
| |
| static void |
| fdemit(int c, int fd) |
| { |
| char ap[PATH_MAX]; |
| int ok; |
| SE; |
| ok = fdpath(fd, ap, sizeof(ap)); |
| emitOp(c, ok ? ap : 0, 0); |
| RE; |
| } |
| |
| static void __attribute((constructor(101))) init() |
| { |
| int r; |
| D; |
| r = emitInit(); |
| if (r) |
| err("init", r); |
| else { |
| char b[PATH_MAX]; |
| procPath(b); |
| emit('r', b); |
| } |
| DD; |
| } |
| |
| static void __attribute((destructor(101))) term() |
| { |
| int r; |
| r = emitTerm(); |
| D; |
| if (r) |
| err("term", r); |
| DD; |
| } |
| |
| static void |
| resolv(void **p, const char *n) |
| { |
| if (!*p) { |
| SE; |
| *p = dlsym(RTLD_NEXT, n); |
| RE; |
| } |
| assert(*p); |
| } |
| |
| #define R(f) resolv((void **)&o##f, #f) |
| |
| static char * |
| realpathat(int ifd, const char *p, char *ap, size_t sz) |
| { |
| int f; |
| int ok; |
| int fd; |
| int cur = ifd == AT_FDCWD; |
| static int (*oopenat)(int, const char *, int, mode_t) = 0; |
| R(openat); |
| if (cur) |
| fd = oopenat(AT_FDCWD, ".", O_RDONLY, 0); |
| else |
| fd = ifd; |
| f = oopenat(fd, p, O_RDONLY, 0); |
| ok = fdpath(f, ap, sz); |
| close(f); |
| if (cur) |
| close(fd); |
| return ok ? ap : NULL; |
| } |
| |
| static void |
| atemit(int c, int fd, const char *p, char *pp) |
| { |
| char ap[PATH_MAX]; |
| SE; |
| emitOp(c, realpathat(fd, p, ap, sizeof(ap)), pp); |
| RE; |
| } |
| |
| FILE * |
| fopen(const char *p, const char *m) |
| { |
| FILE *r; |
| static FILE *(*ofopen)(const char *, const char *) = 0; |
| DP; |
| R(fopen); |
| r = ofopen(p, m); |
| if (r) |
| emit(strchr(m, 'r') ? 'r' : 'w', p); |
| DD; |
| return r; |
| } |
| |
| FILE * |
| fopen64(const char *p, const char *m) |
| { |
| FILE *r; |
| static FILE *(*ofopen64)(const char *, const char *) = 0; |
| DP; |
| R(fopen64); |
| r = ofopen64(p, m); |
| if (r) |
| emit(strchr(m, 'r') ? 'r' : 'w', p); |
| DD; |
| return r; |
| } |
| |
| int |
| open(const char *p, int f, mode_t m) |
| { |
| int r; |
| static int (*oopen)(const char *, int, mode_t) = 0; |
| DP; |
| R(open); |
| r = oopen(p, f, m); |
| if (r >= 0) |
| emit(f & wmode ? 'w' : 'r', p); |
| DD; |
| return r; |
| } |
| |
| int |
| open64(const char *p, int f, mode_t m) |
| { |
| int r; |
| static int (*oopen64)(const char *, int, mode_t) = 0; |
| DP; |
| R(open64); |
| r = oopen64(p, f, m); |
| if (r >= 0) |
| emit(f & wmode ? 'w' : 'r', p); |
| DD; |
| return r; |
| } |
| |
| int |
| openat(int fd, const char *p, int f, mode_t m) |
| { |
| int r; |
| DP; |
| static int (*oopenat)(int, const char *, int, mode_t) = 0; |
| R(openat); |
| r = oopenat(fd, p, f, m); |
| if (r >= 0) |
| fdemit(f & wmode ? 'w' : 'r', r); |
| DD; |
| return r; |
| } |
| |
| int |
| openat64(int fd, const char *p, int f, mode_t m) |
| { |
| int r; |
| DP; |
| static int (*oopenat64)(int, const char *, int, mode_t) = 0; |
| R(openat64); |
| r = oopenat64(fd, p, f, m); |
| if (r >= 0) |
| fdemit(f & wmode ? 'w' : 'r', r); |
| DD; |
| return r; |
| } |
| |
| int |
| rename(const char *p1, const char *p2) |
| { |
| int r; |
| char b1[PATH_MAX]; |
| char *rp1 = realpath(p1, b1); |
| static int (*orename)(const char *, const char *) = 0; |
| D; |
| R(rename); |
| r = orename(p1, p2); |
| if (!r) { |
| char b2[PATH_MAX]; |
| char *rp2 = realpath(p2, b2); |
| emitOp(rp1 ? 'm' : 'M', rp2, rp1); |
| } |
| DD; |
| return r; |
| } |
| |
| int |
| renamex_np(const char *p1, const char *p2, unsigned fl) |
| { |
| int r; |
| char b1[PATH_MAX]; |
| char *rp1 = realpath(p1, b1); |
| static int (*orenamex_np)(const char *, const char *, unsigned) = 0; |
| D; |
| R(renamex_np); |
| r = orenamex_np(p1, p2, fl); |
| if (!r) { |
| char b2[PATH_MAX]; |
| char *rp2 = realpath(p2, b2); |
| emitOp(rp1 ? 'm' : 'M', rp2, rp1); |
| } |
| DD; |
| return r; |
| } |
| |
| int |
| renameat2(int fd1, const char *p1, int fd2, const char *p2, unsigned fl) |
| { |
| int r; |
| D; |
| static int (*orenameat2)( |
| int, const char *, int, const char *, unsigned) = 0; |
| char b[PATH_MAX]; |
| R(renameat2); |
| realpathat(fd1, p1, b, sizeof(b)); |
| r = orenameat2(fd1, p1, fd2, p2, fl); |
| if (!r) |
| atemit('m', fd2, p2, b); |
| DD; |
| return r; |
| } |
| |
| int |
| renameat(int fd1, const char *p1, int fd2, const char *p2) |
| { |
| int r; |
| D; |
| static int (*orenameat)(int, const char *, int, const char *) = 0; |
| char b[PATH_MAX]; |
| R(renameat); |
| realpathat(fd1, p1, b, sizeof(b)); |
| r = orenameat(fd1, p1, fd2, p2); |
| if (!r) |
| atemit('m', fd2, p2, b); |
| DD; |
| return r; |
| } |
| |
| int |
| renameatx_np(int fd1, const char *p1, int fd2, const char *p2, unsigned fl) |
| { |
| int r; |
| char b[PATH_MAX]; |
| D; |
| static int (*orenameatx_np)( |
| int, const char *, int, const char *, unsigned) = 0; |
| R(renameatx_np); |
| realpathat(fd1, p1, b, sizeof(b)); |
| r = orenameatx_np(fd1, p1, fd2, p2, fl); |
| if (!r) |
| atemit('m', fd2, p2, b); |
| DD; |
| return r; |
| } |
| |
| int |
| unlink(const char *p) |
| { |
| int r; |
| char b[PATH_MAX]; |
| char *rp; |
| static int (*ounlink)(const char *) = 0; |
| DP; |
| R(unlink); |
| rp = realpath(p, b); |
| r = ounlink(p); |
| if (!r) |
| emitOp('d', rp, 0); |
| DD; |
| return r; |
| } |
| |
| int |
| unlinkat(int fd, const char *p, int f) |
| { |
| int r; |
| DP; |
| char b[PATH_MAX]; |
| char *rp = realpathat(fd, p, b, sizeof(b)); |
| static int (*ounlinkat)(int, const char *, int); |
| R(unlinkat); |
| r = ounlinkat(fd, p, f); |
| if (!r) |
| emitOp(rp ? 'd' : 'D', rp ? rp : p, 0); |
| DD; |
| return r; |
| } |
| |
| int |
| futimes(int fd, const struct timeval t[2]) |
| { |
| int r; |
| static int (*ofutimes)(int, const struct timeval[2]); |
| D; |
| R(futimes); |
| r = ofutimes(fd, t); |
| if (!r) |
| fdemit('t', fd); |
| DD; |
| return r; |
| } |
| |
| int |
| utimes(const char *p, const struct timeval t[2]) |
| { |
| int r; |
| static int (*outimes)(const char *, const struct timeval[2]); |
| DP; |
| R(utimes); |
| r = outimes(p, t); |
| if (!r) |
| emit('t', p); |
| DD; |
| return r; |
| } |
| |
| #ifdef __APPLE__ |
| #define SUF "$INODE64" |
| |
| static volatile int __thread nested = 0; |
| |
| int |
| fstat(int fd, struct stat *buf) |
| { |
| int r; |
| static int (*ofstat)(int, struct stat *) = 0; |
| D; |
| resolv((void **)&ofstat, "fstat" SUF); |
| nested++; |
| r = ofstat(fd, buf); |
| if (!r && nested == 1) |
| fdemit('q', fd); |
| nested--; |
| DD; |
| return r; |
| } |
| |
| int |
| stat(const char *p, struct stat *buf) |
| { |
| int r; |
| static int (*ostat)(const char *restrict, struct stat *restrict) = 0; |
| DP; |
| resolv((void **)&ostat, "stat" SUF); |
| nested++; |
| r = ostat(p, buf); |
| if (!r && nested == 1) |
| emit('q', p); |
| nested--; |
| DD; |
| return r; |
| } |
| |
| int |
| access(const char *p, int m) |
| { |
| int r; |
| static int (*oaccess)(const char *, int) = 0; |
| DP; |
| R(access); |
| nested++; |
| r = oaccess(p, m); |
| if (!r && nested == 1) |
| emit('q', p); |
| nested--; |
| DD; |
| return r; |
| } |
| |
| int |
| lstat(const char *restrict p, struct stat *buf) |
| { |
| int r; |
| static int (*olstat)(const char *restrict, struct stat *restrict) = 0; |
| DP; |
| resolv((void **)&olstat, "lstat" SUF); |
| nested++; |
| r = olstat(p, buf); |
| if (!r && nested == 1) |
| emit('q', p); |
| nested--; |
| DD; |
| return r; |
| } |
| |
| int |
| fstatat(int fd, const char *p, struct stat *buf, int flag) |
| { |
| int r; |
| DP; |
| static int (*ofstatat)(int, const char *, struct stat *, int) = 0; |
| R(fstatat); |
| r = ofstatat(fd, p, buf, flag); |
| if (!r) |
| atemit('q', fd, p, NULL); |
| DD; |
| return r; |
| } |
| #endif |
| |
| #ifdef __linux__ |
| int |
| __fxstat(int v, int fd, struct stat *restrict buf) |
| { |
| int r; |
| static int (*o__fxstat)(int, int, struct stat *restrict) = 0; |
| D; |
| R(__fxstat); |
| r = o__fxstat(v, fd, buf); |
| if (!r) |
| fdemit('q', fd); |
| DD; |
| return r; |
| } |
| |
| int |
| __xstat(int v, const char *restrict p, struct stat *restrict buf) |
| { |
| int r; |
| static int (*o__xstat)( |
| int, const char *restrict, struct stat *restrict) = 0; |
| DP; |
| R(__xstat); |
| r = o__xstat(v, p, buf); |
| if (!r) |
| emit('q', p); |
| DD; |
| return r; |
| } |
| |
| int |
| __xlstat(int v, const char *restrict p, struct stat *restrict buf) |
| { |
| int r; |
| static int (*o__xlstat)( |
| int, const char *restrict, struct stat *restrict) = 0; |
| DP; |
| R(__xlstat); |
| r = o__xlstat(v, p, buf); |
| if (!r) |
| emit('q', p); |
| DD; |
| return r; |
| } |
| |
| int |
| __lxstat(int v, const char *restrict p, struct stat *restrict buf) |
| { |
| int r; |
| static int (*o__lxstat)( |
| int, const char *restrict, struct stat *restrict) = 0; |
| DP; |
| R(__lxstat); |
| r = o__lxstat(v, p, buf); |
| if (!r) |
| emit('q', p); |
| DD; |
| return r; |
| } |
| |
| int |
| __fxstatat(int v, int fd, const char *p, struct stat *buf, int flag) |
| { |
| int r; |
| DP; |
| static int (*o__fxstatat)( |
| int, int, const char *, struct stat *restrict, int) = 0; |
| R(__fxstatat); |
| r = o__fxstatat(v, fd, p, buf, flag); |
| if (!r) |
| atemit('q', fd, p, NULL); |
| DD; |
| return r; |
| } |
| |
| int |
| utimensat(int fd, const char *p, const struct timespec ts[2], int flags) |
| { |
| int r; |
| static int (*outimensat)( |
| int, const char *, const struct timespec[2], int); |
| DP; |
| R(utimensat); |
| r = outimensat(fd, p, ts, flags); |
| if (!r) |
| atemit('t', fd, p, NULL); |
| DD; |
| return r; |
| } |
| |
| int |
| futimens(int fd, const struct timespec ts[2]) |
| { |
| int r; |
| static int (*ofutimens)(int, const struct timespec[2]); |
| D; |
| R(futimens); |
| r = ofutimens(fd, ts); |
| if (!r) |
| fdemit('t', fd); |
| DD; |
| return r; |
| } |
| |
| #endif |