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 23struct preopen_state; 24 25struct preopen_helper { 26 struct preopen_state *state; 27 struct fd_event *fde; 28 pid_t pid; 29 int fd; 30 bool busy; 31}; 32 33struct 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 53static 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 63static 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 107static 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 131static 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 145static 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 189static 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 214static 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 249static 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 289static void preopen_free_helpers(void **ptr) 290{ 291 TALLOC_FREE(*ptr); 292} 293 294static 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 338static 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 374static 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 443static struct vfs_fn_pointers vfs_preopen_fns = { 444 .open = preopen_open 445}; 446 447NTSTATUS vfs_preopen_init(void); 448NTSTATUS vfs_preopen_init(void) 449{ 450 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, 451 "preopen", &vfs_preopen_fns); 452} 453