docs: Fix typos in man vfs_gpfs.
[Samba.git] / source3 / modules / vfs_preopen.c
blobb67aad86b5575490e4431bc0bc8e72bb57a69700
1 /*
2 * Force a readahead of files by opening them and reading the first bytes
4 * Copyright (C) Volker Lendecke 2008
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.
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.
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.
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "lib/util/sys_rw_data.h"
26 struct preopen_state;
28 struct preopen_helper {
29 struct preopen_state *state;
30 struct tevent_fd *fde;
31 pid_t pid;
32 int fd;
33 bool busy;
36 struct preopen_state {
37 int num_helpers;
38 struct preopen_helper *helpers;
40 size_t to_read; /* How many bytes to read in children? */
41 int queue_max;
43 char *template_fname; /* Filename to be sent to children */
44 size_t number_start; /* start offset into "template_fname" */
45 int num_digits; /* How many digits is the number long? */
47 int fnum_sent; /* last fname sent to children */
49 int fnum_queue_end; /* last fname to be sent, based on
50 * last open call + preopen:queuelen
53 name_compare_entry *preopen_names;
56 static void preopen_helper_destroy(struct preopen_helper *c)
58 int status;
59 close(c->fd);
60 c->fd = -1;
61 kill(c->pid, SIGKILL);
62 waitpid(c->pid, &status, 0);
63 c->busy = true;
66 static void preopen_queue_run(struct preopen_state *state)
68 char *pdelimiter;
69 char delimiter;
71 pdelimiter = state->template_fname + state->number_start
72 + state->num_digits;
73 delimiter = *pdelimiter;
75 while (state->fnum_sent < state->fnum_queue_end) {
77 ssize_t written;
78 size_t to_write;
79 int helper;
81 for (helper=0; helper<state->num_helpers; helper++) {
82 if (state->helpers[helper].busy) {
83 continue;
85 break;
87 if (helper == state->num_helpers) {
88 /* everyone is busy */
89 return;
92 snprintf(state->template_fname + state->number_start,
93 state->num_digits + 1,
94 "%.*lu", state->num_digits,
95 (long unsigned int)(state->fnum_sent + 1));
96 *pdelimiter = delimiter;
98 to_write = talloc_get_size(state->template_fname);
99 written = write_data(state->helpers[helper].fd,
100 state->template_fname, to_write);
101 state->helpers[helper].busy = true;
103 if (written != to_write) {
104 preopen_helper_destroy(&state->helpers[helper]);
106 state->fnum_sent += 1;
110 static void preopen_helper_readable(struct tevent_context *ev,
111 struct tevent_fd *fde, uint16_t flags,
112 void *priv)
114 struct preopen_helper *helper = (struct preopen_helper *)priv;
115 struct preopen_state *state = helper->state;
116 ssize_t nread;
117 char c;
119 if ((flags & TEVENT_FD_READ) == 0) {
120 return;
123 nread = read(helper->fd, &c, 1);
124 if (nread <= 0) {
125 preopen_helper_destroy(helper);
126 return;
129 helper->busy = false;
131 preopen_queue_run(state);
134 static int preopen_helpers_destructor(struct preopen_state *c)
136 int i;
138 for (i=0; i<c->num_helpers; i++) {
139 if (c->helpers[i].fd == -1) {
140 continue;
142 preopen_helper_destroy(&c->helpers[i]);
145 return 0;
148 static bool preopen_helper_open_one(int sock_fd, char **pnamebuf,
149 size_t to_read, void *filebuf)
151 char *namebuf = *pnamebuf;
152 ssize_t nread;
153 char c = 0;
154 int fd;
156 nread = 0;
158 while ((nread == 0) || (namebuf[nread-1] != '\0')) {
159 ssize_t thistime;
161 thistime = read(sock_fd, namebuf + nread,
162 talloc_get_size(namebuf) - nread);
163 if (thistime <= 0) {
164 return false;
167 nread += thistime;
169 if (nread == talloc_get_size(namebuf)) {
170 namebuf = talloc_realloc(
171 NULL, namebuf, char,
172 talloc_get_size(namebuf) * 2);
173 if (namebuf == NULL) {
174 return false;
176 *pnamebuf = namebuf;
180 fd = open(namebuf, O_RDONLY);
181 if (fd == -1) {
182 goto done;
184 nread = read(fd, filebuf, to_read);
185 close(fd);
187 done:
188 (void)write(sock_fd, &c, 1);
189 return true;
192 static bool preopen_helper(int fd, size_t to_read)
194 char *namebuf;
195 void *readbuf;
197 namebuf = talloc_array(NULL, char, 1024);
198 if (namebuf == NULL) {
199 return false;
202 readbuf = talloc_size(NULL, to_read);
203 if (readbuf == NULL) {
204 TALLOC_FREE(namebuf);
205 return false;
208 while (preopen_helper_open_one(fd, &namebuf, to_read, readbuf)) {
212 TALLOC_FREE(readbuf);
213 TALLOC_FREE(namebuf);
214 return false;
217 static NTSTATUS preopen_init_helper(struct preopen_helper *h)
219 int fdpair[2];
220 NTSTATUS status;
222 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) == -1) {
223 status = map_nt_error_from_unix(errno);
224 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno)));
225 return status;
228 h->pid = fork();
230 if (h->pid == -1) {
231 return map_nt_error_from_unix(errno);
234 if (h->pid == 0) {
235 close(fdpair[0]);
236 preopen_helper(fdpair[1], h->state->to_read);
237 exit(0);
239 close(fdpair[1]);
240 h->fd = fdpair[0];
241 h->fde = tevent_add_fd(server_event_context(), h->state, h->fd,
242 TEVENT_FD_READ, preopen_helper_readable, h);
243 if (h->fde == NULL) {
244 close(h->fd);
245 h->fd = -1;
246 return NT_STATUS_NO_MEMORY;
248 h->busy = false;
249 return NT_STATUS_OK;
252 static NTSTATUS preopen_init_helpers(TALLOC_CTX *mem_ctx, size_t to_read,
253 int num_helpers, int queue_max,
254 struct preopen_state **presult)
256 struct preopen_state *result;
257 int i;
259 result = talloc(mem_ctx, struct preopen_state);
260 if (result == NULL) {
261 return NT_STATUS_NO_MEMORY;
264 result->num_helpers = num_helpers;
265 result->helpers = talloc_array(result, struct preopen_helper,
266 num_helpers);
267 if (result->helpers == NULL) {
268 TALLOC_FREE(result);
269 return NT_STATUS_NO_MEMORY;
272 result->to_read = to_read;
273 result->queue_max = queue_max;
274 result->template_fname = NULL;
275 result->fnum_sent = 0;
277 for (i=0; i<num_helpers; i++) {
278 result->helpers[i].state = result;
279 result->helpers[i].fd = -1;
282 talloc_set_destructor(result, preopen_helpers_destructor);
284 for (i=0; i<num_helpers; i++) {
285 preopen_init_helper(&result->helpers[i]);
288 *presult = result;
289 return NT_STATUS_OK;
292 static void preopen_free_helpers(void **ptr)
294 TALLOC_FREE(*ptr);
297 static struct preopen_state *preopen_state_get(vfs_handle_struct *handle)
299 struct preopen_state *state;
300 NTSTATUS status;
301 const char *namelist;
303 if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
304 SMB_VFS_HANDLE_GET_DATA(handle, state, struct preopen_state,
305 return NULL);
306 return state;
309 namelist = lp_parm_const_string(SNUM(handle->conn), "preopen", "names",
310 NULL);
312 if (namelist == NULL) {
313 return NULL;
316 status = preopen_init_helpers(
317 NULL,
318 lp_parm_int(SNUM(handle->conn), "preopen", "num_bytes", 1),
319 lp_parm_int(SNUM(handle->conn), "preopen", "helpers", 1),
320 lp_parm_int(SNUM(handle->conn), "preopen", "queuelen", 10),
321 &state);
322 if (!NT_STATUS_IS_OK(status)) {
323 return NULL;
326 set_namearray(&state->preopen_names, namelist);
328 if (state->preopen_names == NULL) {
329 TALLOC_FREE(state);
330 return NULL;
333 if (!SMB_VFS_HANDLE_TEST_DATA(handle)) {
334 SMB_VFS_HANDLE_SET_DATA(handle, state, preopen_free_helpers,
335 struct preopen_state, return NULL);
338 return state;
341 static bool preopen_parse_fname(const char *fname, unsigned long *pnum,
342 size_t *pstart_idx, int *pnum_digits)
344 const char *p;
345 char *q = NULL;
346 unsigned long num;
348 p = strrchr_m(fname, '/');
349 if (p == NULL) {
350 p = fname;
353 p += 1;
354 while (p[0] != '\0') {
355 if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])) {
356 break;
358 p += 1;
360 if (*p == '\0') {
361 /* no digits around */
362 return false;
365 num = strtoul(p, (char **)&q, 10);
367 if (num+1 < num) {
368 /* overflow */
369 return false;
372 *pnum = num;
373 *pstart_idx = (p - fname);
374 *pnum_digits = (q - p);
375 return true;
378 static int preopen_open(vfs_handle_struct *handle,
379 struct smb_filename *smb_fname, files_struct *fsp,
380 int flags, mode_t mode)
382 struct preopen_state *state;
383 int res;
384 unsigned long num;
386 DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname)));
388 state = preopen_state_get(handle);
389 if (state == NULL) {
390 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
393 res = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
394 if (res == -1) {
395 return -1;
398 if (flags != O_RDONLY) {
399 return res;
402 if (!is_in_path(smb_fname->base_name, state->preopen_names, true)) {
403 DEBUG(10, ("%s does not match the preopen:names list\n",
404 smb_fname_str_dbg(smb_fname)));
405 return res;
408 TALLOC_FREE(state->template_fname);
409 state->template_fname = talloc_asprintf(
410 state, "%s/%s", fsp->conn->cwd, smb_fname->base_name);
412 if (state->template_fname == NULL) {
413 return res;
416 if (!preopen_parse_fname(state->template_fname, &num,
417 &state->number_start, &state->num_digits)) {
418 TALLOC_FREE(state->template_fname);
419 return res;
422 if (num > state->fnum_sent) {
424 * Helpers were too slow, there's no point in reading
425 * files in helpers that we already read in the
426 * parent.
428 state->fnum_sent = num;
431 if ((state->fnum_queue_end != 0) /* Something was started earlier */
432 && (num < (state->fnum_queue_end - state->queue_max))) {
434 * "num" is before the queue we announced. This means
435 * a new run is started.
437 state->fnum_sent = num;
440 state->fnum_queue_end = num + state->queue_max;
442 preopen_queue_run(state);
444 return res;
447 static struct vfs_fn_pointers vfs_preopen_fns = {
448 .open_fn = preopen_open
451 NTSTATUS vfs_preopen_init(void);
452 NTSTATUS vfs_preopen_init(void)
454 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
455 "preopen", &vfs_preopen_fns);