~mapreri/libeatmydata/1556410

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* 
#define CHECK_FILE "/tmp/eatmydata"
*/

#ifndef RTLD_NEXT
#  define _GNU_SOURCE
#endif

#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <stdarg.h>

int errno;

static int (*libc_open)(const char*, int, ...)= NULL;
static int (*libc_fsync)(int)= NULL;
static int (*libc_sync)()= NULL;
static int (*libc_fdatasync)(int)= NULL;
static int (*libc_msync)(void*, size_t, int)= NULL;

#define ASSIGN_DLSYM_OR_DIE(name)			\
        libc_##name = dlsym(RTLD_NEXT, #name);			\
        if (!libc_##name || dlerror())				\
                _exit(1);

void __attribute__ ((constructor)) eatmydata_init(void)
{
	ASSIGN_DLSYM_OR_DIE(open);
	ASSIGN_DLSYM_OR_DIE(fsync);
	ASSIGN_DLSYM_OR_DIE(sync);
	ASSIGN_DLSYM_OR_DIE(fdatasync);
	ASSIGN_DLSYM_OR_DIE(msync);
}

int eatmydata_is_hungry(void)
{
	/* Init here, as it is called before any libc functions */
	if(!libc_open)
		eatmydata_init();

#ifdef CHECK_FILE
	static struct stat buf;
	int old_errno, stat_ret;

	old_errno= errno;
	stat_ret= stat(CHECK_FILE, &buf);
	errno= old_errno;

	/* Treat any error as if file doesn't exist, for safety */
	return !stat_ret;
#else
	/* Always hungry! */
	return 1;
#endif
}

int fsync(int fd)
{
	if (eatmydata_is_hungry()) {
		errno= 0;
		return 0;
	}

	return (*libc_fsync)(fd);
}

/* no errors are defined for this function */
void sync(void)
{
	if (eatmydata_is_hungry())
		return;

	(*libc_sync)();
}

int open(const char* pathname, int flags, ...)
{
	va_list ap;
	mode_t mode;

	va_start(ap, flags);
	mode= va_arg(ap, mode_t);
	va_end(ap);

	/* In pthread environments the dlsym() may call our open(). */
	/* We simply ignore it because libc is already loaded       */
	if (!libc_open) {
		errno = EFAULT;
		return -1;
	}

	if (eatmydata_is_hungry())
		flags &= ~(O_SYNC|O_DSYNC);

	return (*libc_open)(pathname,flags,mode);
}

int fdatasync(int fd)
{
	if (eatmydata_is_hungry()) {
		errno= 0;
		return 0;
	}

	return (*libc_fdatasync)(fd);
}

int msync(void *addr, size_t length, int flags)
{
	if (eatmydata_is_hungry()) {
		errno= 0;
		return 0;
	}

	return (*libc_msync)(addr, length, flags);
}