vfs_preopen: introduce support for "preopen:posix-basic-regex = yes"
[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 #define PREOPEN_MAX_DIGITS 19
36 #define PREOPEN_MAX_NUMBER (uint64_t)9999999999999999999ULL
37
38 struct preopen_state;
39
40 struct preopen_helper {
41         struct preopen_state *state;
42         struct tevent_fd *fde;
43         pid_t pid;
44         int fd;
45         bool busy;
46 };
47
48 struct preopen_state {
49         int num_helpers;
50         struct preopen_helper *helpers;
51
52         size_t to_read;         /* How many bytes to read in children? */
53         int queue_max;
54
55         char *template_fname;   /* Filename to be sent to children */
56         size_t number_start;    /* start offset into "template_fname" */
57         int num_digits;         /* How many digits is the number long? */
58
59         uint64_t fnum_sent;     /* last fname sent to children */
60
61         uint64_t fnum_queue_end;/* last fname to be sent, based on
62                                  * last open call + preopen:queuelen
63                                  */
64
65         struct samba_path_matching *preopen_names;
66         ssize_t last_match_idx; /* remember the last match */
67 };
68
69 static void preopen_helper_destroy(struct preopen_helper *c)
70 {
71         int status;
72         TALLOC_FREE(c->fde);
73         close(c->fd);
74         c->fd = -1;
75         kill(c->pid, SIGKILL);
76         waitpid(c->pid, &status, 0);
77         c->busy = true;
78 }
79
80 static void preopen_queue_run(struct preopen_state *state)
81 {
82         char *pdelimiter;
83         char delimiter;
84
85         pdelimiter = state->template_fname + state->number_start
86                 + state->num_digits;
87         delimiter = *pdelimiter;
88
89         while (state->fnum_sent < state->fnum_queue_end) {
90
91                 ssize_t written;
92                 size_t to_write;
93                 int helper;
94
95                 for (helper=0; helper<state->num_helpers; helper++) {
96                         if (state->helpers[helper].busy) {
97                                 continue;
98                         }
99                         break;
100                 }
101                 if (helper == state->num_helpers) {
102                         /* everyone is busy */
103                         return;
104                 }
105
106                 snprintf(state->template_fname + state->number_start,
107                          state->num_digits + 1,
108                          "%.*llu", state->num_digits,
109                          (long long unsigned int)(state->fnum_sent + 1));
110                 *pdelimiter = delimiter;
111
112                 to_write = talloc_get_size(state->template_fname);
113                 written = write_data(state->helpers[helper].fd,
114                                      state->template_fname, to_write);
115                 state->helpers[helper].busy = true;
116
117                 if (written != to_write) {
118                         preopen_helper_destroy(&state->helpers[helper]);
119                 }
120                 state->fnum_sent += 1;
121         }
122 }
123
124 static void preopen_helper_readable(struct tevent_context *ev,
125                                     struct tevent_fd *fde, uint16_t flags,
126                                     void *priv)
127 {
128         struct preopen_helper *helper = (struct preopen_helper *)priv;
129         struct preopen_state *state = helper->state;
130         ssize_t nread;
131         char c;
132
133         if ((flags & TEVENT_FD_READ) == 0) {
134                 return;
135         }
136
137         nread = read(helper->fd, &c, 1);
138         if (nread <= 0) {
139                 preopen_helper_destroy(helper);
140                 return;
141         }
142
143         helper->busy = false;
144
145         preopen_queue_run(state);
146 }
147
148 static int preopen_helpers_destructor(struct preopen_state *c)
149 {
150         int i;
151
152         for (i=0; i<c->num_helpers; i++) {
153                 if (c->helpers[i].fd == -1) {
154                         continue;
155                 }
156                 preopen_helper_destroy(&c->helpers[i]);
157         }
158
159         return 0;
160 }
161
162 static bool preopen_helper_open_one(int sock_fd, char **pnamebuf,
163                                     size_t to_read, void *filebuf)
164 {
165         char *namebuf = *pnamebuf;
166         ssize_t nread;
167         char c = 0;
168         int fd;
169
170         nread = 0;
171
172         do {
173                 ssize_t thistime;
174
175                 thistime = read(sock_fd, namebuf + nread,
176                                 talloc_get_size(namebuf) - nread);
177                 if (thistime <= 0) {
178                         return false;
179                 }
180
181                 nread += thistime;
182
183                 if (nread == talloc_get_size(namebuf)) {
184                         namebuf = talloc_realloc(
185                                 NULL, namebuf, char,
186                                 talloc_get_size(namebuf) * 2);
187                         if (namebuf == NULL) {
188                                 return false;
189                         }
190                         *pnamebuf = namebuf;
191                 }
192         } while (namebuf[nread - 1] != '\0');
193
194         fd = open(namebuf, O_RDONLY);
195         if (fd == -1) {
196                 goto done;
197         }
198         nread = read(fd, filebuf, to_read);
199         close(fd);
200
201  done:
202         sys_write_v(sock_fd, &c, 1);
203         return true;
204 }
205
206 static bool preopen_helper(int fd, size_t to_read)
207 {
208         char *namebuf;
209         void *readbuf;
210
211         namebuf = talloc_array(NULL, char, 1024);
212         if (namebuf == NULL) {
213                 return false;
214         }
215
216         readbuf = talloc_size(NULL, to_read);
217         if (readbuf == NULL) {
218                 TALLOC_FREE(namebuf);
219                 return false;
220         }
221
222         while (preopen_helper_open_one(fd, &namebuf, to_read, readbuf)) {
223                 ;
224         }
225
226         TALLOC_FREE(readbuf);
227         TALLOC_FREE(namebuf);
228         return false;
229 }
230
231 static NTSTATUS preopen_init_helper(struct preopen_helper *h)
232 {
233         int fdpair[2];
234         NTSTATUS status;
235
236         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) == -1) {
237                 status = map_nt_error_from_unix(errno);
238                 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno)));
239                 return status;
240         }
241
242         h->pid = fork();
243
244         if (h->pid == -1) {
245                 return map_nt_error_from_unix(errno);
246         }
247
248         if (h->pid == 0) {
249                 close(fdpair[0]);
250                 preopen_helper(fdpair[1], h->state->to_read);
251                 exit(0);
252         }
253         close(fdpair[1]);
254         h->fd = fdpair[0];
255         h->fde = tevent_add_fd(global_event_context(), h->state, h->fd,
256                               TEVENT_FD_READ, preopen_helper_readable, h);
257         if (h->fde == NULL) {
258                 close(h->fd);
259                 h->fd = -1;
260                 return NT_STATUS_NO_MEMORY;
261         }
262         h->busy = false;
263         return NT_STATUS_OK;
264 }
265
266 static NTSTATUS preopen_init_helpers(TALLOC_CTX *mem_ctx, size_t to_read,
267                                      int num_helpers, int queue_max,
268                                      struct preopen_state **presult)
269 {
270         struct preopen_state *result;
271         int i;
272
273         result = talloc(mem_ctx, struct preopen_state);
274         if (result == NULL) {
275                 return NT_STATUS_NO_MEMORY;
276         }
277
278         result->num_helpers = num_helpers;
279         result->helpers = talloc_array(result, struct preopen_helper,
280                                        num_helpers);
281         if (result->helpers == NULL) {
282                 TALLOC_FREE(result);
283                 return NT_STATUS_NO_MEMORY;
284         }
285
286         result->to_read = to_read;
287         result->queue_max = queue_max;
288         result->template_fname = NULL;
289         result->fnum_sent = 0;
290         result->fnum_queue_end = 0;
291
292         for (i=0; i<num_helpers; i++) {
293                 result->helpers[i].state = result;
294                 result->helpers[i].fd = -1;
295         }
296
297         talloc_set_destructor(result, preopen_helpers_destructor);
298
299         for (i=0; i<num_helpers; i++) {
300                 preopen_init_helper(&result->helpers[i]);
301         }
302
303         *presult = result;
304         return NT_STATUS_OK;
305 }
306
307 static void preopen_free_helpers(void **ptr)
308 {
309         TALLOC_FREE(*ptr);
310 }
311
312 static struct preopen_state *preopen_state_get(vfs_handle_struct *handle)
313 {
314         struct preopen_state *state;
315         NTSTATUS status;
316         const char *namelist;
317
318         if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
319                 SMB_VFS_HANDLE_GET_DATA(handle, state, struct preopen_state,
320                                         return NULL);
321                 return state;
322         }
323
324         namelist = lp_parm_const_string(SNUM(handle->conn), "preopen", "names",
325                                         NULL);
326
327         if (namelist == NULL) {
328                 return NULL;
329         }
330
331         status = preopen_init_helpers(
332                 NULL,
333                 lp_parm_int(SNUM(handle->conn), "preopen", "num_bytes", 1),
334                 lp_parm_int(SNUM(handle->conn), "preopen", "helpers", 1),
335                 lp_parm_int(SNUM(handle->conn), "preopen", "queuelen", 10),
336                 &state);
337         if (!NT_STATUS_IS_OK(status)) {
338                 return NULL;
339         }
340
341         if (lp_parm_bool(SNUM(handle->conn), "preopen", "posix-basic-regex", false)) {
342                 status = samba_path_matching_regex_sub1_create(state,
343                                                                namelist,
344                                                                &state->preopen_names);
345         } else {
346                 status = samba_path_matching_mswild_create(state,
347                                                            true, /* case_sensitive */
348                                                            namelist,
349                                                            &state->preopen_names);
350         }
351         if (!NT_STATUS_IS_OK(status)) {
352                 TALLOC_FREE(state);
353                 return NULL;
354         }
355         state->last_match_idx = -1;
356
357         if (!SMB_VFS_HANDLE_TEST_DATA(handle)) {
358                 SMB_VFS_HANDLE_SET_DATA(handle, state, preopen_free_helpers,
359                                         struct preopen_state, return NULL);
360         }
361
362         return state;
363 }
364
365 static bool preopen_parse_fname(const char *fname, uint64_t *pnum,
366                                 size_t *pstart_idx, int *pnum_digits)
367 {
368         char digits[PREOPEN_MAX_DIGITS+1] = { 0, };
369         const char *p;
370         char *q = NULL;
371         unsigned long long num;
372         size_t start_idx = 0;
373         int num_digits = -1;
374         int error = 0;
375
376         if (*pstart_idx > 0 && *pnum_digits > 0) {
377                 /*
378                  * If the caller knowns
379                  * how many digits are expected
380                  * and on what position,
381                  * we should copy the exact
382                  * subset before we start
383                  * parsing the string into a number
384                  */
385
386                 if (*pnum_digits < 1) {
387                         /*
388                          * We need at least one digit
389                          */
390                         return false;
391                 }
392                 if (*pnum_digits > PREOPEN_MAX_DIGITS) {
393                         /*
394                          * a string with as much digits as
395                          * PREOPEN_MAX_DIGITS is the longest
396                          * string that would make any sense for us.
397                          *
398                          * The rest will be checked via
399                          * smb_strtoull().
400                          */
401                         return false;
402                 }
403                 p = fname + *pstart_idx;
404                 memcpy(digits, p, *pnum_digits);
405                 p = digits;
406                 start_idx = *pstart_idx;
407                 goto parse;
408         }
409
410         p = strrchr_m(fname, '/');
411         if (p == NULL) {
412                 p = fname;
413         }
414
415         p += 1;
416         while (p[0] != '\0') {
417                 if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])) {
418                         break;
419                 }
420                 p += 1;
421         }
422         if (*p == '\0') {
423                 /* no digits around */
424                 return false;
425         }
426
427         start_idx = (p - fname);
428
429 parse:
430         num = smb_strtoull(p, (char **)&q, 10, &error, SMB_STR_STANDARD);
431         if (error != 0) {
432                 return false;
433         }
434
435         if (num >= PREOPEN_MAX_NUMBER) {
436                 /* overflow */
437                 return false;
438         }
439
440         num_digits = (q - p);
441
442         if (*pnum_digits != -1 && *pnum_digits != num_digits) {
443                 /*
444                  * If the caller knowns how many digits
445                  * it expects we should fail if we got something
446                  * different.
447                  */
448                 return false;
449         }
450
451         *pnum = num;
452         *pstart_idx = start_idx;
453         *pnum_digits = num_digits;
454         return true;
455 }
456
457 static uint64_t num_digits_max_value(int num_digits)
458 {
459         uint64_t num_max = 1;
460         int i;
461
462         if (num_digits < 1) {
463                 return 0;
464         }
465         if (num_digits >= PREOPEN_MAX_DIGITS) {
466                 return PREOPEN_MAX_NUMBER;
467         }
468
469         for (i = 0; i < num_digits; i++) {
470                 num_max *= 10;
471         }
472
473         /*
474          * We actually want
475          * 9   instead of 10
476          * 99  instead of 100
477          * 999 instead of 1000
478          */
479         return num_max - 1;
480 }
481
482 static int preopen_openat(struct vfs_handle_struct *handle,
483                           const struct files_struct *dirfsp,
484                           const struct smb_filename *smb_fname,
485                           struct files_struct *fsp,
486                           int flags,
487                           mode_t mode)
488 {
489         const char *dirname = dirfsp->fsp_name->base_name;
490         struct preopen_state *state;
491         int res;
492         uint64_t num;
493         uint64_t num_max;
494         NTSTATUS status;
495         char *new_template = NULL;
496         size_t new_start = 0;
497         int new_digits = -1;
498         size_t new_end = 0;
499         ssize_t match_idx = -1;
500         ssize_t replace_start = -1;
501         ssize_t replace_end = -1;
502         bool need_reset = false;
503
504         DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname)));
505
506         state = preopen_state_get(handle);
507         if (state == NULL) {
508                 return SMB_VFS_NEXT_OPENAT(handle,
509                                            dirfsp,
510                                            smb_fname,
511                                            fsp,
512                                            flags,
513                                            mode);
514         }
515
516         res = SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, flags, mode);
517         if (res == -1) {
518                 return -1;
519         }
520
521         if ((flags & O_ACCMODE) != O_RDONLY) {
522                 return res;
523         }
524
525         /*
526          * Make sure we can later contruct an absolute pathname
527          */
528         if (dirname[0] != '/') {
529                 return res;
530         }
531         /*
532          * There's no point in preopen the directory itself.
533          */
534         if (ISDOT(smb_fname->base_name)) {
535                 return res;
536         }
537         /*
538          * If we got an absolute path in
539          * smb_fname it's most likely the
540          * reopen via /proc/self/fd/$fd
541          */
542         if (smb_fname->base_name[0] == '/') {
543                 return res;
544         }
545
546         status = samba_path_matching_check_last_component(state->preopen_names,
547                                                           smb_fname->base_name,
548                                                           &match_idx,
549                                                           &replace_start,
550                                                           &replace_end);
551         if (!NT_STATUS_IS_OK(status)) {
552                 match_idx = -1;
553         }
554         if (match_idx < 0) {
555                 DEBUG(10, ("%s does not match the preopen:names list\n",
556                            smb_fname_str_dbg(smb_fname)));
557                 return res;
558         }
559
560         new_template = talloc_asprintf(
561                 state, "%s/%s",
562                 dirname, smb_fname->base_name);
563         if (new_template == NULL) {
564                 return res;
565         }
566
567         if (replace_start != -1 && replace_end != -1) {
568                 size_t dirofs = strlen(dirname) + 1;
569                 new_start = dirofs + replace_start;
570                 new_digits = replace_end - replace_start;
571         }
572
573         if (!preopen_parse_fname(new_template, &num,
574                                  &new_start, &new_digits)) {
575                 TALLOC_FREE(new_template);
576                 return res;
577         }
578         new_end = new_start + new_digits;
579
580         if (state->last_match_idx != match_idx) {
581                 /*
582                  * If a different pattern caused the match
583                  * we better reset the queue
584                  */
585                 need_reset = true;
586         } else if (state->number_start != new_start) {
587                 /*
588                  * If the digits started at a different possition
589                  * we better reset the queue
590                  */
591                 need_reset = true;
592         } else if (state->num_digits != new_digits) {
593                 /*
594                  * If number of digits changed
595                  * we better reset the queue
596                  */
597                 need_reset = true;
598         } else if (strncmp(state->template_fname, new_template, new_start) != 0) {
599                 /*
600                  * If name before the digits changed
601                  * we better reset the queue
602                  */
603                 need_reset = true;
604         } else if (strcmp(state->template_fname + new_end, new_template + new_end) != 0) {
605                 /*
606                  * If name after the digits changed
607                  * we better reset the queue
608                  */
609                 need_reset = true;
610         }
611
612         if (need_reset) {
613                 /*
614                  * Reset the queue
615                  */
616                 state->fnum_sent = 0;
617                 state->fnum_queue_end = 0;
618                 state->last_match_idx = match_idx;
619         }
620
621         TALLOC_FREE(state->template_fname);
622         state->template_fname = new_template;
623         state->number_start = new_start;
624         state->num_digits = new_digits;
625
626         if (num > state->fnum_sent) {
627                 /*
628                  * Helpers were too slow, there's no point in reading
629                  * files in helpers that we already read in the
630                  * parent.
631                  */
632                 state->fnum_sent = num;
633         }
634
635         if ((state->fnum_queue_end != 0) /* Something was started earlier */
636             && (num < (state->fnum_queue_end - state->queue_max))) {
637                 /*
638                  * "num" is before the queue we announced. This means
639                  * a new run is started.
640                  */
641                 state->fnum_sent = num;
642         }
643
644         num_max = num_digits_max_value(state->num_digits);
645         state->fnum_queue_end = MIN(num_max, num + state->queue_max);
646
647         preopen_queue_run(state);
648
649         return res;
650 }
651
652 static struct vfs_fn_pointers vfs_preopen_fns = {
653         .openat_fn = preopen_openat,
654 };
655
656 static_decl_vfs;
657 NTSTATUS vfs_preopen_init(TALLOC_CTX *ctx)
658 {
659         NTSTATUS status;
660
661         status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
662                                   "preopen",
663                                   &vfs_preopen_fns);
664         if (!NT_STATUS_IS_OK(status)) {
665                 return status;
666         }
667
668         vfs_preopen_debug_level = debug_add_class("preopen");
669         if (vfs_preopen_debug_level == -1) {
670                 vfs_preopen_debug_level = DBGC_VFS;
671                 DBG_ERR("Couldn't register custom debugging class!\n");
672         } else {
673                 DBG_DEBUG("Debug class number of 'preopen': %d\n",
674                           vfs_preopen_debug_level);
675         }
676
677         return NT_STATUS_OK;
678 }