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"
27 struct preopen_helper
{
28 struct preopen_state
*state
;
35 struct preopen_state
{
37 struct preopen_helper
*helpers
;
39 size_t to_read
; /* How many bytes to read in children? */
42 char *template_fname
; /* Filename to be sent to children */
43 size_t number_start
; /* start offset into "template_fname" */
44 int num_digits
; /* How many digits is the number long? */
46 int fnum_sent
; /* last fname sent to children */
48 int fnum_queue_end
; /* last fname to be sent, based on
49 * last open call + preopen:queuelen
52 name_compare_entry
*preopen_names
;
55 static void preopen_helper_destroy(struct preopen_helper
*c
)
60 kill(c
->pid
, SIGKILL
);
61 waitpid(c
->pid
, &status
, 0);
65 static void preopen_queue_run(struct preopen_state
*state
)
70 pdelimiter
= state
->template_fname
+ state
->number_start
72 delimiter
= *pdelimiter
;
74 while (state
->fnum_sent
< state
->fnum_queue_end
) {
80 for (helper
=0; helper
<state
->num_helpers
; helper
++) {
81 if (state
->helpers
[helper
].busy
) {
86 if (helper
== state
->num_helpers
) {
87 /* everyone is busy */
91 snprintf(state
->template_fname
+ state
->number_start
,
92 state
->num_digits
+ 1,
93 "%.*lu", state
->num_digits
,
94 (long unsigned int)(state
->fnum_sent
+ 1));
95 *pdelimiter
= delimiter
;
97 to_write
= talloc_get_size(state
->template_fname
);
98 written
= write_data(state
->helpers
[helper
].fd
,
99 state
->template_fname
, to_write
);
100 state
->helpers
[helper
].busy
= true;
102 if (written
!= to_write
) {
103 preopen_helper_destroy(&state
->helpers
[helper
]);
105 state
->fnum_sent
+= 1;
109 static void preopen_helper_readable(struct event_context
*ev
,
110 struct fd_event
*fde
, uint16_t flags
,
113 struct preopen_helper
*helper
= (struct preopen_helper
*)priv
;
114 struct preopen_state
*state
= helper
->state
;
118 if ((flags
& EVENT_FD_READ
) == 0) {
122 nread
= read(helper
->fd
, &c
, 1);
124 preopen_helper_destroy(helper
);
128 helper
->busy
= false;
130 preopen_queue_run(state
);
133 static int preopen_helpers_destructor(struct preopen_state
*c
)
137 for (i
=0; i
<c
->num_helpers
; i
++) {
138 if (c
->helpers
[i
].fd
== -1) {
141 preopen_helper_destroy(&c
->helpers
[i
]);
147 static bool preopen_helper_open_one(int sock_fd
, char **pnamebuf
,
148 size_t to_read
, void *filebuf
)
150 char *namebuf
= *pnamebuf
;
151 ssize_t nwritten
, nread
;
157 while ((nread
== 0) || (namebuf
[nread
-1] != '\0')) {
160 thistime
= read(sock_fd
, namebuf
+ nread
,
161 talloc_get_size(namebuf
) - nread
);
168 if (nread
== talloc_get_size(namebuf
)) {
169 namebuf
= TALLOC_REALLOC_ARRAY(
171 talloc_get_size(namebuf
) * 2);
172 if (namebuf
== NULL
) {
179 fd
= open(namebuf
, O_RDONLY
);
183 nread
= read(fd
, filebuf
, to_read
);
187 nwritten
= write(sock_fd
, &c
, 1);
191 static bool preopen_helper(int fd
, size_t to_read
)
196 namebuf
= TALLOC_ARRAY(NULL
, char, 1024);
197 if (namebuf
== NULL
) {
201 readbuf
= talloc_size(NULL
, to_read
);
202 if (readbuf
== NULL
) {
203 TALLOC_FREE(namebuf
);
207 while (preopen_helper_open_one(fd
, &namebuf
, to_read
, readbuf
)) {
211 TALLOC_FREE(readbuf
);
212 TALLOC_FREE(namebuf
);
216 static NTSTATUS
preopen_init_helper(struct preopen_helper
*h
)
221 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, fdpair
) == -1) {
222 status
= map_nt_error_from_unix(errno
);
223 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno
)));
230 return map_nt_error_from_unix(errno
);
235 preopen_helper(fdpair
[1], h
->state
->to_read
);
240 h
->fde
= event_add_fd(smbd_event_context(), h
->state
, h
->fd
,
241 EVENT_FD_READ
, preopen_helper_readable
, h
);
242 if (h
->fde
== NULL
) {
245 return NT_STATUS_NO_MEMORY
;
251 static NTSTATUS
preopen_init_helpers(TALLOC_CTX
*mem_ctx
, size_t to_read
,
252 int num_helpers
, int queue_max
,
253 struct preopen_state
**presult
)
255 struct preopen_state
*result
;
258 result
= talloc(mem_ctx
, struct preopen_state
);
259 if (result
== NULL
) {
260 return NT_STATUS_NO_MEMORY
;
263 result
->num_helpers
= num_helpers
;
264 result
->helpers
= TALLOC_ARRAY(result
, struct preopen_helper
,
266 if (result
->helpers
== NULL
) {
268 return NT_STATUS_NO_MEMORY
;
271 result
->to_read
= to_read
;
272 result
->queue_max
= queue_max
;
273 result
->template_fname
= NULL
;
274 result
->fnum_sent
= 0;
276 for (i
=0; i
<num_helpers
; i
++) {
277 result
->helpers
[i
].state
= result
;
278 result
->helpers
[i
].fd
= -1;
281 talloc_set_destructor(result
, preopen_helpers_destructor
);
283 for (i
=0; i
<num_helpers
; i
++) {
284 preopen_init_helper(&result
->helpers
[i
]);
291 static void preopen_free_helpers(void **ptr
)
296 static struct preopen_state
*preopen_state_get(vfs_handle_struct
*handle
)
298 struct preopen_state
*state
;
300 const char *namelist
;
302 if (SMB_VFS_HANDLE_TEST_DATA(handle
)) {
303 SMB_VFS_HANDLE_GET_DATA(handle
, state
, struct preopen_state
,
308 namelist
= lp_parm_const_string(SNUM(handle
->conn
), "preopen", "names",
311 if (namelist
== NULL
) {
315 status
= preopen_init_helpers(
317 lp_parm_int(SNUM(handle
->conn
), "preopen", "num_bytes", 1),
318 lp_parm_int(SNUM(handle
->conn
), "preopen", "helpers", 1),
319 lp_parm_int(SNUM(handle
->conn
), "preopen", "queuelen", 10),
321 if (!NT_STATUS_IS_OK(status
)) {
325 set_namearray(&state
->preopen_names
, namelist
);
327 if (state
->preopen_names
== NULL
) {
332 if (!SMB_VFS_HANDLE_TEST_DATA(handle
)) {
333 SMB_VFS_HANDLE_SET_DATA(handle
, state
, preopen_free_helpers
,
334 struct preopen_state
, return NULL
);
340 static bool preopen_parse_fname(const char *fname
, unsigned long *pnum
,
341 size_t *pstart_idx
, int *pnum_digits
)
346 p
= strrchr_m(fname
, '/');
352 while (p
[0] != '\0') {
353 if (isdigit(p
[0]) && isdigit(p
[1]) && isdigit(p
[2])) {
359 /* no digits around */
363 num
= strtoul(p
, (char **)&q
, 10);
371 *pstart_idx
= (p
- fname
);
372 *pnum_digits
= (q
- p
);
376 static int preopen_open(vfs_handle_struct
*handle
,
377 struct smb_filename
*smb_fname
, files_struct
*fsp
,
378 int flags
, mode_t mode
)
380 struct preopen_state
*state
;
384 DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname
)));
386 state
= preopen_state_get(handle
);
388 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
391 res
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
396 if (flags
!= O_RDONLY
) {
400 if (!is_in_path(smb_fname
->base_name
, state
->preopen_names
, true)) {
401 DEBUG(10, ("%s does not match the preopen:names list\n",
402 smb_fname_str_dbg(smb_fname
)));
406 TALLOC_FREE(state
->template_fname
);
407 state
->template_fname
= talloc_asprintf(
408 state
, "%s/%s", fsp
->conn
->connectpath
, smb_fname
->base_name
);
410 if (state
->template_fname
== NULL
) {
414 if (!preopen_parse_fname(state
->template_fname
, &num
,
415 &state
->number_start
, &state
->num_digits
)) {
416 TALLOC_FREE(state
->template_fname
);
420 if (num
> state
->fnum_sent
) {
422 * Helpers were too slow, there's no point in reading
423 * files in helpers that we already read in the
426 state
->fnum_sent
= num
;
429 if ((state
->fnum_queue_end
!= 0) /* Something was started earlier */
430 && (num
< (state
->fnum_queue_end
- state
->queue_max
))) {
432 * "num" is before the queue we announced. This means
433 * a new run is started.
435 state
->fnum_sent
= num
;
438 state
->fnum_queue_end
= num
+ state
->queue_max
;
440 preopen_queue_run(state
);
445 static struct vfs_fn_pointers vfs_preopen_fns
= {
446 .open_fn
= preopen_open
449 NTSTATUS
vfs_preopen_init(void);
450 NTSTATUS
vfs_preopen_init(void)
452 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
453 "preopen", &vfs_preopen_fns
);