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.
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"
29 struct preopen_helper
{
30 struct preopen_state
*state
;
31 struct tevent_fd
*fde
;
37 struct preopen_state
{
39 struct preopen_helper
*helpers
;
41 size_t to_read
; /* How many bytes to read in children? */
44 char *template_fname
; /* Filename to be sent to children */
45 size_t number_start
; /* start offset into "template_fname" */
46 int num_digits
; /* How many digits is the number long? */
48 int fnum_sent
; /* last fname sent to children */
50 int fnum_queue_end
; /* last fname to be sent, based on
51 * last open call + preopen:queuelen
54 name_compare_entry
*preopen_names
;
57 static void preopen_helper_destroy(struct preopen_helper
*c
)
63 kill(c
->pid
, SIGKILL
);
64 waitpid(c
->pid
, &status
, 0);
68 static void preopen_queue_run(struct preopen_state
*state
)
73 pdelimiter
= state
->template_fname
+ state
->number_start
75 delimiter
= *pdelimiter
;
77 while (state
->fnum_sent
< state
->fnum_queue_end
) {
83 for (helper
=0; helper
<state
->num_helpers
; helper
++) {
84 if (state
->helpers
[helper
].busy
) {
89 if (helper
== state
->num_helpers
) {
90 /* everyone is busy */
94 snprintf(state
->template_fname
+ state
->number_start
,
95 state
->num_digits
+ 1,
96 "%.*lu", state
->num_digits
,
97 (long unsigned int)(state
->fnum_sent
+ 1));
98 *pdelimiter
= delimiter
;
100 to_write
= talloc_get_size(state
->template_fname
);
101 written
= write_data(state
->helpers
[helper
].fd
,
102 state
->template_fname
, to_write
);
103 state
->helpers
[helper
].busy
= true;
105 if (written
!= to_write
) {
106 preopen_helper_destroy(&state
->helpers
[helper
]);
108 state
->fnum_sent
+= 1;
112 static void preopen_helper_readable(struct tevent_context
*ev
,
113 struct tevent_fd
*fde
, uint16_t flags
,
116 struct preopen_helper
*helper
= (struct preopen_helper
*)priv
;
117 struct preopen_state
*state
= helper
->state
;
121 if ((flags
& TEVENT_FD_READ
) == 0) {
125 nread
= read(helper
->fd
, &c
, 1);
127 preopen_helper_destroy(helper
);
131 helper
->busy
= false;
133 preopen_queue_run(state
);
136 static int preopen_helpers_destructor(struct preopen_state
*c
)
140 for (i
=0; i
<c
->num_helpers
; i
++) {
141 if (c
->helpers
[i
].fd
== -1) {
144 preopen_helper_destroy(&c
->helpers
[i
]);
150 static bool preopen_helper_open_one(int sock_fd
, char **pnamebuf
,
151 size_t to_read
, void *filebuf
)
153 char *namebuf
= *pnamebuf
;
163 thistime
= read(sock_fd
, namebuf
+ nread
,
164 talloc_get_size(namebuf
) - nread
);
171 if (nread
== talloc_get_size(namebuf
)) {
172 namebuf
= talloc_realloc(
174 talloc_get_size(namebuf
) * 2);
175 if (namebuf
== NULL
) {
180 } while (namebuf
[nread
- 1] != '\0');
182 fd
= open(namebuf
, O_RDONLY
);
186 nread
= read(fd
, filebuf
, to_read
);
190 sys_write_v(sock_fd
, &c
, 1);
194 static bool preopen_helper(int fd
, size_t to_read
)
199 namebuf
= talloc_array(NULL
, char, 1024);
200 if (namebuf
== NULL
) {
204 readbuf
= talloc_size(NULL
, to_read
);
205 if (readbuf
== NULL
) {
206 TALLOC_FREE(namebuf
);
210 while (preopen_helper_open_one(fd
, &namebuf
, to_read
, readbuf
)) {
214 TALLOC_FREE(readbuf
);
215 TALLOC_FREE(namebuf
);
219 static NTSTATUS
preopen_init_helper(struct preopen_helper
*h
)
224 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, fdpair
) == -1) {
225 status
= map_nt_error_from_unix(errno
);
226 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno
)));
233 return map_nt_error_from_unix(errno
);
238 preopen_helper(fdpair
[1], h
->state
->to_read
);
243 h
->fde
= tevent_add_fd(global_event_context(), h
->state
, h
->fd
,
244 TEVENT_FD_READ
, preopen_helper_readable
, h
);
245 if (h
->fde
== NULL
) {
248 return NT_STATUS_NO_MEMORY
;
254 static NTSTATUS
preopen_init_helpers(TALLOC_CTX
*mem_ctx
, size_t to_read
,
255 int num_helpers
, int queue_max
,
256 struct preopen_state
**presult
)
258 struct preopen_state
*result
;
261 result
= talloc(mem_ctx
, struct preopen_state
);
262 if (result
== NULL
) {
263 return NT_STATUS_NO_MEMORY
;
266 result
->num_helpers
= num_helpers
;
267 result
->helpers
= talloc_array(result
, struct preopen_helper
,
269 if (result
->helpers
== NULL
) {
271 return NT_STATUS_NO_MEMORY
;
274 result
->to_read
= to_read
;
275 result
->queue_max
= queue_max
;
276 result
->template_fname
= NULL
;
277 result
->fnum_sent
= 0;
278 result
->fnum_queue_end
= 0;
280 for (i
=0; i
<num_helpers
; i
++) {
281 result
->helpers
[i
].state
= result
;
282 result
->helpers
[i
].fd
= -1;
285 talloc_set_destructor(result
, preopen_helpers_destructor
);
287 for (i
=0; i
<num_helpers
; i
++) {
288 preopen_init_helper(&result
->helpers
[i
]);
295 static void preopen_free_helpers(void **ptr
)
300 static struct preopen_state
*preopen_state_get(vfs_handle_struct
*handle
)
302 struct preopen_state
*state
;
304 const char *namelist
;
306 if (SMB_VFS_HANDLE_TEST_DATA(handle
)) {
307 SMB_VFS_HANDLE_GET_DATA(handle
, state
, struct preopen_state
,
312 namelist
= lp_parm_const_string(SNUM(handle
->conn
), "preopen", "names",
315 if (namelist
== NULL
) {
319 status
= preopen_init_helpers(
321 lp_parm_int(SNUM(handle
->conn
), "preopen", "num_bytes", 1),
322 lp_parm_int(SNUM(handle
->conn
), "preopen", "helpers", 1),
323 lp_parm_int(SNUM(handle
->conn
), "preopen", "queuelen", 10),
325 if (!NT_STATUS_IS_OK(status
)) {
329 set_namearray(&state
->preopen_names
, namelist
);
331 if (state
->preopen_names
== NULL
) {
336 if (!SMB_VFS_HANDLE_TEST_DATA(handle
)) {
337 SMB_VFS_HANDLE_SET_DATA(handle
, state
, preopen_free_helpers
,
338 struct preopen_state
, return NULL
);
344 static bool preopen_parse_fname(const char *fname
, unsigned long *pnum
,
345 size_t *pstart_idx
, int *pnum_digits
)
352 p
= strrchr_m(fname
, '/');
358 while (p
[0] != '\0') {
359 if (isdigit(p
[0]) && isdigit(p
[1]) && isdigit(p
[2])) {
365 /* no digits around */
369 num
= smb_strtoul(p
, (char **)&q
, 10, &error
, SMB_STR_STANDARD
);
380 *pstart_idx
= (p
- fname
);
381 *pnum_digits
= (q
- p
);
385 static int preopen_open(vfs_handle_struct
*handle
,
386 struct smb_filename
*smb_fname
, files_struct
*fsp
,
387 int flags
, mode_t mode
)
389 struct preopen_state
*state
;
393 DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname
)));
395 state
= preopen_state_get(handle
);
397 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
400 res
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
405 if ((flags
& O_ACCMODE
) != O_RDONLY
) {
409 if (!is_in_path(smb_fname
->base_name
, state
->preopen_names
, true)) {
410 DEBUG(10, ("%s does not match the preopen:names list\n",
411 smb_fname_str_dbg(smb_fname
)));
415 TALLOC_FREE(state
->template_fname
);
416 state
->template_fname
= talloc_asprintf(
418 fsp
->conn
->cwd_fsp
->fsp_name
->base_name
, smb_fname
->base_name
);
420 if (state
->template_fname
== NULL
) {
424 if (!preopen_parse_fname(state
->template_fname
, &num
,
425 &state
->number_start
, &state
->num_digits
)) {
426 TALLOC_FREE(state
->template_fname
);
430 if (num
> state
->fnum_sent
) {
432 * Helpers were too slow, there's no point in reading
433 * files in helpers that we already read in the
436 state
->fnum_sent
= num
;
439 if ((state
->fnum_queue_end
!= 0) /* Something was started earlier */
440 && (num
< (state
->fnum_queue_end
- state
->queue_max
))) {
442 * "num" is before the queue we announced. This means
443 * a new run is started.
445 state
->fnum_sent
= num
;
448 state
->fnum_queue_end
= num
+ state
->queue_max
;
450 preopen_queue_run(state
);
455 static struct vfs_fn_pointers vfs_preopen_fns
= {
456 .open_fn
= preopen_open
460 NTSTATUS
vfs_preopen_init(TALLOC_CTX
*ctx
)
462 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
463 "preopen", &vfs_preopen_fns
);