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.
25 struct preopen_helper
{
26 struct preopen_state
*state
;
33 struct preopen_state
{
35 struct preopen_helper
*helpers
;
37 size_t to_read
; /* How many bytes to read in children? */
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? */
44 int fnum_sent
; /* last fname sent to children */
46 int fnum_queue_end
; /* last fname to be sent, based on
47 * last open call + preopen:queuelen
50 name_compare_entry
*preopen_names
;
53 static void preopen_helper_destroy(struct preopen_helper
*c
)
58 kill(c
->pid
, SIGKILL
);
59 waitpid(c
->pid
, &status
, 0);
63 static void preopen_queue_run(struct preopen_state
*state
)
68 pdelimiter
= state
->template_fname
+ state
->number_start
70 delimiter
= *pdelimiter
;
72 while (state
->fnum_sent
< state
->fnum_queue_end
) {
78 for (helper
=0; helper
<state
->num_helpers
; helper
++) {
79 if (state
->helpers
[helper
].busy
) {
84 if (helper
== state
->num_helpers
) {
85 /* everyone is busy */
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
;
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;
100 if (written
!= to_write
) {
101 preopen_helper_destroy(&state
->helpers
[helper
]);
103 state
->fnum_sent
+= 1;
107 static void preopen_helper_readable(struct event_context
*ev
,
108 struct fd_event
*fde
, uint16_t flags
,
111 struct preopen_helper
*helper
= (struct preopen_helper
*)priv
;
112 struct preopen_state
*state
= helper
->state
;
116 if ((flags
& EVENT_FD_READ
) == 0) {
120 nread
= read(helper
->fd
, &c
, 1);
122 preopen_helper_destroy(helper
);
126 helper
->busy
= false;
128 preopen_queue_run(state
);
131 static int preopen_helpers_destructor(struct preopen_state
*c
)
135 for (i
=0; i
<c
->num_helpers
; i
++) {
136 if (c
->helpers
[i
].fd
== -1) {
139 preopen_helper_destroy(&c
->helpers
[i
]);
145 static bool preopen_helper_open_one(int sock_fd
, char **pnamebuf
,
146 size_t to_read
, void *filebuf
)
148 char *namebuf
= *pnamebuf
;
149 ssize_t nwritten
, nread
;
155 while ((nread
== 0) || (namebuf
[nread
-1] != '\0')) {
158 thistime
= read(sock_fd
, namebuf
+ nread
,
159 talloc_get_size(namebuf
) - nread
);
166 if (nread
== talloc_get_size(namebuf
)) {
167 namebuf
= TALLOC_REALLOC_ARRAY(
169 talloc_get_size(namebuf
) * 2);
170 if (namebuf
== NULL
) {
177 fd
= open(namebuf
, O_RDONLY
);
181 nread
= read(fd
, filebuf
, to_read
);
185 nwritten
= write(sock_fd
, &c
, 1);
189 static bool preopen_helper(int fd
, size_t to_read
)
194 namebuf
= TALLOC_ARRAY(NULL
, char, 1024);
195 if (namebuf
== NULL
) {
199 readbuf
= talloc_size(NULL
, to_read
);
200 if (readbuf
== NULL
) {
201 TALLOC_FREE(namebuf
);
205 while (preopen_helper_open_one(fd
, &namebuf
, to_read
, readbuf
)) {
209 TALLOC_FREE(readbuf
);
210 TALLOC_FREE(namebuf
);
214 static NTSTATUS
preopen_init_helper(struct preopen_helper
*h
)
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
)));
228 return map_nt_error_from_unix(errno
);
233 preopen_helper(fdpair
[1], h
->state
->to_read
);
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
) {
243 return NT_STATUS_NO_MEMORY
;
249 static NTSTATUS
preopen_init_helpers(TALLOC_CTX
*mem_ctx
, size_t to_read
,
250 int num_helpers
, int queue_max
,
251 struct preopen_state
**presult
)
253 struct preopen_state
*result
;
256 result
= talloc(mem_ctx
, struct preopen_state
);
257 if (result
== NULL
) {
258 return NT_STATUS_NO_MEMORY
;
261 result
->num_helpers
= num_helpers
;
262 result
->helpers
= TALLOC_ARRAY(result
, struct preopen_helper
,
264 if (result
->helpers
== NULL
) {
266 return NT_STATUS_NO_MEMORY
;
269 result
->to_read
= to_read
;
270 result
->queue_max
= queue_max
;
271 result
->template_fname
= NULL
;
272 result
->fnum_sent
= 0;
274 for (i
=0; i
<num_helpers
; i
++) {
275 result
->helpers
[i
].state
= result
;
276 result
->helpers
[i
].fd
= -1;
279 talloc_set_destructor(result
, preopen_helpers_destructor
);
281 for (i
=0; i
<num_helpers
; i
++) {
282 preopen_init_helper(&result
->helpers
[i
]);
289 static void preopen_free_helpers(void **ptr
)
294 static struct preopen_state
*preopen_state_get(vfs_handle_struct
*handle
)
296 struct preopen_state
*state
;
298 const char *namelist
;
300 if (SMB_VFS_HANDLE_TEST_DATA(handle
)) {
301 SMB_VFS_HANDLE_GET_DATA(handle
, state
, struct preopen_state
,
306 namelist
= lp_parm_const_string(SNUM(handle
->conn
), "preopen", "names",
309 if (namelist
== NULL
) {
313 status
= preopen_init_helpers(
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),
319 if (!NT_STATUS_IS_OK(status
)) {
323 set_namearray(&state
->preopen_names
, (char *)namelist
);
325 if (state
->preopen_names
== NULL
) {
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
);
338 static bool preopen_parse_fname(const char *fname
, unsigned long *pnum
,
339 size_t *pstart_idx
, int *pnum_digits
)
344 p
= strrchr_m(fname
, '/');
350 while (p
[0] != '\0') {
351 if (isdigit(p
[0]) && isdigit(p
[1]) && isdigit(p
[2])) {
357 /* no digits around */
361 num
= strtoul(p
, (char **)&q
, 10);
369 *pstart_idx
= (p
- fname
);
370 *pnum_digits
= (q
- p
);
374 static int preopen_open(vfs_handle_struct
*handle
, const char *fname
,
375 files_struct
*fsp
, int flags
, mode_t mode
)
377 struct preopen_state
*state
;
381 DEBUG(10, ("preopen_open called on %s\n", fname
));
383 state
= preopen_state_get(handle
);
385 return SMB_VFS_NEXT_OPEN(handle
, fname
, fsp
, flags
, mode
);
388 res
= SMB_VFS_NEXT_OPEN(handle
, fname
, fsp
, flags
, mode
);
393 if (flags
!= O_RDONLY
) {
397 if (!is_in_path(fname
, state
->preopen_names
, true)) {
398 DEBUG(10, ("%s does not match the preopen:names list\n",
403 TALLOC_FREE(state
->template_fname
);
404 state
->template_fname
= talloc_asprintf(
405 state
, "%s/%s", fsp
->conn
->connectpath
, fname
);
407 if (state
->template_fname
== NULL
) {
411 if (!preopen_parse_fname(state
->template_fname
, &num
,
412 &state
->number_start
, &state
->num_digits
)) {
413 TALLOC_FREE(state
->template_fname
);
417 if (num
> state
->fnum_sent
) {
419 * Helpers were too slow, there's no point in reading
420 * files in helpers that we already read in the
423 state
->fnum_sent
= num
;
426 if ((state
->fnum_queue_end
!= 0) /* Something was started earlier */
427 && (num
< (state
->fnum_queue_end
- state
->queue_max
))) {
429 * "num" is before the queue we announced. This means
430 * a new run is started.
432 state
->fnum_sent
= num
;
435 state
->fnum_queue_end
= num
+ state
->queue_max
;
437 preopen_queue_run(state
);
442 /* VFS operations structure */
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
,
451 NTSTATUS
vfs_preopen_init(void);
452 NTSTATUS
vfs_preopen_init(void)
454 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
455 "preopen", preopen_ops
);