• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/ntvfs/posix/
1/*
2   Unix SMB/CIFS implementation.
3
4   Copyright (C) Andrew Tridgell 2004
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 3 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, see <http://www.gnu.org/licenses/>.
18*/
19/*
20  directory listing functions for posix backend
21*/
22
23#include "includes.h"
24#include "vfs_posix.h"
25#include "system/dir.h"
26
27#define NAME_CACHE_SIZE 100
28
29struct name_cache_entry {
30	char *name;
31	off_t offset;
32};
33
34struct pvfs_dir {
35	struct pvfs_state *pvfs;
36	bool no_wildcard;
37	char *single_name;
38	const char *pattern;
39	off_t offset;
40	DIR *dir;
41	const char *unix_path;
42	bool end_of_search;
43	struct name_cache_entry *name_cache;
44	uint32_t name_cache_index;
45};
46
47/* these three numbers are chosen to minimise the chances of a bad
48   interaction with the OS value for 'end of directory'. On IRIX
49   telldir() returns 0xFFFFFFFF at the end of a directory, and that
50   caused an infinite loop with the original values of 0,1,2
51
52   On XFS on linux telldir returns 0x7FFFFFFF at the end of a
53   directory. Thus the change from 0x80000002, as otherwise
54   0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
55*/
56#define DIR_OFFSET_DOT    0
57#define DIR_OFFSET_DOTDOT 1
58#define DIR_OFFSET_BASE   0x80000022
59
60/*
61  a special directory listing case where the pattern has no wildcard. We can just do a single stat()
62  thus avoiding the more expensive directory scan
63*/
64static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
65				      const char *pattern, struct pvfs_dir *dir)
66{
67	if (!name->exists) {
68		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
69	}
70
71	dir->pvfs = pvfs;
72	dir->no_wildcard = true;
73	dir->end_of_search = false;
74	dir->unix_path = talloc_strdup(dir, name->full_name);
75	if (!dir->unix_path) {
76		return NT_STATUS_NO_MEMORY;
77	}
78
79	dir->single_name = talloc_strdup(dir, pattern);
80	if (!dir->single_name) {
81		return NT_STATUS_NO_MEMORY;
82	}
83
84	dir->dir = NULL;
85	dir->offset = 0;
86	dir->pattern = NULL;
87
88	return NT_STATUS_OK;
89}
90
91/*
92  destroy an open search
93*/
94static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
95{
96	if (dir->dir) closedir(dir->dir);
97	return 0;
98}
99
100/*
101  start to read a directory
102
103  if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
104*/
105NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
106			 TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
107{
108	char *pattern;
109	struct pvfs_dir *dir;
110
111	(*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
112	if (*dirp == NULL) {
113		return NT_STATUS_NO_MEMORY;
114	}
115
116	dir = *dirp;
117
118	/* split the unix path into a directory + pattern */
119	pattern = strrchr(name->full_name, '/');
120	if (!pattern) {
121		/* this should not happen, as pvfs_unix_path is supposed to
122		   return an absolute path */
123		return NT_STATUS_UNSUCCESSFUL;
124	}
125
126	*pattern++ = 0;
127
128	if (!name->has_wildcard) {
129		return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
130	}
131
132	dir->unix_path = talloc_strdup(dir, name->full_name);
133	if (!dir->unix_path) {
134		return NT_STATUS_NO_MEMORY;
135	}
136
137	dir->pattern = talloc_strdup(dir, pattern);
138	if (dir->pattern == NULL) {
139		return NT_STATUS_NO_MEMORY;
140	}
141
142	dir->dir = opendir(name->full_name);
143	if (!dir->dir) {
144		return pvfs_map_errno(pvfs, errno);
145	}
146
147	dir->pvfs = pvfs;
148	dir->no_wildcard = false;
149	dir->end_of_search = false;
150	dir->offset = DIR_OFFSET_DOT;
151	dir->name_cache = talloc_zero_array(dir,
152					    struct name_cache_entry,
153					    NAME_CACHE_SIZE);
154	if (dir->name_cache == NULL) {
155		talloc_free(dir);
156		return NT_STATUS_NO_MEMORY;
157	}
158
159	talloc_set_destructor(dir, pvfs_dirlist_destructor);
160
161	return NT_STATUS_OK;
162}
163
164/*
165  add an entry to the local cache
166*/
167static void dcache_add(struct pvfs_dir *dir, const char *name)
168{
169	struct name_cache_entry *e;
170
171	dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
172	e = &dir->name_cache[dir->name_cache_index];
173
174	if (e->name) talloc_free(e->name);
175
176	e->name = talloc_strdup(dir->name_cache, name);
177	e->offset = dir->offset;
178}
179
180/*
181   return the next entry
182*/
183const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
184{
185	struct dirent *de;
186	enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
187
188	/* non-wildcard searches are easy */
189	if (dir->no_wildcard) {
190		dir->end_of_search = true;
191		if (*ofs != 0) return NULL;
192		(*ofs)++;
193		return dir->single_name;
194	}
195
196	/* . and .. are handled separately as some unix systems will
197	   not return them first in a directory, but windows client
198	   may assume that these entries always appear first */
199	if (*ofs == DIR_OFFSET_DOT) {
200		(*ofs) = DIR_OFFSET_DOTDOT;
201		dir->offset = *ofs;
202		if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
203			dcache_add(dir, ".");
204			return ".";
205		}
206	}
207
208	if (*ofs == DIR_OFFSET_DOTDOT) {
209		(*ofs) = DIR_OFFSET_BASE;
210		dir->offset = *ofs;
211		if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
212			dcache_add(dir, "..");
213			return "..";
214		}
215	}
216
217	if (*ofs == DIR_OFFSET_BASE) {
218		rewinddir(dir->dir);
219	} else if (*ofs != dir->offset) {
220		seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
221	}
222	dir->offset = *ofs;
223
224	while ((de = readdir(dir->dir))) {
225		const char *dname = de->d_name;
226
227		if (ISDOT(dname) || ISDOTDOT(dname)) {
228			continue;
229		}
230
231		if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
232			char *short_name = pvfs_short_name_component(dir->pvfs, dname);
233			if (short_name == NULL ||
234			    ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
235				talloc_free(short_name);
236				continue;
237			}
238			talloc_free(short_name);
239		}
240
241		dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
242		(*ofs) = dir->offset;
243
244		dcache_add(dir, dname);
245
246		return dname;
247	}
248
249	dir->end_of_search = true;
250	return NULL;
251}
252
253/*
254  return unix directory of an open search
255*/
256const char *pvfs_list_unix_path(struct pvfs_dir *dir)
257{
258	return dir->unix_path;
259}
260
261/*
262  return true if end of search has been reached
263*/
264bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
265{
266	return dir->end_of_search;
267}
268
269/*
270  seek to the given name
271*/
272NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
273{
274	struct dirent *de;
275	int i;
276
277	dir->end_of_search = false;
278
279	if (ISDOT(name)) {
280		dir->offset = DIR_OFFSET_DOTDOT;
281		*ofs = dir->offset;
282		return NT_STATUS_OK;
283	}
284
285	if (ISDOTDOT(name)) {
286		dir->offset = DIR_OFFSET_BASE;
287		*ofs = dir->offset;
288		return NT_STATUS_OK;
289	}
290
291	for (i=dir->name_cache_index;i>=0;i--) {
292		struct name_cache_entry *e = &dir->name_cache[i];
293		if (e->name && strcasecmp_m(name, e->name) == 0) {
294			*ofs = e->offset;
295			return NT_STATUS_OK;
296		}
297	}
298	for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
299		struct name_cache_entry *e = &dir->name_cache[i];
300		if (e->name && strcasecmp_m(name, e->name) == 0) {
301			*ofs = e->offset;
302			return NT_STATUS_OK;
303		}
304	}
305
306	rewinddir(dir->dir);
307
308	while ((de = readdir(dir->dir))) {
309		if (strcasecmp_m(name, de->d_name) == 0) {
310			dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
311			*ofs = dir->offset;
312			return NT_STATUS_OK;
313		}
314	}
315
316	dir->end_of_search = true;
317
318	return NT_STATUS_OBJECT_NAME_NOT_FOUND;
319}
320
321/*
322  seek to the given offset
323*/
324NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
325{
326	struct dirent *de;
327	int i;
328
329	dir->end_of_search = false;
330
331	if (resume_key == DIR_OFFSET_DOT) {
332		*ofs = DIR_OFFSET_DOTDOT;
333		return NT_STATUS_OK;
334	}
335
336	if (resume_key == DIR_OFFSET_DOTDOT) {
337		*ofs = DIR_OFFSET_BASE;
338		return NT_STATUS_OK;
339	}
340
341	if (resume_key == DIR_OFFSET_BASE) {
342		rewinddir(dir->dir);
343		if ((de=readdir(dir->dir)) == NULL) {
344			dir->end_of_search = true;
345			return NT_STATUS_OBJECT_NAME_NOT_FOUND;
346		}
347		*ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
348		dir->offset = *ofs;
349		return NT_STATUS_OK;
350	}
351
352	for (i=dir->name_cache_index;i>=0;i--) {
353		struct name_cache_entry *e = &dir->name_cache[i];
354		if (resume_key == (uint32_t)e->offset) {
355			*ofs = e->offset;
356			return NT_STATUS_OK;
357		}
358	}
359	for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
360		struct name_cache_entry *e = &dir->name_cache[i];
361		if (resume_key == (uint32_t)e->offset) {
362			*ofs = e->offset;
363			return NT_STATUS_OK;
364		}
365	}
366
367	rewinddir(dir->dir);
368
369	while ((de = readdir(dir->dir))) {
370		dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
371		if (resume_key == (uint32_t)dir->offset) {
372			*ofs = dir->offset;
373			return NT_STATUS_OK;
374		}
375	}
376
377	dir->end_of_search = true;
378
379	return NT_STATUS_OBJECT_NAME_NOT_FOUND;
380}
381
382
383/*
384  see if a directory is empty
385*/
386bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
387{
388	struct dirent *de;
389	DIR *dir = opendir(name->full_name);
390	if (dir == NULL) {
391		return true;
392	}
393
394	while ((de = readdir(dir))) {
395		if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
396			closedir(dir);
397			return false;
398		}
399	}
400
401	closedir(dir);
402	return true;
403}
404