Skip to content

Instantly share code, notes, and snippets.

@sdeming
Created December 14, 2011 19:29
Show Gist options
  • Save sdeming/1478087 to your computer and use it in GitHub Desktop.
Save sdeming/1478087 to your computer and use it in GitHub Desktop.
Old .so to redirect .svn directories to another place, keeping your local directory tree clean. From years ago at site5. AKA the Load Bearing Kludge. Build then use the .so in your LD_PRELOAD before executing svn.
#define _GNU_SOURCE
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <features.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static char *get_prefix()
{
static char *prefix = NULL;
/* Define prefix if it hasn't already been defined. */
if (prefix == NULL) {
prefix = getenv("DOT_SVN_PREFIX");
if (!prefix) {
prefix = "/backup/svn_home";
}
}
return prefix;
}
/**
* test 's' to see if it's a .svn candidate.
*/
#define IF_DOT_SVN(s) \
if ((0 == strncmp(s, ".svn", 4) || \
0 != strstr(s, "/.svn")) && \
0 != strncmp(s, get_prefix(), strlen(get_prefix())))
/*
* NOTE: ripped from bash loadables.
* Make all the directories leading up to PATH, then create PATH. Note that
* this changes the process's umask; make sure that all paths leading to a
* return reset it to ORIGINAL_UMASK
*/
static int make_path (char *path, int nmode, int parent_mode)
{
int oumask;
struct stat sb;
char *p, *npath;
int ret = 0;
if (stat (path, &sb) == 0) {
return EEXIST;
}
oumask = umask (0);
npath = strdup (path); /* So we can write to it. */
/* Check whether or not we need to do anything with intermediate dirs. */
/* Skip leading slashes. */
p = npath;
while (*p == '/') {
p++;
}
while (p = strchr (p, '/')) {
*p = '\0';
if (stat (npath, &sb) != 0) {
if ((ret = mkdir (npath, parent_mode)) != 0) {
perror("(make_path part)");
umask (oumask);
free (npath);
return ret;
}
}
else if (S_ISDIR (sb.st_mode) == 0) {
perror("(make_path part not dir)");
umask (oumask);
free (npath);
return ENOTDIR;
}
*p++ = '/'; /* restore slash */
while (*p == '/') {
p++;
}
}
/* Create the final directory component. */
if (stat (npath, &sb) && (ret = mkdir (npath, nmode) != 0)) {
perror("(make_path)");
umask (oumask);
free (npath);
return ret;
}
umask (oumask);
free (npath);
return 0;
}
/**
* PLEASE PLEASE PLEASE
* ---
* Remember to free the strings this generates.
*/
static char *adjusted_filename(const char *filename)
{
/* Deal with relative paths. */
char *cd = NULL;
if (filename[0] != '/') {
cd = get_current_dir_name();
}
/* Calculate the buffer size necessary for the new filename. */
int l = 16 +
strlen(filename) +
strlen(get_prefix()) +
((cd != NULL) ? strlen(cd) : 0);
/* Build the new filename. */
char *new_name = malloc(l);
if (cd) {
snprintf(new_name, l, "%s%s/%s", get_prefix(), cd, filename);
}
else {
snprintf(new_name, l, "%s%s", get_prefix(), filename);
}
/* Safe to free NULL, so don't waste cycles checking. */
free(cd);
/* Return the new filename */
return new_name;
}
/**
* intercept open64.
*/
int open64(const char *pathname, int flags, ...)
{
mode_t mode;
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "open64");
}
if (flags & O_CREAT) {
va_list arg;
va_start(arg, flags);
mode = va_arg(arg, mode_t);
va_end(arg);
}
IF_DOT_SVN(pathname) {
char *new_name = adjusted_filename(pathname);
int ret = orig_func(new_name, flags, mode);
free(new_name);
return ret;
}
return orig_func(pathname, flags, mode);
}
/**
* intercept open.
*/
int open(const char *pathname, int flags, ...)
{
mode_t mode;
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "open");
}
if (flags & O_CREAT) {
va_list arg;
va_start(arg, flags);
mode = va_arg(arg, mode_t);
va_end(arg);
}
IF_DOT_SVN(pathname) {
char *new_name = adjusted_filename(pathname);
int ret = orig_func(new_name, flags, mode);
free(new_name);
return ret;
}
return orig_func(pathname, flags, mode);
}
/*
* intercept fopen
*/
FILE *fopen(const char *path, const char *mode)
{
static FILE *(*orig_func)();
if (!orig_func) {
orig_func = (FILE*(*)()) dlsym(RTLD_NEXT, "fopen");
}
IF_DOT_SVN(path) {
char *new_name = adjusted_filename(path);
FILE *ret = orig_func(new_name, mode);
free(new_name);
return ret;
}
return orig_func(path, mode);
}
/*
* intercept freopen
*/
FILE *freopen(const char *path, const char *mode, FILE *stream)
{
static FILE* (*orig_func)();
if (!orig_func) {
orig_func = (FILE*(*)()) dlsym(RTLD_NEXT, "freopen");
}
IF_DOT_SVN(path) {
char *new_name = adjusted_filename(path);
FILE *ret = orig_func(new_name, mode, stream);
free(new_name);
return ret;
}
return orig_func(path, mode, stream);
}
/*
* intercept fopen64
*/
FILE *fopen64(const char *path, const char *mode)
{
static FILE* (*orig_func)();
if (!orig_func) {
orig_func = (FILE*(*)()) dlsym(RTLD_NEXT, "fopen64");
}
IF_DOT_SVN(path) {
char *new_name = adjusted_filename(path);
FILE *ret = orig_func(new_name, mode);
free(new_name);
return ret;
}
return orig_func(path, mode);
}
/*
* intercept freopen64
*/
FILE *freopen64(const char *path, const char *mode, FILE *stream)
{
static FILE* (*orig_func)();
if (!orig_func) {
orig_func = (FILE*(*)()) dlsym(RTLD_NEXT, "freopen64");
}
IF_DOT_SVN(path) {
char *new_name = adjusted_filename(path);
FILE *ret = orig_func(new_name, mode, stream);
free(new_name);
return ret;
}
return orig_func(path, mode, stream);
}
/**
* intercept stat64.
*/
int stat64(const char *file_name, struct stat64 *buf)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "stat64");
}
IF_DOT_SVN(file_name) {
char *new_name = adjusted_filename(file_name);
int ret = orig_func(new_name, buf);
free(new_name);
return ret;
}
return orig_func(file_name, buf);
}
/**
* intercept lstat64.
*/
int lstat64(const char *file_name, struct stat64 *buf)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "lstat64");
}
IF_DOT_SVN(file_name) {
char *new_name = adjusted_filename(file_name);
int ret = orig_func(new_name, buf);
free(new_name);
return ret;
}
return orig_func(file_name, buf);
}
/**
* POSIX -- glibc inlines the ?stat functions as __?xstat, these
* are then called by the real ?stat functions.
*/
/*
* intercept __xstat.
*/
int __xstat(int __ver, __const char *__filename, struct stat *__stat_buf)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "__xstat");
}
IF_DOT_SVN(__filename) {
char *new_name = adjusted_filename(__filename);
int ret = orig_func(__ver, new_name, __stat_buf);
free(new_name);
return ret;
}
return orig_func(__ver, __filename, __stat_buf);
}
/*
* intercept __lxstat.
*/
int __lxstat(int __ver, __const char *__filename, struct stat *__stat_buf)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "__lxstat");
}
IF_DOT_SVN(__filename) {
char *new_name = adjusted_filename(__filename);
int ret = orig_func(__ver, new_name, __stat_buf);
free(new_name);
return ret;
}
return orig_func(__ver, __filename, __stat_buf);
}
/*
* intercept __fxstat64.
*/
int __xstat64(int __ver, __const char *__filename, struct stat64 *__stat_buf)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "__xstat64");
}
IF_DOT_SVN(__filename) {
char *new_name = adjusted_filename(__filename);
int ret = orig_func(__ver, new_name, __stat_buf);
free(new_name);
return ret;
}
return orig_func(__ver, __filename, __stat_buf);
}
/*
* intercept __lxstat64.
*/
int __lxstat64(int __ver, __const char *__filename, struct stat64 *__stat_buf)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "__lxstat64");
}
IF_DOT_SVN(__filename) {
char *new_name = adjusted_filename(__filename);
int ret = orig_func(__ver, new_name, __stat_buf);
free(new_name);
return ret;
}
return orig_func(__ver, __filename, __stat_buf);
}
/*
* intercept chmod.
*/
int chmod(const char *file, mode_t mode)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "chmod");
}
IF_DOT_SVN(file) {
char *new_name = adjusted_filename(file);
int ret = orig_func(new_name, mode);
free(new_name);
return ret;
}
return orig_func(file, mode);
}
/*
* intercept rename.
*/
int rename(const char *oldpath, const char *newpath)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "rename");
}
char *new_oldpath = NULL;
char *new_newpath = NULL;
IF_DOT_SVN(oldpath) {
new_oldpath = adjusted_filename(oldpath);
}
IF_DOT_SVN(newpath) {
new_newpath = adjusted_filename(newpath);
}
if (new_oldpath || new_newpath) {
int ret = orig_func(new_oldpath ? new_oldpath : oldpath,
new_newpath ? new_newpath : newpath);
free(new_oldpath);
free(new_newpath);
return ret;
}
return orig_func(oldpath, newpath);
}
/*
* intercept mkdir.
*/
int mkdir(const char *pathname, mode_t mode)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "mkdir");
}
IF_DOT_SVN(pathname) {
char *new_name = adjusted_filename(pathname);
/*
* This is perhaps the trickiest part of this whole
* mess. We have to walk each directory part and make
* sure it exists, or if not make it. Since the
* .svn directories live in existing directory trees
* we have to mirror it exactly underneat our special
* tree.
*/
int ret = make_path(new_name, mode, mode);
//int ret = orig_func(new_name, mode);
free(new_name);
return ret;
}
return orig_func(pathname, mode);
}
/*
* intercept rmdir.
*/
int rmdir(const char *pathname)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "rmdir");
}
IF_DOT_SVN(pathname) {
char *new_name = adjusted_filename(pathname);
int ret = orig_func(new_name);
free(new_name);
return ret;
}
return orig_func(pathname);
}
/*
* intercept creat.
*/
int creat(const char *pathname, mode_t mode)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "creat");
}
IF_DOT_SVN(pathname) {
char *new_name = adjusted_filename(pathname);
int ret = orig_func(new_name, mode);
free(new_name);
return ret;
}
return orig_func(pathname, mode);
}
int unlink(const char *pathname)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "unlink");
}
IF_DOT_SVN(pathname) {
char *new_name = adjusted_filename(pathname);
int ret = orig_func(new_name);
free(new_name);
return ret;
}
return orig_func(pathname);
}
int remove(const char *pathname)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "remove");
}
IF_DOT_SVN(pathname) {
char *new_name = adjusted_filename(pathname);
int ret = orig_func(new_name);
free(new_name);
return ret;
}
return orig_func(pathname);
}
/*
* intercept link.
*/
int link(const char *oldpath, const char *newpath)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "link");
}
char *new_oldpath = NULL;
char *new_newpath = NULL;
IF_DOT_SVN(oldpath) {
new_oldpath = adjusted_filename(oldpath);
}
IF_DOT_SVN(newpath) {
new_newpath = adjusted_filename(newpath);
}
if (new_oldpath || new_newpath) {
int ret = orig_func(new_oldpath ? new_oldpath : oldpath,
new_newpath ? new_newpath : newpath);
free(new_oldpath);
free(new_newpath);
return ret;
}
return orig_func(oldpath, newpath);
}
/*
* intercept symlink.
*/
int symlink(const char *oldpath, const char *newpath)
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "symlink");
}
char *new_oldpath = NULL;
char *new_newpath = NULL;
IF_DOT_SVN(oldpath) {
new_oldpath = adjusted_filename(oldpath);
}
IF_DOT_SVN(newpath) {
new_newpath = adjusted_filename(newpath);
}
if (new_oldpath || new_newpath) {
int ret = orig_func(new_oldpath ? new_oldpath : oldpath,
new_newpath ? new_newpath : newpath);
free(new_oldpath);
free(new_newpath);
return ret;
}
return orig_func(oldpath, newpath);
}
/*
* intercept opendir.
*/
DIR *opendir(__const char *__name)
{
static DIR* (*orig_func)();
if (!orig_func) {
orig_func = (DIR*(*)()) dlsym(RTLD_NEXT, "opendir");
}
IF_DOT_SVN(__name) {
char *new_name = adjusted_filename(__name);
DIR *ret = orig_func(new_name);
free(new_name);
return ret;
}
return orig_func(__name);
}
/*
* intercept scandir.
*/
int scandir(__const char *__restrict __dir,
struct dirent ***__restrict __namelist,
int(*__selector)(__const struct dirent *),
int(*__cmp)(__const void *, __const void *))
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "scandir");
}
IF_DOT_SVN(__dir) {
char *new_name = adjusted_filename(__dir);
int ret = orig_func(new_name, __namelist, __selector, __cmp);
free(new_name);
return ret;
}
return orig_func(__dir, __namelist, __selector, __cmp);
}
/*
* intercept scandir64.
*/
int scandir64(__const char *__restrict __dir,
struct dirent64 ***__restrict __namelist,
int(*__selector)(__const struct dirent64 *),
int(*__cmp)(__const void *, __const void *))
{
static int (*orig_func)();
if (!orig_func) {
orig_func = (int(*)()) dlsym(RTLD_NEXT, "scandir64");
}
IF_DOT_SVN(__dir) {
char *new_name = adjusted_filename(__dir);
int ret = orig_func(new_name, __namelist, __selector, __cmp);
free(new_name);
return ret;
}
return orig_func(__dir, __namelist, __selector, __cmp);
}
/**
* TODO:
* ==========================================================================
* mknod -- not likely needed
* mkfifo -- not likely needed
*/
#
# The simplest of makefiles.
#
# Perhapse after some refactoring this will be more complex, but for now
# it is sweet and simple.
#
# SD 2005-10-23
#
CC=gcc
CFLAGS=-shared
fix_dotsvn.so: fix_dotsvn.c
$(CC) $(CFLAGS) fix_dotsvn.c -o fix_dotsvn.so -ldl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment