vfs_preopen: only reset the queue state if preopen_parse_fname() found matching digits
[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 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "lib/util/sys_rw.h"
25 #include "lib/util/sys_rw_data.h"
26 #include "lib/util/smb_strtox.h"
27 #include "lib/util_matching.h"
28 #include "lib/global_contexts.h"
29
30 static int vfs_preopen_debug_level = DBGC_VFS;
31
32 #undef DBGC_CLASS
33 #define DBGC_CLASS vfs_preopen_debug_level
34
35 struct preopen_state;
36
37 struct preopen_helper {
38         struct preopen_state *state;
39         struct tevent_fd *fde;
40         pid_t pid;
41         int fd;
42         bool busy;
43 };
44
45 struct preopen_state {
46         int num_helpers;
47         struct preopen_helper *helpers;
48
49         size_t to_read;         /* How many bytes to read in children? */
50         int queue_max;
51
52         char *template_fname;   /* Filename to be sent to children */
53         size_t number_start;    /* start offset into "template_fname" */
54         int num_digits;         /* How many digits is the number long? */
55
56         int fnum_sent;          /* last fname sent to children */
57
58         int fnum_queue_end;     /* last fname to be sent, based on
59                                  * last open call + preopen:queuelen
60                                  */
61
62         struct samba_path_matching *preopen_names;
63 };
64
65 static void preopen_helper_destroy(struct preopen_helper *c)
66 {
67         int status;
68         TALLOC_FREE(c->fde);
69         close(c->fd);
70         c->fd = -1;
71         kill(c->pid, SIGKILL);
72         waitpid(c->pid, &status, 0);
73         c->busy = true;
74 }
75
76 static void preopen_queue_run(struct preopen_state *state)
77 {
78         char *pdelimiter;
79         char delimiter;
80
81         pdelimiter = state->template_fname + state->number_start
82                 + state->num_digits;
83         delimiter = *pdelimiter;
84
85         while (state->fnum_sent < state->fnum_queue_end) {
86
87                 ssize_t written;
88                 size_t to_write;
89                 int helper;
90
91                 for (helper=0; helper<state->num_helpers; helper++) {
92                         if (state->helpers[helper].busy) {
93                                 continue;
94                         }
95                         break;
96                 }
97                 if (helper == state->num_helpers) {
98                         /* everyone is busy */
99                         return;
100                 }
101
102                 snprintf(state->template_fname + state->number_start,
103                          state->num_digits + 1,
104                          "%.*lu", state->num_digits,
105                          (long unsigned int)(state->fnum_sent + 1));
106                 *pdelimiter = delimiter;
107
108                 to_write = talloc_get_size(state->template_fname);
109                 written = write_data(state->helpers[helper].fd,
110                                      state->template_fname, to_write);
111                 state->helpers[helper].busy = true;
112
113                 if (written != to_write) {
114                         preopen_helper_destroy(&state->helpers[helper]);
115                 }
116                 state->fnum_sent += 1;
117         }
118 }
119
120 static void preopen_helper_readable(struct tevent_context *ev,
121                                     struct tevent_fd *fde, uint16_t flags,
122                                     void *priv)
123 {
124         struct preopen_helper *helper = (struct preopen_helper *)priv;
125         struct preopen_state *state = helper->state;
126         ssize_t nread;
127         char c;
128
129         if ((flags & TEVENT_FD_READ) == 0) {
130                 return;
131         }
132
133         nread = read(helper->fd, &c, 1);
134         if (nread <= 0) {
135                 preopen_helper_destroy(helper);
136                 return;
137         }
138
139         helper->busy = false;
140
141         preopen_queue_run(state);
142 }
143
144 static int preopen_helpers_destructor(struct preopen_state *c)
145 {
146         int i;
147
148         for (i=0; i<c->num_helpers; i++) {
149                 if (c->helpers[i].fd == -1) {
150                         continue;
151                 }
152                 preopen_helper_destroy(&c->helpers[i]);
153         }
154
155         return 0;
156 }
157
158 static bool preopen_helper_open_one(int sock_fd, char **pnamebuf,
159                                     size_t to_read, void *filebuf)
160 {
161         char *namebuf = *pnamebuf;
162         ssize_t nread;
163         char c = 0;
164         int fd;
165
166         nread = 0;
167
168         do {
169                 ssize_t thistime;
170
171                 thistime = read(sock_fd, namebuf + nread,
172                                 talloc_get_size(namebuf) - nread);
173                 if (thistime <= 0) {
174                         return false;
175                 }
176
177                 nread += thistime;
178
179                 if (nread == talloc_get_size(namebuf)) {
180                         namebuf = talloc_realloc(
181                                 NULL, namebuf, char,
182                                 talloc_get_size(namebuf) * 2);
183                         if (namebuf == NULL) {
184                                 return false;
185                         }
186                         *pnamebuf = namebuf;
187                 }
188         } while (namebuf[nread - 1] != '\0');
189
190         fd = open(namebuf, O_RDONLY);
191         if (fd == -1) {
192                 goto done;
193         }
194         nread = read(fd, filebuf, to_read);
195         close(fd);
196
197  done:
198         sys_write_v(sock_fd, &c, 1);
199         return true;
200 }
201
202 static bool preopen_helper(int fd, size_t to_read)
203 {
204         char *namebuf;
205         void *readbuf;
206
207         namebuf = talloc_array(NULL, char, 1024);
208         if (namebuf == NULL) {
209                 return false;
210         }
211
212         readbuf = talloc_size(NULL, to_read);
213         if (readbuf == NULL) {
214                 TALLOC_FREE(namebuf);
215                 return false;
216         }
217
218         while (preopen_helper_open_one(fd, &namebuf, to_read, readbuf)) {
219                 ;
220         }
221
222         TALLOC_FREE(readbuf);
223         TALLOC_FREE(namebuf);
224         return false;
225 }
226
227 static NTSTATUS preopen_init_helper(struct preopen_helper *h)
228 {
229         int fdpair[2];
230         NTSTATUS status;
231
232         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) == -1) {
233                 status = map_nt_error_from_unix(errno);
234                 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno)));
235                 return status;
236         }
237
238         h->pid = fork();
239
240         if (h->pid == -1) {
241                 return map_nt_error_from_unix(errno);
242         }
243
244         if (h->pid == 0) {
245                 close(fdpair[0]);
246                 preopen_helper(fdpair[1], h->state->to_read);
247                 exit(0);
248         }
249         close(fdpair[1]);
250         h->fd = fdpair[0];
251         h->fde = tevent_add_fd(global_event_context(), h->state, h->fd,
252                               TEVENT_FD_READ, preopen_helper_readable, h);
253         if (h->fde == NULL) {
254                 close(h->fd);
255                 h->fd = -1;
256                 return NT_STATUS_NO_MEMORY;
257         }
258         h->busy = false;
259         return NT_STATUS_OK;
260 }
261
262 static NTSTATUS preopen_init_helpers(TALLOC_CTX *mem_ctx, size_t to_read,
263                                      int num_helpers, int queue_max,
264                                      struct preopen_state **presult)
265 {
266         struct preopen_state *result;
267         int i;
268
269         result = talloc(mem_ctx, struct preopen_state);
270         if (result == NULL) {
271                 return NT_STATUS_NO_MEMORY;
272         }
273
274         result->num_helpers = num_helpers;
275         result->helpers = talloc_array(result, struct preopen_helper,
276                                        num_helpers);
277         if (result->helpers == NULL) {
278                 TALLOC_FREE(result);
279                 return NT_STATUS_NO_MEMORY;
280         }
281
282         result->to_read = to_read;
283         result->queue_max = queue_max;
284         result->template_fname = NULL;
285         result->fnum_sent = 0;
286         result->fnum_queue_end = 0;
287
288         for (i=0; i<num_helpers; i++) {
289                 result->helpers[i].state = result;
290                 result->helpers[i].fd = -1;
291         }
292
293         talloc_set_destructor(result, preopen_helpers_destructor);
294
295         for (i=0; i<num_helpers; i++) {
296                 preopen_init_helper(&result->helpers[i]);
297         }
298
299         *presult = result;
300         return NT_STATUS_OK;
301 }
302
303 static void preopen_free_helpers(void **ptr)
304 {
305         TALLOC_FREE(*ptr);
306 }
307
308 static struct preopen_state *preopen_state_get(vfs_handle_struct *handle)
309 {
310         struct preopen_state *state;
311         NTSTATUS status;
312         const char *namelist;
313
314         if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
315                 SMB_VFS_HANDLE_GET_DATA(handle, state, struct preopen_state,
316                                         return NULL);
317                 return state;
318         }
319
320         namelist = lp_parm_const_string(SNUM(handle->conn), "preopen", "names",
321                                         NULL);
322
323         if (namelist == NULL) {
324                 return NULL;
325         }
326
327         status = preopen_init_helpers(
328                 NULL,
329                 lp_parm_int(SNUM(handle->conn), "preopen", "num_bytes", 1),
330                 lp_parm_int(SNUM(handle->conn), "preopen", "helpers", 1),
331                 lp_parm_int(SNUM(handle->conn), "preopen", "queuelen", 10),
332                 &state);
333         if (!NT_STATUS_IS_OK(status)) {
334                 return NULL;
335         }
336
337         status = samba_path_matching_mswild_create(state,
338                                                    true, /* case_sensitive */
339                                                    namelist,
340                                                    &state->preopen_names);
341         if (!NT_STATUS_IS_OK(status)) {
342                 TALLOC_FREE(state);
343                 return NULL;
344         }
345
346         if (!SMB_VFS_HANDLE_TEST_DATA(handle)) {
347                 SMB_VFS_HANDLE_SET_DATA(handle, state, preopen_free_helpers,
348                                         struct preopen_state, return NULL);
349         }
350
351         return state;
352 }
353
354 static bool preopen_parse_fname(const char *fname, unsigned long *pnum,
355                                 size_t *pstart_idx, int *pnum_digits)
356 {
357         const char *p;
358         char *q = NULL;
359         unsigned long num;
360         int error = 0;
361
362         p = strrchr_m(fname, '/');
363         if (p == NULL) {
364                 p = fname;
365         }
366
367         p += 1;
368         while (p[0] != '\0') {
369                 if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])) {
370                         break;
371                 }
372                 p += 1;
373         }
374         if (*p == '\0') {
375                 /* no digits around */
376                 return false;
377         }
378
379         num = smb_strtoul(p, (char **)&q, 10, &error, SMB_STR_STANDARD);
380         if (error != 0) {
381                 return false;
382         }
383
384         if (num+1 < num) {
385                 /* overflow */
386                 return false;
387         }
388
389         *pnum = num;
390         *pstart_idx = (p - fname);
391         *pnum_digits = (q - p);
392         return true;
393 }
394
395 static int preopen_openat(struct vfs_handle_struct *handle,
396                           const struct files_struct *dirfsp,
397                           const struct smb_filename *smb_fname,
398                           struct files_struct *fsp,
399                           int flags,
400                           mode_t mode)
401 {
402         const char *dirname = dirfsp->fsp_name->base_name;
403         struct preopen_state *state;
404         int res;
405         unsigned long num;
406         NTSTATUS status;
407         char *new_template = NULL;
408         size_t new_start = 0;
409         int new_digits = -1;
410         ssize_t match_idx = -1;
411
412         DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname)));
413
414         state = preopen_state_get(handle);
415         if (state == NULL) {
416                 return SMB_VFS_NEXT_OPENAT(handle,
417                                            dirfsp,
418                                            smb_fname,
419                                            fsp,
420                                            flags,
421                                            mode);
422         }
423
424         res = SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, flags, mode);
425         if (res == -1) {
426                 return -1;
427         }
428
429         if ((flags & O_ACCMODE) != O_RDONLY) {
430                 return res;
431         }
432
433         /*
434          * Make sure we can later contruct an absolute pathname
435          */
436         if (dirname[0] != '/') {
437                 return res;
438         }
439         /*
440          * There's no point in preopen the directory itself.
441          */
442         if (ISDOT(smb_fname->base_name)) {
443                 return res;
444         }
445         /*
446          * If we got an absolute path in
447          * smb_fname it's most likely the
448          * reopen via /proc/self/fd/$fd
449          */
450         if (smb_fname->base_name[0] == '/') {
451                 return res;
452         }
453
454         status = samba_path_matching_check_last_component(state->preopen_names,
455                                                           smb_fname->base_name,
456                                                           &match_idx,
457                                                           NULL, /* replace_start */
458                                                           NULL);/* replace_end */
459         if (!NT_STATUS_IS_OK(status)) {
460                 match_idx = -1;
461         }
462         if (match_idx < 0) {
463                 DEBUG(10, ("%s does not match the preopen:names list\n",
464                            smb_fname_str_dbg(smb_fname)));
465                 return res;
466         }
467
468         new_template = talloc_asprintf(
469                 state, "%s/%s",
470                 dirname, smb_fname->base_name);
471         if (new_template == NULL) {
472                 return res;
473         }
474
475         if (!preopen_parse_fname(new_template, &num,
476                                  &new_start, &new_digits)) {
477                 TALLOC_FREE(new_template);
478                 return res;
479         }
480
481         TALLOC_FREE(state->template_fname);
482         state->template_fname = new_template;
483         state->number_start = new_start;
484         state->num_digits = new_digits;
485
486         if (num > state->fnum_sent) {
487                 /*
488                  * Helpers were too slow, there's no point in reading
489                  * files in helpers that we already read in the
490                  * parent.
491                  */
492                 state->fnum_sent = num;
493         }
494
495         if ((state->fnum_queue_end != 0) /* Something was started earlier */
496             && (num < (state->fnum_queue_end - state->queue_max))) {
497                 /*
498                  * "num" is before the queue we announced. This means
499                  * a new run is started.
500                  */
501                 state->fnum_sent = num;
502         }
503
504         state->fnum_queue_end = num + state->queue_max;
505
506         preopen_queue_run(state);
507
508         return res;
509 }
510
511 static struct vfs_fn_pointers vfs_preopen_fns = {
512         .openat_fn = preopen_openat,
513 };
514
515 static_decl_vfs;
516 NTSTATUS vfs_preopen_init(TALLOC_CTX *ctx)
517 {
518         NTSTATUS status;
519
520         status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
521                                   "preopen",
522                                   &vfs_preopen_fns);
523         if (!NT_STATUS_IS_OK(status)) {
524                 return status;
525         }
526
527         vfs_preopen_debug_level = debug_add_class("preopen");
528         if (vfs_preopen_debug_level == -1) {
529                 vfs_preopen_debug_level = DBGC_VFS;
530                 DBG_ERR("Couldn't register custom debugging class!\n");
531         } else {
532                 DBG_DEBUG("Debug class number of 'preopen': %d\n",
533                           vfs_preopen_debug_level);
534         }
535
536         return NT_STATUS_OK;
537 }