• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source3/modules/
1/*
2 * Force a readahead of files by opening them and reading the first bytes
3 *
4 * Copyright (C) Volker Lendecke 2008
5 *
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.
10 *
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.
15 *
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.
19 */
20
21#include "includes.h"
22
23struct preopen_state;
24
25struct preopen_helper {
26	struct preopen_state *state;
27	struct fd_event *fde;
28	pid_t pid;
29	int fd;
30	bool busy;
31};
32
33struct preopen_state {
34	int num_helpers;
35	struct preopen_helper *helpers;
36
37	size_t to_read;		/* How many bytes to read in children? */
38	int queue_max;
39
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? */
43
44	int fnum_sent;		/* last fname sent to children */
45
46	int fnum_queue_end;	/* last fname to be sent, based on
47				 * last open call + preopen:queuelen
48				 */
49
50	name_compare_entry *preopen_names;
51};
52
53static void preopen_helper_destroy(struct preopen_helper *c)
54{
55	int status;
56	close(c->fd);
57	c->fd = -1;
58	kill(c->pid, SIGKILL);
59	waitpid(c->pid, &status, 0);
60	c->busy = true;
61}
62
63static void preopen_queue_run(struct preopen_state *state)
64{
65	char *pdelimiter;
66	char delimiter;
67
68	pdelimiter = state->template_fname + state->number_start
69		+ state->num_digits;
70	delimiter = *pdelimiter;
71
72	while (state->fnum_sent < state->fnum_queue_end) {
73
74		ssize_t written;
75		size_t to_write;
76		int helper;
77
78		for (helper=0; helper<state->num_helpers; helper++) {
79			if (state->helpers[helper].busy) {
80				continue;
81			}
82			break;
83		}
84		if (helper == state->num_helpers) {
85			/* everyone is busy */
86			return;
87		}
88
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;
94
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;
99
100		if (written != to_write) {
101			preopen_helper_destroy(&state->helpers[helper]);
102		}
103		state->fnum_sent += 1;
104	}
105}
106
107static void preopen_helper_readable(struct event_context *ev,
108				    struct fd_event *fde, uint16_t flags,
109				    void *priv)
110{
111	struct preopen_helper *helper = (struct preopen_helper *)priv;
112	struct preopen_state *state = helper->state;
113	ssize_t nread;
114	char c;
115
116	if ((flags & EVENT_FD_READ) == 0) {
117		return;
118	}
119
120	nread = read(helper->fd, &c, 1);
121	if (nread <= 0) {
122		preopen_helper_destroy(helper);
123		return;
124	}
125
126	helper->busy = false;
127
128	preopen_queue_run(state);
129}
130
131static int preopen_helpers_destructor(struct preopen_state *c)
132{
133	int i;
134
135	for (i=0; i<c->num_helpers; i++) {
136		if (c->helpers[i].fd == -1) {
137			continue;
138		}
139		preopen_helper_destroy(&c->helpers[i]);
140	}
141
142	return 0;
143}
144
145static bool preopen_helper_open_one(int sock_fd, char **pnamebuf,
146				    size_t to_read, void *filebuf)
147{
148	char *namebuf = *pnamebuf;
149	ssize_t nwritten, nread;
150	char c = 0;
151	int fd;
152
153	nread = 0;
154
155	while ((nread == 0) || (namebuf[nread-1] != '\0')) {
156		ssize_t thistime;
157
158		thistime = read(sock_fd, namebuf + nread,
159				talloc_get_size(namebuf) - nread);
160		if (thistime <= 0) {
161			return false;
162		}
163
164		nread += thistime;
165
166		if (nread == talloc_get_size(namebuf)) {
167			namebuf = TALLOC_REALLOC_ARRAY(
168				NULL, namebuf, char,
169				talloc_get_size(namebuf) * 2);
170			if (namebuf == NULL) {
171				return false;
172			}
173			*pnamebuf = namebuf;
174		}
175	}
176
177	fd = open(namebuf, O_RDONLY);
178	if (fd == -1) {
179		goto done;
180	}
181	nread = read(fd, filebuf, to_read);
182	close(fd);
183
184 done:
185	nwritten = write(sock_fd, &c, 1);
186	return true;
187}
188
189static bool preopen_helper(int fd, size_t to_read)
190{
191	char *namebuf;
192	void *readbuf;
193
194	namebuf = TALLOC_ARRAY(NULL, char, 1024);
195	if (namebuf == NULL) {
196		return false;
197	}
198
199	readbuf = talloc_size(NULL, to_read);
200	if (readbuf == NULL) {
201		TALLOC_FREE(namebuf);
202		return false;
203	}
204
205	while (preopen_helper_open_one(fd, &namebuf, to_read, readbuf)) {
206		;
207	}
208
209	TALLOC_FREE(readbuf);
210	TALLOC_FREE(namebuf);
211	return false;
212}
213
214static NTSTATUS preopen_init_helper(struct preopen_helper *h)
215{
216	int fdpair[2];
217	NTSTATUS status;
218
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)));
222		return status;
223	}
224
225	h->pid = sys_fork();
226
227	if (h->pid == -1) {
228		return map_nt_error_from_unix(errno);
229	}
230
231	if (h->pid == 0) {
232		close(fdpair[0]);
233		preopen_helper(fdpair[1], h->state->to_read);
234		exit(0);
235	}
236	close(fdpair[1]);
237	h->fd = fdpair[0];
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) {
241		close(h->fd);
242		h->fd = -1;
243		return NT_STATUS_NO_MEMORY;
244	}
245	h->busy = false;
246	return NT_STATUS_OK;
247}
248
249static NTSTATUS preopen_init_helpers(TALLOC_CTX *mem_ctx, size_t to_read,
250				     int num_helpers, int queue_max,
251				     struct preopen_state **presult)
252{
253	struct preopen_state *result;
254	int i;
255
256	result = talloc(mem_ctx, struct preopen_state);
257	if (result == NULL) {
258		return NT_STATUS_NO_MEMORY;
259	}
260
261	result->num_helpers = num_helpers;
262	result->helpers = TALLOC_ARRAY(result, struct preopen_helper,
263				       num_helpers);
264	if (result->helpers == NULL) {
265		TALLOC_FREE(result);
266		return NT_STATUS_NO_MEMORY;
267	}
268
269	result->to_read = to_read;
270	result->queue_max = queue_max;
271	result->template_fname = NULL;
272	result->fnum_sent = 0;
273
274	for (i=0; i<num_helpers; i++) {
275		result->helpers[i].state = result;
276		result->helpers[i].fd = -1;
277	}
278
279	talloc_set_destructor(result, preopen_helpers_destructor);
280
281	for (i=0; i<num_helpers; i++) {
282		preopen_init_helper(&result->helpers[i]);
283	}
284
285	*presult = result;
286	return NT_STATUS_OK;
287}
288
289static void preopen_free_helpers(void **ptr)
290{
291	TALLOC_FREE(*ptr);
292}
293
294static struct preopen_state *preopen_state_get(vfs_handle_struct *handle)
295{
296	struct preopen_state *state;
297	NTSTATUS status;
298	const char *namelist;
299
300	if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
301		SMB_VFS_HANDLE_GET_DATA(handle, state, struct preopen_state,
302					return NULL);
303		return state;
304	}
305
306	namelist = lp_parm_const_string(SNUM(handle->conn), "preopen", "names",
307					NULL);
308
309	if (namelist == NULL) {
310		return NULL;
311	}
312
313	status = preopen_init_helpers(
314		NULL,
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),
318		&state);
319	if (!NT_STATUS_IS_OK(status)) {
320		return NULL;
321	}
322
323	set_namearray(&state->preopen_names, (char *)namelist);
324
325	if (state->preopen_names == NULL) {
326		TALLOC_FREE(state);
327		return NULL;
328	}
329
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);
333	}
334
335	return state;
336}
337
338static bool preopen_parse_fname(const char *fname, unsigned long *pnum,
339				size_t *pstart_idx, int *pnum_digits)
340{
341	const char *p, *q;
342	unsigned long num;
343
344	p = strrchr_m(fname, '/');
345	if (p == NULL) {
346		p = fname;
347	}
348
349	p += 1;
350	while (p[0] != '\0') {
351		if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])) {
352			break;
353		}
354		p += 1;
355	}
356	if (*p == '\0') {
357		/* no digits around */
358		return false;
359	}
360
361	num = strtoul(p, (char **)&q, 10);
362
363	if (num+1 < num) {
364		/* overflow */
365		return false;
366	}
367
368	*pnum = num;
369	*pstart_idx = (p - fname);
370	*pnum_digits = (q - p);
371	return true;
372}
373
374static int preopen_open(vfs_handle_struct *handle,
375			struct smb_filename *smb_fname, files_struct *fsp,
376			int flags, mode_t mode)
377{
378	struct preopen_state *state;
379	int res;
380	unsigned long num;
381
382	DEBUG(10, ("preopen_open called on %s\n", smb_fname_str_dbg(smb_fname)));
383
384	state = preopen_state_get(handle);
385	if (state == NULL) {
386		return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
387	}
388
389	res = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
390	if (res == -1) {
391		return -1;
392	}
393
394	if (flags != O_RDONLY) {
395		return res;
396	}
397
398	if (!is_in_path(smb_fname->base_name, state->preopen_names, true)) {
399		DEBUG(10, ("%s does not match the preopen:names list\n",
400			   smb_fname_str_dbg(smb_fname)));
401		return res;
402	}
403
404	TALLOC_FREE(state->template_fname);
405	state->template_fname = talloc_asprintf(
406		state, "%s/%s", fsp->conn->connectpath, smb_fname->base_name);
407
408	if (state->template_fname == NULL) {
409		return res;
410	}
411
412	if (!preopen_parse_fname(state->template_fname, &num,
413				 &state->number_start, &state->num_digits)) {
414		TALLOC_FREE(state->template_fname);
415		return res;
416	}
417
418	if (num > state->fnum_sent) {
419		/*
420		 * Helpers were too slow, there's no point in reading
421		 * files in helpers that we already read in the
422		 * parent.
423		 */
424		state->fnum_sent = num;
425	}
426
427	if ((state->fnum_queue_end != 0) /* Something was started earlier */
428	    && (num < (state->fnum_queue_end - state->queue_max))) {
429		/*
430		 * "num" is before the queue we announced. This means
431		 * a new run is started.
432		 */
433		state->fnum_sent = num;
434	}
435
436	state->fnum_queue_end = num + state->queue_max;
437
438	preopen_queue_run(state);
439
440	return res;
441}
442
443static struct vfs_fn_pointers vfs_preopen_fns = {
444	.open = preopen_open
445};
446
447NTSTATUS vfs_preopen_init(void);
448NTSTATUS vfs_preopen_init(void)
449{
450	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
451				"preopen", &vfs_preopen_fns);
452}
453