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/sys_rw_data.h"
28 struct preopen_helper
{
29 struct preopen_state
*state
;
30 struct tevent_fd
*fde
;
36 struct preopen_state
{
38 struct preopen_helper
*helpers
;
40 size_t to_read
; /* How many bytes to read in children? */
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
)
61 kill(c
->pid
, SIGKILL
);
62 waitpid(c
->pid
, &status
, 0);
66 static void preopen_queue_run(struct preopen_state
*state
)
71 pdelimiter
= state
->template_fname
+ state
->number_start
73 delimiter
= *pdelimiter
;
75 while (state
->fnum_sent
< state
->fnum_queue_end
) {
81 for (helper
=0; helper
<state
->num_helpers
; helper
++) {
82 if (state
->helpers
[helper
].busy
) {
87 if (helper
== state
->num_helpers
) {
88 /* everyone is busy */
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
,
114 struct preopen_helper
*helper
= (struct preopen_helper
*)priv
;
115 struct preopen_state
*state
= helper
->state
;
119 if ((flags
& TEVENT_FD_READ
) == 0) {
123 nread
= read(helper
->fd
, &c
, 1);
125 preopen_helper_destroy(helper
);
129 helper
->busy
= false;
131 preopen_queue_run(state
);
134 static int preopen_helpers_destructor(struct preopen_state
*c
)
138 for (i
=0; i
<c
->num_helpers
; i
++) {
139 if (c
->helpers
[i
].fd
== -1) {
142 preopen_helper_destroy(&c
->helpers
[i
]);
148 static bool preopen_helper_open_one(int sock_fd
, char **pnamebuf
,
149 size_t to_read
, void *filebuf
)
151 char *namebuf
= *pnamebuf
;
158 while ((nread
== 0) || (namebuf
[nread
-1] != '\0')) {
161 thistime
= read(sock_fd
, namebuf
+ nread
,
162 talloc_get_size(namebuf
) - nread
);
169 if (nread
== talloc_get_size(namebuf
)) {
170 namebuf
= talloc_realloc(
172 talloc_get_size(namebuf
) * 2);
173 if (namebuf
== NULL
) {
180 fd
= open(namebuf
, O_RDONLY
);
184 nread
= read(fd
, filebuf
, to_read
);
188 (void)write(sock_fd
, &c
, 1);
192 static bool preopen_helper(int fd
, size_t to_read
)
197 namebuf
= talloc_array(NULL
, char, 1024);
198 if (namebuf
== NULL
) {
202 readbuf
= talloc_size(NULL
, to_read
);
203 if (readbuf
== NULL
) {
204 TALLOC_FREE(namebuf
);
208 while (preopen_helper_open_one(fd
, &namebuf
, to_read
, readbuf
)) {
212 TALLOC_FREE(readbuf
);
213 TALLOC_FREE(namebuf
);
217 static NTSTATUS
preopen_init_helper(struct preopen_helper
*h
)
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
)));
231 return map_nt_error_from_unix(errno
);
236 preopen_helper(fdpair
[1], h
->state
->to_read
);
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
) {
246 return NT_STATUS_NO_MEMORY
;
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
;
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
,
267 if (result
->helpers
== NULL
) {
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
]);
292 static void preopen_free_helpers(void **ptr
)
297 static struct preopen_state
*preopen_state_get(vfs_handle_struct
*handle
)
299 struct preopen_state
*state
;
301 const char *namelist
;
303 if (SMB_VFS_HANDLE_TEST_DATA(handle
)) {
304 SMB_VFS_HANDLE_GET_DATA(handle
, state
, struct preopen_state
,
309 namelist
= lp_parm_const_string(SNUM(handle
->conn
), "preopen", "names",
312 if (namelist
== NULL
) {
316 status
= preopen_init_helpers(
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),
322 if (!NT_STATUS_IS_OK(status
)) {
326 set_namearray(&state
->preopen_names
, namelist
);
328 if (state
->preopen_names
== 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
);
341 static bool preopen_parse_fname(const char *fname
, unsigned long *pnum
,
342 size_t *pstart_idx
, int *pnum_digits
)
348 p
= strrchr_m(fname
, '/');
354 while (p
[0] != '\0') {
355 if (isdigit(p
[0]) && isdigit(p
[1]) && isdigit(p
[2])) {
361 /* no digits around */
365 num
= strtoul(p
, (char **)&q
, 10);
373 *pstart_idx
= (p
- fname
);
374 *pnum_digits
= (q
- p
);
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
;
386 DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname
)));
388 state
= preopen_state_get(handle
);
390 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
393 res
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
398 if (flags
!= O_RDONLY
) {
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
)));
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
) {
416 if (!preopen_parse_fname(state
->template_fname
, &num
,
417 &state
->number_start
, &state
->num_digits
)) {
418 TALLOC_FREE(state
->template_fname
);
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
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
);
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
);