Commit 2e46a921 authored by Nils Goroll's avatar Nils Goroll

Add an interposer for open() and its variants to remove the O_DIRECT flag

This does not actualy fit the project name "liblongpath", but most of the
surrounding code is identical. So for the time being I will tolerate
the fact that liblongpath contains a little side use, but should we want
to integrate more functionality, we should probably give the project a
more generic name.

Use of the interposer by environment variables

LD_PRELOAD=<path_to_lib>

* standard behaviour: map O_DIRECT to O_DSYNC for open() failing
  with EINVAL
* set CONVODIRECT=none to just ignore O_DIRECT
parent e370f9f5
......@@ -28,7 +28,11 @@ DISTCHECK_CONFIGURE_FLAGS = \
AM_LDFLAGS = $(AM_LT_LDFLAGS)
lib_LTLIBRARIES = liblongpath.la liblongpath_unsafe.la liblongpath_interpose.la
lib_LTLIBRARIES = \
liblongpath.la \
liblongpath_unsafe.la \
liblongpath_interpose.la \
libo_direct_interpose.la
liblongpath_includes = \
include/longpath/longpath.h \
......@@ -46,6 +50,10 @@ liblongpath_la_SOURCES = \
$(liblongpath_includes) \
longpath.c
libo_direct_la_SOURCES = \
$(liblongpath_includes) \
o_direct.c
## liblongpath_unsafe
liblongpath_unsafe_la_CFLAGS = $(AM_CFLAGS) -DUSE_CHDIR_FALLBACK
......@@ -65,6 +73,17 @@ liblongpath_interpose_la_LDFLAGS = $(AM_LDFLAGS) \
liblongpath_interpose_la_SOURCES = $(liblongpath_la_SOURCES)
## o_direct_interpose
libo_direct_interpose_la_CFLAGS = $(AM_CFLAGS) -DINTERPOSE \
-DOWN_PATH=\"$(libdir)/libo_direct_interpose.so\"
libo_direct_interpose_la_LDFLAGS = $(AM_LDFLAGS) \
-export-dynamic -avoid-version -shared -ldl
libo_direct_interpose_la_SOURCES = $(libo_direct_la_SOURCES)
## TEST - XXX make a proper test directory etc
bin_PROGRAMS = longpath_test longpath_unsafe_test
......
#if defined(INTERPOSE) && defined(TRACE)
FUNCTION(void *, dlopen, const char *, int mode)
#endif
/*
* table file with functions for which a *64 variant can or cannot exist,
* depending upon the compilation environment (see lf64 etc)
*
* using seperate FUNCTION and FUNCTION64 macros because the 64 variants can
* have a different signature (read: stat64)
*/
// flags 2nd
FUNCTION(int, open, const char *, int, ...)
FUNCTION64(int, open, const char *, int, ...)
/* 64 at funcs */
#if defined(__EXTENSIONS__) || !defined(__XOPEN_OR_POSIX) || \
defined(_ATFILE_SOURCE)
// flags 3rd
FUNCTION(int, openat, int, const char *, int, ...)
FUNCTION64(int, openat, int, const char *, int, ...)
// flags 3rd
# ifdef HAVE_ATTROPEN
FUNCTION(int, attropen, const char *, const char *, int, ...)
FUNCTION64(int, attropen, const char *, const char *, int, ...)
# endif
#endif /* __EXTENSIONS ... */
/* 64 mkstemp GNU*/
#if defined(_GNU_SOURCE)
#if defined(HAVE_MKOSTEMP)
// flags 2nd
FUNCTION64(int, mkostemp, char *, int)
FUNCTION(int, mkostemp, char *, int)
#endif
#if defined(HAVE_MKOSTEMPS)
// flags 3nd
FUNCTION(int, mkostemps, char *, int, int)
FUNCTION64(int, mkostemps, char *, int, int)
#endif
#endif /* GNU_SOURCE */
/*-
* wrapper library for replacing O_DIRECT with O_SYNC if necessary
*
* Copyright (c) 2015 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Nils Goroll <nils.goroll@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "../config.h"
#define _ATFILE_SOURCE
#if defined(_LP64)
#define _FILE_OFFSET_BITS 64
#else
#define _FILE_OFFSET_BITS 32
#endif
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#ifdef HAVE_ASSERT_H
#include <assert.h>
#else
#define assert(EX) ((void)0)
#endif
/*
* Thanks to Jonathan Wakely for these macros
* http://gcc.gnu.org/ml/gcc-help/2011-01/msg00121.html
*
*/
#if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 405
# define GCC_DIAG_DO_PRAGMA(x) _Pragma (#x)
# define GCC_DIAG_PRAGMA(x) GCC_DIAG_DO_PRAGMA(GCC diagnostic x)
# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
# define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(push) \
GCC_DIAG_PRAGMA(ignored x)
# define GCC_DIAG_ON(x) GCC_DIAG_PRAGMA(pop)
# else
# define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(ignored x)
# define GCC_DIAG_ON(x) GCC_DIAG_PRAGMA(warning x)
# endif
#else
# define GCC_DIAG_OFF(x)
# define GCC_DIAG_ON(x)
#endif
/*
* work around solaris implementing stat() and lstat() in sys/stat_impl.h
* on the basis of _xstat()
*/
#ifdef INTERPOSE
# include <longpath/longpath_common.h>
# define real(x) real_ ## x
# if defined(_LP64)
# define real64(x) real_ ## x
# else
# define real64(x) real_ ## x ## 64
# endif
# define our(x) x
# if defined(_LP64)
# define our64(x) x
# else
# define our64(x) x ## 64
# endif
/*
* declaration of real() function pointers
*/
# define INTERPOSE_REAL
# define FUNCTION(type, name, ...) static type (* real(name))(__VA_ARGS__);
# if ! defined(_LP64)
# define FUNCTION64(type, name, ...) static type (* real64(name))(__VA_ARGS__);
# else
# define FUNCTION64(type, name, ...)
# endif /* _LP64 */
# include <longpath/longpath_tbl_functions_flags.h>
# include <longpath/longpath_tbl_functions_flags64.h>
# undef FUNCTION
# undef FUNCTION64
# undef INTERPOSE_REAL
# include <dlfcn.h>
# include <stdio.h>
# define init_dlsym(fptr, name) do { \
if (! (fptr = dlsym(RTLD_NEXT, #name))) { \
perror("looking up symbol " #name " failed"); \
exit(1); \
} \
} while(0)
#ifdef TRACE
static int tracefd;
static pid_t pid;
//static void *my_dl_handle;
#include <unistd.h>
#endif
/*
* init real() function ponters
*/
void __attribute__ ((constructor)) o_direct_init(void);
GCC_DIAG_OFF("-pedantic")
// for getenv
#include <stdlib.h>
#include <string.h>
static int addflag = O_DSYNC;
void __attribute__ ((constructor))
o_direct_init(void) {
char *e;
# define FUNCTION(type, name, ...) init_dlsym(real(name), name);
# if ! defined(_LP64)
# define FUNCTION64(type, name, ...) init_dlsym(real64(name), name ## 64);
# else
# define FUNCTION64(type, name, ...)
# endif /* _LP64 */
# include <longpath/longpath_tbl_functions_flags.h>
# include <longpath/longpath_tbl_functions_flags64.h>
# undef FUNCTION
# undef FUNCTION64
#ifdef TRACE
tracefd = real(open)("/tmp/lptrace", O_WRONLY|O_CREAT|O_APPEND, 0600);
if (tracefd == -1) {
perror("trace file ");
exit(1);
}
pid = getpid();
#endif /* TRACE */
e = getenv("CONVODIRECT");
if (e && strcmp(e, "none") == 0) {
addflag = 0;
}
}
GCC_DIAG_ON("-pedantic")
#ifdef TRACE
#define TRACELEN 1024
#define trace(fmt, ...) do { \
char _buf[TRACELEN]; \
int _l = snprintf(_buf, TRACELEN, "%d: " fmt, pid, ##__VA_ARGS__); \
if ((_l > 0) && (_l < TRACELEN)) \
(void)write(tracefd, _buf, _l); \
} while(0)
#include <stdio.h>
#include <string.h>
void *
our(dlopen)(const char *pathname, int mode) {
trace("dlopen(%s, 0x%x)\n", pathname ? pathname : "(null)", mode);
if (pathname && strstr(pathname, "libc.so")) {
// const int nmode = RTLD_GROUP|RTLD_FIRST;
trace("returning dlopen(%s, 0x%x)\n", OWN_PATH, mode);
return real(dlopen)(OWN_PATH, mode);
}
if (mode & RTLD_GLOBAL) {
const int nmode = RTLD_NOW|RTLD_GLOBAL;
void *r;
trace("actual dlopen(%s, 0x%x)\n", OWN_PATH, mode|RTLD_PARENT);
r = real(dlopen)(pathname, mode|RTLD_PARENT);
trace("doing additional dlopen(%s, 0x%x)\n", OWN_PATH, nmode);
(void) real(dlopen)(OWN_PATH, nmode);
return r;
}
return real(dlopen)(pathname, mode);
}
#endif /* TRACE */
#else /* ! INTERPOSE */
# include <longpath/longpath.h>
# define real(x) x
# if defined(_LP64)
# define real64(x) x
# else
# define real64(x) x ## 64
# endif
# define our(x) x ## w
# if defined(_LP64)
# define our64(x) x ## w
# else
# define our64(x) x ## w ## 64
# endif
#endif /* INTERPOSE */
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif
// varargs for open
#if defined(HAVE_SYS_VARARGS_H)
# include <sys/varargs.h>
#elif defined(HAVE_STDARG_H)
# include <stdarg.h>
#endif
#define nood(flags) ((flags) & (int)~O_DIRECT | addflag )
// flags 2nd argument
#define Xod2va(funcn, a1, flags, ...) \
int __ret = funcn(a1, flags, ##__VA_ARGS__); \
\
if (__ret == -1 && errno == EINVAL && \
(flags) & O_DIRECT) { \
return funcn(a1, nood(flags), \
##__VA_ARGS__); \
} \
return __ret
#define Xod2(funcn, a1, flags) \
int __ret = funcn(a1, flags); \
\
if (__ret == -1 && errno == EINVAL && \
(flags) & O_DIRECT) { \
return funcn(a1, nood(flags)); \
} \
return __ret
// flags 3rd argument
#define Xod3va(funcn, a1, a2, flags, ...) \
int __ret = funcn(a1, a2, flags, ##__VA_ARGS__); \
\
if (__ret == -1 && errno == EINVAL && \
(flags) & O_DIRECT) { \
return funcn(a1, a2, nood(flags), \
##__VA_ARGS__); \
} \
return __ret
#define Xod3(funcn, a1, a2, flags) \
int __ret = funcn(a1, a2, flags); \
\
if (__ret == -1 && errno == EINVAL && \
(flags) & O_DIRECT) { \
return funcn(a1, a2, nood(flags)); \
} \
return __ret
/* OPEN */
static int
_openat(int filedes, const char *path, int oflag, mode_t mode) {
Xod3va(real(openat), filedes, path, oflag, mode);
}
#if !defined(_LP64)
static int
_openat64(int filedes, const char *path, int oflag, mode_t mode) {
Xod3va(real64(openat), filedes, path, oflag, mode);
}
#endif
#define Openatl(bits) \
mode_t mode = 0; \
if (oflag & O_CREAT) { \
va_list arg; \
va_start(arg, oflag); \
mode = va_arg(arg, mode_t); \
va_end(arg); \
} \
\
return _openat ## bits (filedes, path, oflag, mode)
int
our(openat)(int filedes, const char *path, int oflag, ...) {
Openatl(/*32*/);
}
#if !defined(_LP64)
int
our64(openat)(int filedes, const char *path, int oflag, ...) {
Openatl(64);
}
#endif
#define Openl(bits) \
mode_t mode = 0; \
if (oflag & O_CREAT) { \
va_list arg; \
va_start(arg, oflag); \
mode = va_arg(arg, mode_t); \
va_end(arg); \
} \
\
return _openat ## bits (AT_FDCWD, path, oflag, mode)
int
our(open)(const char *path, int oflag, ...) {
Openl(/*32*/);
}
#if !defined(_LP64)
int
our64(open)(const char *path, int oflag, ...) {
Openl(64);
}
#endif
/* ATTROPEN */
#ifdef HAVE_ATTROPEN
#define Attropenl(bits) \
int fd, afd; \
va_list ap; \
\
fd = our64(open) (path, O_RDONLY|O_NONBLOCK); \
if (fd == -1) \
return -1; \
\
va_start(ap, oflag); \
afd = _openat ## bits (fd, apath, oflag|O_XATTR, \
va_arg(ap, mode_t)); \
va_end(ap); \
\
close_ifd(fd); \
return afd
int
our(attropen)(const char *path, const char *apath, int oflag, ...) {
Attropenl(/*32*/);
}
#if !defined(_LP64)
int
our64(attropen)(const char *path, const char *apath, int oflag, ...) {
Attropenl(64);
}
#endif
#endif /* HAVE_ATTROPEN */
/* mkstemp GNU */
#if defined(_GNU_SOURCE)
#if defined(HAVE_MKOSTEMP)
int
our(mkostemp)(char *template, int flags) {
Xod2(real(mkostemp), template, flags);
}
#endif
#if defined(HAVE_MKOSTEMPS)
int
our(mkostemps)(char *template, int slen, int flags) {
Xod3(real(mkostemps), template, slen, flags);
}
#endif
#if !defined(_LP64)
#if defined(HAVE_MKOSTEMP)
int
our64(mkostemp)(char *template, int flags) {
Xod2(real64(mkostemp), template, flags);
}
#endif
#if defined(HAVE_MKOSTEMPS)
int
our64(mkostemps)(char *template, int slen, int flags) {
Xod3(real64(mkostemps), template, slen, flags);
}
#endif /* HAVE_MKOSTEMPS */
#endif /* ! _LP64 */
#endif /* _GNU_SOURCE */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment