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
)
62 kill(c
->pid
, SIGKILL
);
63 waitpid(c
->pid
, &status
, 0);
67 static void preopen_queue_run(struct preopen_state
*state
)
72 pdelimiter
= state
->template_fname
+ state
->number_start
74 delimiter
= *pdelimiter
;
76 while (state
->fnum_sent
< state
->fnum_queue_end
) {
82 for (helper
=0; helper
<state
->num_helpers
; helper
++) {
83 if (state
->helpers
[helper
].busy
) {
88 if (helper
== state
->num_helpers
) {
89 /* everyone is busy */
93 snprintf(state
->template_fname
+ state
->number_start
,
94 state
->num_digits
+ 1,
95 "%.*lu", state
->num_digits
,
96 (long unsigned int)(state
->fnum_sent
+ 1));
97 *pdelimiter
= delimiter
;
99 to_write
= talloc_get_size(state
->template_fname
);
100 written
= write_data(state
->helpers
[helper
].fd
,
101 state
->template_fname
, to_write
);
102 state
->helpers
[helper
].busy
= true;
104 if (written
!= to_write
) {
105 preopen_helper_destroy(&state
->helpers
[helper
]);
107 state
->fnum_sent
+= 1;
111 static void preopen_helper_readable(struct tevent_context
*ev
,
112 struct tevent_fd
*fde
, uint16_t flags
,
115 struct preopen_helper
*helper
= (struct preopen_helper
*)priv
;
116 struct preopen_state
*state
= helper
->state
;
120 if ((flags
& TEVENT_FD_READ
) == 0) {
124 nread
= read(helper
->fd
, &c
, 1);
126 preopen_helper_destroy(helper
);
130 helper
->busy
= false;
132 preopen_queue_run(state
);
135 static int preopen_helpers_destructor(struct preopen_state
*c
)
139 for (i
=0; i
<c
->num_helpers
; i
++) {
140 if (c
->helpers
[i
].fd
== -1) {
143 preopen_helper_destroy(&c
->helpers
[i
]);
149 static bool preopen_helper_open_one(int sock_fd
, char **pnamebuf
,
150 size_t to_read
, void *filebuf
)
152 char *namebuf
= *pnamebuf
;
159 while ((nread
== 0) || (namebuf
[nread
-1] != '\0')) {
162 thistime
= read(sock_fd
, namebuf
+ nread
,
163 talloc_get_size(namebuf
) - nread
);
170 if (nread
== talloc_get_size(namebuf
)) {
171 namebuf
= talloc_realloc(
173 talloc_get_size(namebuf
) * 2);
174 if (namebuf
== NULL
) {
181 fd
= open(namebuf
, O_RDONLY
);
185 nread
= read(fd
, filebuf
, to_read
);
189 sys_write_v(sock_fd
, &c
, 1);
193 static bool preopen_helper(int fd
, size_t to_read
)
198 namebuf
= talloc_array(NULL
, char, 1024);
199 if (namebuf
== NULL
) {
203 readbuf
= talloc_size(NULL
, to_read
);
204 if (readbuf
== NULL
) {
205 TALLOC_FREE(namebuf
);
209 while (preopen_helper_open_one(fd
, &namebuf
, to_read
, readbuf
)) {
213 TALLOC_FREE(readbuf
);
214 TALLOC_FREE(namebuf
);
218 static NTSTATUS
preopen_init_helper(struct preopen_helper
*h
)
223 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, fdpair
) == -1) {
224 status
= map_nt_error_from_unix(errno
);
225 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno
)));
232 return map_nt_error_from_unix(errno
);
237 preopen_helper(fdpair
[1], h
->state
->to_read
);
242 h
->fde
= tevent_add_fd(server_event_context(), h
->state
, h
->fd
,
243 TEVENT_FD_READ
, preopen_helper_readable
, h
);
244 if (h
->fde
== NULL
) {
247 return NT_STATUS_NO_MEMORY
;
253 static NTSTATUS
preopen_init_helpers(TALLOC_CTX
*mem_ctx
, size_t to_read
,
254 int num_helpers
, int queue_max
,
255 struct preopen_state
**presult
)
257 struct preopen_state
*result
;
260 result
= talloc(mem_ctx
, struct preopen_state
);
261 if (result
== NULL
) {
262 return NT_STATUS_NO_MEMORY
;
265 result
->num_helpers
= num_helpers
;
266 result
->helpers
= talloc_array(result
, struct preopen_helper
,
268 if (result
->helpers
== NULL
) {
270 return NT_STATUS_NO_MEMORY
;
273 result
->to_read
= to_read
;
274 result
->queue_max
= queue_max
;
275 result
->template_fname
= NULL
;
276 result
->fnum_sent
= 0;
278 for (i
=0; i
<num_helpers
; i
++) {
279 result
->helpers
[i
].state
= result
;
280 result
->helpers
[i
].fd
= -1;
283 talloc_set_destructor(result
, preopen_helpers_destructor
);
285 for (i
=0; i
<num_helpers
; i
++) {
286 preopen_init_helper(&result
->helpers
[i
]);
293 static void preopen_free_helpers(void **ptr
)
298 static struct preopen_state
*preopen_state_get(vfs_handle_struct
*handle
)
300 struct preopen_state
*state
;
302 const char *namelist
;
304 if (SMB_VFS_HANDLE_TEST_DATA(handle
)) {
305 SMB_VFS_HANDLE_GET_DATA(handle
, state
, struct preopen_state
,
310 namelist
= lp_parm_const_string(SNUM(handle
->conn
), "preopen", "names",
313 if (namelist
== NULL
) {
317 status
= preopen_init_helpers(
319 lp_parm_int(SNUM(handle
->conn
), "preopen", "num_bytes", 1),
320 lp_parm_int(SNUM(handle
->conn
), "preopen", "helpers", 1),
321 lp_parm_int(SNUM(handle
->conn
), "preopen", "queuelen", 10),
323 if (!NT_STATUS_IS_OK(status
)) {
327 set_namearray(&state
->preopen_names
, namelist
);
329 if (state
->preopen_names
== NULL
) {
334 if (!SMB_VFS_HANDLE_TEST_DATA(handle
)) {
335 SMB_VFS_HANDLE_SET_DATA(handle
, state
, preopen_free_helpers
,
336 struct preopen_state
, return NULL
);
342 static bool preopen_parse_fname(const char *fname
, unsigned long *pnum
,
343 size_t *pstart_idx
, int *pnum_digits
)
349 p
= strrchr_m(fname
, '/');
355 while (p
[0] != '\0') {
356 if (isdigit(p
[0]) && isdigit(p
[1]) && isdigit(p
[2])) {
362 /* no digits around */
366 num
= strtoul(p
, (char **)&q
, 10);
374 *pstart_idx
= (p
- fname
);
375 *pnum_digits
= (q
- p
);
379 static int preopen_open(vfs_handle_struct
*handle
,
380 struct smb_filename
*smb_fname
, files_struct
*fsp
,
381 int flags
, mode_t mode
)
383 struct preopen_state
*state
;
387 DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname
)));
389 state
= preopen_state_get(handle
);
391 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
394 res
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
399 if (flags
!= O_RDONLY
) {
403 if (!is_in_path(smb_fname
->base_name
, state
->preopen_names
, true)) {
404 DEBUG(10, ("%s does not match the preopen:names list\n",
405 smb_fname_str_dbg(smb_fname
)));
409 TALLOC_FREE(state
->template_fname
);
410 state
->template_fname
= talloc_asprintf(
411 state
, "%s/%s", fsp
->conn
->cwd
, smb_fname
->base_name
);
413 if (state
->template_fname
== NULL
) {
417 if (!preopen_parse_fname(state
->template_fname
, &num
,
418 &state
->number_start
, &state
->num_digits
)) {
419 TALLOC_FREE(state
->template_fname
);
423 if (num
> state
->fnum_sent
) {
425 * Helpers were too slow, there's no point in reading
426 * files in helpers that we already read in the
429 state
->fnum_sent
= num
;
432 if ((state
->fnum_queue_end
!= 0) /* Something was started earlier */
433 && (num
< (state
->fnum_queue_end
- state
->queue_max
))) {
435 * "num" is before the queue we announced. This means
436 * a new run is started.
438 state
->fnum_sent
= num
;
441 state
->fnum_queue_end
= num
+ state
->queue_max
;
443 preopen_queue_run(state
);
448 static struct vfs_fn_pointers vfs_preopen_fns
= {
449 .open_fn
= preopen_open
452 NTSTATUS
vfs_preopen_init(void);
453 NTSTATUS
vfs_preopen_init(void)
455 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
456 "preopen", &vfs_preopen_fns
);