root/source3/modules/vfs_preopen.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. preopen_helper_destroy
  2. preopen_queue_run
  3. preopen_helper_readable
  4. preopen_helpers_destructor
  5. preopen_helper_open_one
  6. preopen_helper
  7. preopen_init_helper
  8. preopen_init_helpers
  9. preopen_free_helpers
  10. preopen_state_get
  11. preopen_parse_fname
  12. preopen_open
  13. vfs_preopen_init

   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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 290 {
 291         TALLOC_FREE(*ptr);
 292 }
 293 
 294 static struct preopen_state *preopen_state_get(vfs_handle_struct *handle)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 453 {
 454         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
 455                                 "preopen", preopen_ops);
 456 }

/* [<][>][^][v][top][bottom][index][help] */