| #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 |