-
Notifications
You must be signed in to change notification settings - Fork 1
/
posix.c
156 lines (144 loc) · 4.16 KB
/
posix.c
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#define _GNU_SOURCE 1
#include <assert.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "debug.h"
#include "fproc.h"
DECLARE_CHANNEL(posix);
typedef int (openfqn)(const char*, int, ...);
typedef ssize_t (writefqn)(int, const void*, size_t);
typedef int (closefqn)(int);
static openfqn* openf = NULL;
static writefqn* writef = NULL;
static closefqn* closef = NULL;
/* When the file is closed, we need the filename so we can pass it to the vis
* code. But we're only given the filename on open, not close. This table
* maps FILE*s to filenames, which we populate on open.
* \note This is *not* currently thread-safe. */
struct openposixfile {
char* name;
int fd;
};
#define MAX_FILES 1024
static struct openposixfile posix_files[MAX_FILES] = {{NULL, 0}};
/* predicate for finding a posix_file. 2nd argument should be a pointer to the
* file descriptor.*/
typedef int (ofposixp)(const struct openposixfile*, const void*);
static struct openposixfile*
ofposix_find(struct openposixfile* arr, ofposixp* p, const void* userdata)
{
for(size_t i=0; i < MAX_FILES; ++i) {
if(p(&arr[i], userdata)) {
return &arr[i];
}
}
return NULL;
}
static int
fd_of(const struct openposixfile* f, const void* fdvoid)
{
const int* fd = (const int*)fdvoid;
return f->fd == (*fd);
}
__attribute__((constructor(201))) static void
fp_posix_init()
{
TRACE(posix, "[%ld] loading function pointers.", (long)getpid());
openf = dlsym(RTLD_NEXT, "open");
writef = dlsym(RTLD_NEXT, "write");
closef = dlsym(RTLD_NEXT, "close");
assert(openf != NULL);
assert(writef != NULL);
assert(closef != NULL);
}
int
open(const char* fn, int flags, ...)
{
mode_t mode = S_IRUSR | S_IWUSR;
if(flags & O_CREAT) {
va_list lst;
va_start(lst, flags);
mode = va_arg(lst, mode_t);
va_end(lst);
}
if(!matches(transferlibs, fn) || flags & O_RDONLY ||
strncmp(fn, "/tmp", 4) == 0 ||
strncmp(fn, "/dev", 4) == 0) {
TRACE(posix, "%s opened, but ignored by policy.", fn);
const int des = openf(fn, flags, mode);
return des;
}
const int des = openf(fn, flags, mode);
if(des <= 0) { /* open failed; ignoring this file. */
TRACE(posix, "posix-opening %s ignored due to open failure", fn);
return des;
} else {
TRACE(posix, "posix-opening %s...", fn);
}
/* need an empty entry in the table to store the return value. */
int empty = 0;
struct openposixfile* of = ofposix_find(posix_files, fd_of, &empty);
if(of == NULL) {
WARN(posix, "out of open files. skipping '%s'", fn);
return des;
}
assert(of->name == NULL);
of->name = strdup(fn);
of->fd = des;
file(transferlibs, fn);
return des;
}
ssize_t
write(int fd, const void *buf, size_t sz)
{
struct openposixfile* of = ofposix_find(posix_files, fd_of, &fd);
if(of == NULL) {
return writef(fd, buf, sz);
}
TRACE(posix, "writing %zu bytes to %d", sz, fd);
stream(transferlibs, of->name, buf, sz);
/* It will cause us a lot of problems if a write ends up being short, and the
* application then resubmits the next part of the partial write. So,
* iterate and make sure we avoid any partial writes. */
{
ssize_t written = 0;
do {
errno=0;
ssize_t bytes = writef(fd, ((const char*)buf)+written, sz-written);
if(bytes == -1 && errno == EINTR) { continue; }
if(bytes == -1 && written == 0) { return -1; }
if(bytes == -1) { return written; }
written += bytes;
} while((size_t)written < sz);
return written;
}
}
int
close(int des)
{
struct openposixfile* of = ofposix_find(posix_files, fd_of, &des);
if(of == NULL) {
TRACE(posix, "don't know FD %d; skipping 'close' instrumentation.", des);
return closef(des);
}
TRACE(posix, "closing %s (FD %d [%d])", of->name, des, of->fd);
assert(of->fd == des);
if(of->name == NULL) { /* don't bother, not a real file? */
WARN(posix, "null filename!?");
return closef(des);
}
finish(transferlibs, of->name);
{ /* free up rid of table entry */
free(of->name);
of->name = NULL;
of->fd = 0;
}
return closef(des);
}