2
* Copyright (C) 2008-2012 Stewart Smith <stewart@flamingspork.com>
3
* This program is free software: you can redistribute it and/or modify it
4
* under the terms of the GNU General Public License version 3, as published
5
* by the Free Software Foundation.
7
* This program is distributed in the hope that it will be useful, but
8
* WITHOUT ANY WARRANTY; without even the implied warranties of
9
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
10
* PURPOSE. See the GNU General Public License for more details.
12
* You should have received a copy of the GNU General Public License along
13
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
#include "libeatmydata/visibility.h"
19
#undef _FILE_OFFSET_BITS // Hack to get open and open64 on 32bit
20
#undef __USE_FILE_OFFSET64
21
#include <sys/types.h>
31
#define CHECK_FILE "/tmp/eatmydata"
35
* Mac OS X 10.7 doesn't declare fdatasync().
37
#if defined HAVE_DECL_FDATASYNC && !HAVE_DECL_FDATASYNC
38
int fdatasync(int fd);
41
typedef int (*libc_open_t)(const char*, int, ...);
42
typedef int (*libc_open64_t)(const char*, int, ...);
43
typedef int (*libc_fsync_t)(int);
44
typedef int (*libc_sync_t)(void);
45
typedef int (*libc_fdatasync_t)(int);
46
typedef int (*libc_msync_t)(void*, size_t, int);
47
#ifdef HAVE_SYNC_FILE_RANGE
48
typedef int (*libc_sync_file_range_t)(int, off64_t, off64_t, unsigned int);
51
static libc_open_t libc_open= NULL;
52
static libc_open64_t libc_open64= NULL;
53
static libc_fsync_t libc_fsync= NULL;
54
static libc_sync_t libc_sync= NULL;
55
static libc_fdatasync_t libc_fdatasync= NULL;
56
static libc_msync_t libc_msync= NULL;
57
#ifdef HAVE_SYNC_FILE_RANGE
58
static libc_sync_file_range_t libc_sync_file_range= NULL;
61
#define ASSIGN_DLSYM_OR_DIE(name) \
62
libc_##name = (libc_##name##_##t)(intptr_t)dlsym(RTLD_NEXT, #name); \
63
if (!libc_##name || dlerror()) \
66
#define ASSIGN_DLSYM_IF_EXIST(name) \
67
libc_##name = (libc_##name##_##t)(intptr_t)dlsym(RTLD_NEXT, #name); \
71
int LIBEATMYDATA_API msync(void *addr, size_t length, int flags);
73
void __attribute__ ((constructor)) eatmydata_init(void);
75
void __attribute__ ((constructor)) eatmydata_init(void)
77
ASSIGN_DLSYM_OR_DIE(open);
78
ASSIGN_DLSYM_OR_DIE(open64);
79
ASSIGN_DLSYM_OR_DIE(fsync);
80
ASSIGN_DLSYM_OR_DIE(sync);
81
ASSIGN_DLSYM_OR_DIE(fdatasync);
82
ASSIGN_DLSYM_OR_DIE(msync);
83
#ifdef HAVE_SYNC_FILE_RANGE
84
ASSIGN_DLSYM_IF_EXIST(sync_file_range);
88
static int eatmydata_is_hungry(void)
90
/* Init here, as it is called before any libc functions */
95
static struct stat buf;
96
int old_errno, stat_ret;
99
stat_ret= stat(CHECK_FILE, &buf);
102
/* Treat any error as if file doesn't exist, for safety */
110
int LIBEATMYDATA_API fsync(int fd)
112
if (eatmydata_is_hungry()) {
113
pthread_testcancel();
118
return (*libc_fsync)(fd);
121
/* no errors are defined for this function */
122
void LIBEATMYDATA_API sync(void)
124
if (eatmydata_is_hungry()) {
125
pthread_testcancel();
132
int LIBEATMYDATA_API open(const char* pathname, int flags, ...)
138
#if SIZEOF_MODE_T < SIZEOF_INT
139
mode= (mode_t) va_arg(ap, int);
141
mode= va_arg(ap, mode_t);
145
/* In pthread environments the dlsym() may call our open(). */
146
/* We simply ignore it because libc is already loaded */
152
if (eatmydata_is_hungry())
153
flags &= ~(O_SYNC|O_DSYNC);
155
return (*libc_open)(pathname,flags,mode);
158
#ifndef __USE_FILE_OFFSET64
159
int LIBEATMYDATA_API open64(const char* pathname, int flags, ...)
165
#if SIZEOF_MODE_T < SIZEOF_INT
166
mode= (mode_t) va_arg(ap, int);
168
mode= va_arg(ap, mode_t);
172
/* In pthread environments the dlsym() may call our open(). */
173
/* We simply ignore it because libc is already loaded */
179
if (eatmydata_is_hungry())
180
flags &= ~(O_SYNC|O_DSYNC);
182
return (*libc_open64)(pathname,flags,mode);
186
int LIBEATMYDATA_API fdatasync(int fd)
188
if (eatmydata_is_hungry()) {
189
pthread_testcancel();
194
return (*libc_fdatasync)(fd);
197
int LIBEATMYDATA_API msync(void *addr, size_t length, int flags)
199
if (eatmydata_is_hungry()) {
200
pthread_testcancel();
205
return (*libc_msync)(addr, length, flags);
208
#ifdef HAVE_SYNC_FILE_RANGE
209
int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags)
211
if (eatmydata_is_hungry()) {
212
pthread_testcancel();
217
return (libc_sync_file_range)(fd, offset, nbytes, flags);