Merge branch 'master' of git://git.samba.org/samba into minschema
[samba.git] / source3 / modules / vfs_preopen.c
1 /*
2  * Force a readahead of files by opening them and reading the first bytes
3  *
4  * Copyright (C) Volker Lendecke 2008
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "includes.h"
22
23 struct preopen_state;
24
25 struct preopen_helper {
26         struct preopen_state *state;
27         struct fd_event *fde;
28         pid_t pid;
29         int fd;
30         bool busy;
31 };
32
33 struct preopen_state {
34         int num_helpers;
35         struct preopen_helper *helpers;
36
37         size_t to_read;         /* How many bytes to read in children? */
38         int queue_max;
39
40         char *template_fname;   /* Filename to be sent to children */
41         size_t number_start;    /* start offset into "template_fname" */
42         int num_digits;         /* How many digits is the number long? */
43
44         int fnum_sent;          /* last fname sent to children */
45
46         int fnum_queue_end;     /* last fname to be sent, based on
47                                  * last open call + preopen:queuelen
48                                  */
49
50         name_compare_entry *preopen_names;
51 };
52
53 static void preopen_helper_destroy(struct preopen_helper *c)
54 {
55         int status;
56         close(c->fd);
57         c->fd = -1;
58         kill(c->pid, SIGKILL);
59         waitpid(c->pid, &status, 0);
60         c->busy = true;
61 }
62
63 static void preopen_queue_run(struct preopen_state *state)
64 {
65         char *pdelimiter;
66         char delimiter;
67
68         pdelimiter = state->template_fname + state->number_start
69                 + state->num_digits;
70         delimiter = *pdelimiter;
71
72         while (state->fnum_sent < state->fnum_queue_end) {
73
74                 ssize_t written;
75                 size_t to_write;
76                 int helper;
77
78                 for (helper=0; helper<state->num_helpers; helper++) {
79                         if (state->helpers[helper].busy) {
80                                 continue;
81                         }
82                         break;
83                 }
84                 if (helper == state->num_helpers) {
85                         /* everyone is busy */
86                         return;
87                 }
88
89                 snprintf(state->template_fname + state->number_start,
90                          state->num_digits + 1,
91                          "%.*lu", state->num_digits,
92                          (long unsigned int)(state->fnum_sent + 1));
93                 *pdelimiter = delimiter;
94
95                 to_write = talloc_get_size(state->template_fname);
96                 written = write_data(state->helpers[helper].fd,
97                                      state->template_fname, to_write);
98                 state->helpers[helper].busy = true;
99
100                 if (written != to_write) {
101                         preopen_helper_destroy(&state->helpers[helper]);
102                 }
103                 state->fnum_sent += 1;
104         }
105 }
106
107 static void preopen_helper_readable(struct event_context *ev,
108                                     struct fd_event *fde, uint16_t flags,
109                                     void *priv)
110 {
111         struct preopen_helper *helper = (struct preopen_helper *)priv;
112         struct preopen_state *state = helper->state;
113         ssize_t nread;
114         char c;
115
116         if ((flags & EVENT_FD_READ) == 0) {
117                 return;
118         }
119
120         nread = read(helper->fd, &c, 1);
121         if (nread <= 0) {
122                 preopen_helper_destroy(helper);
123                 return;
124         }
125
126         helper->busy = false;
127
128         preopen_queue_run(state);
129 }
130
131 static int preopen_helpers_destructor(struct preopen_state *c)
132 {
133         int i;
134
135         for (i=0; i<c->num_helpers; i++) {
136                 if (c->helpers[i].fd == -1) {
137                         continue;
138                 }
139                 preopen_helper_destroy(&c->helpers[i]);
140         }
141
142         return 0;
143 }
144
145 static bool preopen_helper_open_one(int sock_fd, char **pnamebuf,
146                                     size_t to_read, void *filebuf)
147 {
148         char *namebuf = *pnamebuf;
149         ssize_t nwritten, nread;
150         char c = 0;
151         int fd;
152
153         nread = 0;
154
155         while ((nread == 0) || (namebuf[nread-1] != '\0')) {
156                 ssize_t thistime;
157
158                 thistime = read(sock_fd, namebuf + nread,
159                                 talloc_get_size(namebuf) - nread);
160                 if (thistime <= 0) {
161                         return false;
162                 }
163
164                 nread += thistime;
165
166                 if (nread == talloc_get_size(namebuf)) {
167                         namebuf = TALLOC_REALLOC_ARRAY(
168                                 NULL, namebuf, char,
169                                 talloc_get_size(namebuf) * 2);
170                         if (namebuf == NULL) {
171                                 return false;
172                         }
173                         *pnamebuf = namebuf;
174                 }
175         }
176
177         fd = open(namebuf, O_RDONLY);
178         if (fd == -1) {
179                 goto done;
180         }
181         nread = read(fd, filebuf, to_read);
182         close(fd);
183
184  done:
185         nwritten = write(sock_fd, &c, 1);
186         return true;
187 }
188
189 static bool preopen_helper(int fd, size_t to_read)
190 {
191         char *namebuf;
192         void *readbuf;
193
194         namebuf = TALLOC_ARRAY(NULL, char, 1024);
195         if (namebuf == NULL) {
196                 return false;
197         }
198
199         readbuf = talloc_size(NULL, to_read);
200         if (readbuf == NULL) {
201                 TALLOC_FREE(namebuf);
202                 return false;
203         }
204
205         while (preopen_helper_open_one(fd, &namebuf, to_read, readbuf)) {
206                 ;
207         }
208
209         TALLOC_FREE(readbuf);
210         TALLOC_FREE(namebuf);
211         return false;
212 }
213
214 static NTSTATUS preopen_init_helper(struct preopen_helper *h)
215 {
216         int fdpair[2];
217         NTSTATUS status;
218
219         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) == -1) {
220                 status = map_nt_error_from_unix(errno);
221                 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno)));
222                 return status;
223         }
224
225         h->pid = sys_fork();
226
227         if (h->pid == -1) {
228                 return map_nt_error_from_unix(errno);
229         }
230
231         if (h->pid == 0) {
232                 close(fdpair[0]);
233                 preopen_helper(fdpair[1], h->state->to_read);
234                 exit(0);
235         }
236         close(fdpair[1]);
237         h->fd = fdpair[0];
238         h->fde = event_add_fd(smbd_event_context(), h->state, h->fd,
239                               EVENT_FD_READ, preopen_helper_readable, h);
240         if (h->fde == NULL) {
241                 close(h->fd);
242                 h->fd = -1;
243                 return NT_STATUS_NO_MEMORY;
244         }
245         h->busy = false;
246         return NT_STATUS_OK;
247 }
248
249 static NTSTATUS preopen_init_helpers(TALLOC_CTX *mem_ctx, size_t to_read,
250                                      int num_helpers, int queue_max,
251                                      struct preopen_state **presult)
252 {
253         struct preopen_state *result;
254         int i;
255
256         result = talloc(mem_ctx, struct preopen_state);
257         if (result == NULL) {
258                 return NT_STATUS_NO_MEMORY;
259         }
260
261         result->num_helpers = num_helpers;
262         result->helpers = TALLOC_ARRAY(result, struct preopen_helper,
263                                        num_helpers);
264         if (result->helpers == NULL) {
265                 TALLOC_FREE(result);
266                 return NT_STATUS_NO_MEMORY;
267         }
268
269         result->to_read = to_read;
270         result->queue_max = queue_max;
271         result->template_fname = NULL;
272         result->fnum_sent = 0;
273
274         for (i=0; i<num_helpers; i++) {
275                 result->helpers[i].state = result;
276                 result->helpers[i].fd = -1;
277         }
278
279         talloc_set_destructor(result, preopen_helpers_destructor);
280
281         for (i=0; i<num_helpers; i++) {
282                 preopen_init_helper(&result->helpers[i]);
283         }
284
285         *presult = result;
286         return NT_STATUS_OK;
287 }
288
289 static void preopen_free_helpers(void **ptr)
290 {
291         TALLOC_FREE(*ptr);
292 }
293
294 static struct preopen_state *preopen_state_get(vfs_handle_struct *handle)
295 {
296         struct preopen_state *state;
297         NTSTATUS status;
298         const char *namelist;
299
300         if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
301                 SMB_VFS_HANDLE_GET_DATA(handle, state, struct preopen_state,
302                                         return NULL);
303                 return state;
304         }
305
306         namelist = lp_parm_const_string(SNUM(handle->conn), "preopen", "names",
307                                         NULL);
308
309         if (namelist == NULL) {
310                 return NULL;
311         }
312
313         status = preopen_init_helpers(
314                 NULL,
315                 lp_parm_int(SNUM(handle->conn), "preopen", "num_bytes", 1),
316                 lp_parm_int(SNUM(handle->conn), "preopen", "helpers", 1),
317                 lp_parm_int(SNUM(handle->conn), "preopen", "queuelen", 10),
318                 &state);
319         if (!NT_STATUS_IS_OK(status)) {
320                 return NULL;
321         }
322
323         set_namearray(&state->preopen_names, (char *)namelist);
324
325         if (state->preopen_names == NULL) {
326                 TALLOC_FREE(state);
327                 return NULL;
328         }
329
330         if (!SMB_VFS_HANDLE_TEST_DATA(handle)) {
331                 SMB_VFS_HANDLE_SET_DATA(handle, state, preopen_free_helpers,
332                                         struct preopen_state, return NULL);
333         }
334
335         return state;
336 }
337
338 static bool preopen_parse_fname(const char *fname, unsigned long *pnum,
339                                 size_t *pstart_idx, int *pnum_digits)
340 {
341         const char *p, *q;
342         unsigned long num;
343
344         p = strrchr_m(fname, '/');
345         if (p == NULL) {
346                 p = fname;
347         }
348
349         p += 1;
350         while (p[0] != '\0') {
351                 if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])) {
352                         break;
353                 }
354                 p += 1;
355         }
356         if (*p == '\0') {
357                 /* no digits around */
358                 return false;
359         }
360
361         num = strtoul(p, (char **)&q, 10);
362
363         if (num+1 < num) {
364                 /* overflow */
365                 return false;
366         }
367
368         *pnum = num;
369         *pstart_idx = (p - fname);
370         *pnum_digits = (q - p);
371         return true;
372 }
373
374 static int preopen_open(vfs_handle_struct *handle, const char *fname,
375                         files_struct *fsp, int flags, mode_t mode)
376 {
377         struct preopen_state *state;
378         int res;
379         unsigned long num;
380
381         DEBUG(10, ("preopen_open called on %s\n", fname));
382
383         state = preopen_state_get(handle);
384         if (state == NULL) {
385                 return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
386         }
387
388         res = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
389         if (res == -1) {
390                 return -1;
391         }
392
393         if (flags != O_RDONLY) {
394                 return res;
395         }
396
397         if (!is_in_path(fname, state->preopen_names, true)) {
398                 DEBUG(10, ("%s does not match the preopen:names list\n",
399                            fname));
400                 return res;
401         }
402
403         TALLOC_FREE(state->template_fname);
404         state->template_fname = talloc_asprintf(
405                 state, "%s/%s", fsp->conn->connectpath, fname);
406
407         if (state->template_fname == NULL) {
408                 return res;
409         }
410
411         if (!preopen_parse_fname(state->template_fname, &num,
412                                  &state->number_start, &state->num_digits)) {
413                 TALLOC_FREE(state->template_fname);
414                 return res;
415         }
416
417         if (num > state->fnum_sent) {
418                 /*
419                  * Helpers were too slow, there's no point in reading
420                  * files in helpers that we already read in the
421                  * parent.
422                  */
423                 state->fnum_sent = num;
424         }
425
426         if ((state->fnum_queue_end != 0) /* Something was started earlier */
427             && (num < (state->fnum_queue_end - state->queue_max))) {
428                 /*
429                  * "num" is before the queue we announced. This means
430                  * a new run is started.
431                  */
432                 state->fnum_sent = num;
433         }
434
435         state->fnum_queue_end = num + state->queue_max;
436
437         preopen_queue_run(state);
438
439         return res;
440 }
441
442 /* VFS operations structure */
443
444 static vfs_op_tuple preopen_ops[] = {
445         {SMB_VFS_OP(preopen_open),      SMB_VFS_OP_OPEN,
446          SMB_VFS_LAYER_TRANSPARENT},
447         {SMB_VFS_OP(NULL),              SMB_VFS_OP_NOOP,
448          SMB_VFS_LAYER_NOOP}
449 };
450
451 NTSTATUS vfs_preopen_init(void);
452 NTSTATUS vfs_preopen_init(void)
453 {
454         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
455                                 "preopen", preopen_ops);
456 }