e545901035bc1853e942565d659777fb4f69264e
[gd/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,
375                         struct smb_filename *smb_fname, files_struct *fsp,
376                         int flags, mode_t mode)
377 {
378         struct preopen_state *state;
379         int res;
380         unsigned long num;
381
382         DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname)));
383
384         state = preopen_state_get(handle);
385         if (state == NULL) {
386                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
387         }
388
389         res = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
390         if (res == -1) {
391                 return -1;
392         }
393
394         if (flags != O_RDONLY) {
395                 return res;
396         }
397
398         if (!is_in_path(smb_fname->base_name, state->preopen_names, true)) {
399                 DEBUG(10, ("%s does not match the preopen:names list\n",
400                            smb_fname_str_dbg(smb_fname)));
401                 return res;
402         }
403
404         TALLOC_FREE(state->template_fname);
405         state->template_fname = talloc_asprintf(
406                 state, "%s/%s", fsp->conn->connectpath, smb_fname->base_name);
407
408         if (state->template_fname == NULL) {
409                 return res;
410         }
411
412         if (!preopen_parse_fname(state->template_fname, &num,
413                                  &state->number_start, &state->num_digits)) {
414                 TALLOC_FREE(state->template_fname);
415                 return res;
416         }
417
418         if (num > state->fnum_sent) {
419                 /*
420                  * Helpers were too slow, there's no point in reading
421                  * files in helpers that we already read in the
422                  * parent.
423                  */
424                 state->fnum_sent = num;
425         }
426
427         if ((state->fnum_queue_end != 0) /* Something was started earlier */
428             && (num < (state->fnum_queue_end - state->queue_max))) {
429                 /*
430                  * "num" is before the queue we announced. This means
431                  * a new run is started.
432                  */
433                 state->fnum_sent = num;
434         }
435
436         state->fnum_queue_end = num + state->queue_max;
437
438         preopen_queue_run(state);
439
440         return res;
441 }
442
443 static struct vfs_fn_pointers vfs_preopen_fns = {
444         .open = preopen_open
445 };
446
447 NTSTATUS vfs_preopen_init(void);
448 NTSTATUS vfs_preopen_init(void)
449 {
450         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
451                                 "preopen", &vfs_preopen_fns);
452 }