diff options
author | Adrian Kummerlaender | 2015-10-10 21:42:30 +0200 |
---|---|---|
committer | Adrian Kummerlaender | 2015-10-10 21:42:30 +0200 |
commit | 5c7c4edfe45be83211d5c6217824dd4b9ec562d2 (patch) | |
tree | 25c0eb12c8cfd1d52e535ba6997d5cdf75c0fc26 | |
download | change-5c7c4edfe45be83211d5c6217824dd4b9ec562d2.tar change-5c7c4edfe45be83211d5c6217824dd4b9ec562d2.tar.gz change-5c7c4edfe45be83211d5c6217824dd4b9ec562d2.tar.bz2 change-5c7c4edfe45be83211d5c6217824dd4b9ec562d2.tar.lz change-5c7c4edfe45be83211d5c6217824dd4b9ec562d2.tar.xz change-5c7c4edfe45be83211d5c6217824dd4b9ec562d2.tar.zst change-5c7c4edfe45be83211d5c6217824dd4b9ec562d2.zip |
Implement basic `read` and `write` interposition
The goal is to implement a shared library to be loaded using `LD_PRELOAD` that tracks all file changes performed by a process.
This information is required to enable general purpose change logging inside a shell session. i.e. I want to be able to display diffs and before- / after-hashes of all changes performed by every single command without requiring every changed file to be known prior to execution.
-rw-r--r-- | change_log.cc | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/change_log.cc b/change_log.cc new file mode 100644 index 0000000..d4c3dc4 --- /dev/null +++ b/change_log.cc @@ -0,0 +1,76 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <unistd.h> +#include <dlfcn.h> +#include <sys/stat.h> + +#include <cstring> +#include <iostream> +#include <functional> + +template <class Result, typename... Arguments> +std::function<Result(Arguments...)> get_real_function( + const std::string& symbol_name) { + + Result (*real_function)(Arguments...) = nullptr; + const void* symbol_address = dlsym(RTLD_NEXT, symbol_name.c_str()); + + std::memcpy(&real_function, &symbol_address, sizeof(symbol_address)); + + return std::function<Result(Arguments...)>(real_function); +} + +std::string get_file_name(int fd) { + char proc_link[20]; + char file_name[256]; + + snprintf(proc_link, sizeof(proc_link), "/proc/self/fd/%d", fd); + const ssize_t name_size = readlink(proc_link, file_name, sizeof(file_name)); + + if ( name_size > 0 ) { + file_name[name_size] = '\0'; + + return std::string(file_name); + } else { + return std::string(); + } +} + +bool is_regular_file(int fd) { + struct stat fd_stat; + fstat(fd, &fd_stat); + + return S_ISREG(fd_stat.st_mode); +} + +ssize_t read(int fd, void *buffer, size_t count) { + static auto real_read = get_real_function<ssize_t, int, void*, size_t>("read"); + + const ssize_t result = real_read(fd, buffer, count); + + if ( is_regular_file(fd) ) { + std::cerr << "read size " + << count + << " of " + << get_file_name(fd) + << std::endl; + } + + return result; +} + +ssize_t write(int fd, const void* buffer, size_t count) { + static auto real_write = get_real_function<ssize_t, int, const void*, size_t>("write"); + + if ( is_regular_file(fd) ) { + std::cerr << "write size " + << count + << " to " + << get_file_name(fd) + << std::endl; + } + + return real_write(fd, buffer, count); +} |