2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2004
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.
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, see <http://www.gnu.org/licenses/>.
20 directory listing functions for posix backend
24 #include "vfs_posix.h"
25 #include "system/dir.h"
27 #define NAME_CACHE_SIZE 100
29 struct name_cache_entry
{
35 struct pvfs_state
*pvfs
;
41 const char *unix_path
;
43 struct name_cache_entry
*name_cache
;
44 uint32_t name_cache_index
;
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
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
56 #define DIR_OFFSET_DOT 0
57 #define DIR_OFFSET_DOTDOT 1
58 #define DIR_OFFSET_BASE 0x80000022
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
64 static NTSTATUS
pvfs_list_no_wildcard(struct pvfs_state
*pvfs
, struct pvfs_filename
*name
,
65 const char *pattern
, struct pvfs_dir
*dir
)
68 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
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
;
79 dir
->single_name
= talloc_strdup(dir
, pattern
);
80 if (!dir
->single_name
) {
81 return NT_STATUS_NO_MEMORY
;
92 destroy an open search
94 static int pvfs_dirlist_destructor(struct pvfs_dir
*dir
)
96 if (dir
->dir
) closedir(dir
->dir
);
101 start to read a directory
103 if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
105 NTSTATUS
pvfs_list_start(struct pvfs_state
*pvfs
, struct pvfs_filename
*name
,
106 TALLOC_CTX
*mem_ctx
, struct pvfs_dir
**dirp
)
109 struct pvfs_dir
*dir
;
111 (*dirp
) = talloc_zero(mem_ctx
, struct pvfs_dir
);
113 return NT_STATUS_NO_MEMORY
;
118 /* split the unix path into a directory + pattern */
119 pattern
= strrchr(name
->full_name
, '/');
121 /* this should not happen, as pvfs_unix_path is supposed to
122 return an absolute path */
123 return NT_STATUS_UNSUCCESSFUL
;
128 if (!name
->has_wildcard
) {
129 return pvfs_list_no_wildcard(pvfs
, name
, pattern
, dir
);
132 dir
->unix_path
= talloc_strdup(dir
, name
->full_name
);
133 if (!dir
->unix_path
) {
134 return NT_STATUS_NO_MEMORY
;
137 dir
->pattern
= talloc_strdup(dir
, pattern
);
138 if (dir
->pattern
== NULL
) {
139 return NT_STATUS_NO_MEMORY
;
142 dir
->dir
= opendir(name
->full_name
);
144 return pvfs_map_errno(pvfs
, errno
);
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
,
154 if (dir
->name_cache
== NULL
) {
156 return NT_STATUS_NO_MEMORY
;
159 talloc_set_destructor(dir
, pvfs_dirlist_destructor
);
165 add an entry to the local cache
167 static void dcache_add(struct pvfs_dir
*dir
, const char *name
)
169 struct name_cache_entry
*e
;
171 dir
->name_cache_index
= (dir
->name_cache_index
+1) % NAME_CACHE_SIZE
;
172 e
= &dir
->name_cache
[dir
->name_cache_index
];
174 if (e
->name
) talloc_free(e
->name
);
176 e
->name
= talloc_strdup(dir
->name_cache
, name
);
177 e
->offset
= dir
->offset
;
181 return the next entry
183 const char *pvfs_list_next(struct pvfs_dir
*dir
, off_t
*ofs
)
186 enum protocol_types protocol
= dir
->pvfs
->ntvfs
->ctx
->protocol
;
188 /* non-wildcard searches are easy */
189 if (dir
->no_wildcard
) {
190 dir
->end_of_search
= true;
191 if (*ofs
!= 0) return NULL
;
193 return dir
->single_name
;
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
;
202 if (ms_fnmatch_protocol(dir
->pattern
, ".", protocol
) == 0) {
203 dcache_add(dir
, ".");
208 if (*ofs
== DIR_OFFSET_DOTDOT
) {
209 (*ofs
) = DIR_OFFSET_BASE
;
211 if (ms_fnmatch_protocol(dir
->pattern
, "..", protocol
) == 0) {
212 dcache_add(dir
, "..");
217 if (*ofs
== DIR_OFFSET_BASE
) {
219 } else if (*ofs
!= dir
->offset
) {
220 seekdir(dir
->dir
, (*ofs
) - DIR_OFFSET_BASE
);
224 while ((de
= readdir(dir
->dir
))) {
225 const char *dname
= de
->d_name
;
227 if (ISDOT(dname
) || ISDOTDOT(dname
)) {
231 if (ms_fnmatch_protocol(dir
->pattern
, dname
, protocol
) != 0) {
232 char *short_name
= pvfs_short_name_component(dir
->pvfs
, dname
);
233 if (short_name
== NULL
||
234 ms_fnmatch_protocol(dir
->pattern
, short_name
, protocol
) != 0) {
235 talloc_free(short_name
);
238 talloc_free(short_name
);
241 dir
->offset
= telldir(dir
->dir
) + DIR_OFFSET_BASE
;
242 (*ofs
) = dir
->offset
;
244 dcache_add(dir
, dname
);
249 dir
->end_of_search
= true;
254 return unix directory of an open search
256 const char *pvfs_list_unix_path(struct pvfs_dir
*dir
)
258 return dir
->unix_path
;
262 return true if end of search has been reached
264 bool pvfs_list_eos(struct pvfs_dir
*dir
, off_t ofs
)
266 return dir
->end_of_search
;
270 seek to the given name
272 NTSTATUS
pvfs_list_seek(struct pvfs_dir
*dir
, const char *name
, off_t
*ofs
)
277 dir
->end_of_search
= false;
280 dir
->offset
= DIR_OFFSET_DOTDOT
;
285 if (ISDOTDOT(name
)) {
286 dir
->offset
= DIR_OFFSET_BASE
;
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) {
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) {
308 while ((de
= readdir(dir
->dir
))) {
309 if (strcasecmp_m(name
, de
->d_name
) == 0) {
310 dir
->offset
= telldir(dir
->dir
) + DIR_OFFSET_BASE
;
316 dir
->end_of_search
= true;
318 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
322 seek to the given offset
324 NTSTATUS
pvfs_list_seek_ofs(struct pvfs_dir
*dir
, uint32_t resume_key
, off_t
*ofs
)
329 dir
->end_of_search
= false;
331 if (resume_key
== DIR_OFFSET_DOT
) {
332 *ofs
= DIR_OFFSET_DOTDOT
;
336 if (resume_key
== DIR_OFFSET_DOTDOT
) {
337 *ofs
= DIR_OFFSET_BASE
;
341 if (resume_key
== DIR_OFFSET_BASE
) {
343 if ((de
=readdir(dir
->dir
)) == NULL
) {
344 dir
->end_of_search
= true;
345 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
347 *ofs
= telldir(dir
->dir
) + DIR_OFFSET_BASE
;
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
) {
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
) {
369 while ((de
= readdir(dir
->dir
))) {
370 dir
->offset
= telldir(dir
->dir
) + DIR_OFFSET_BASE
;
371 if (resume_key
== (uint32_t)dir
->offset
) {
377 dir
->end_of_search
= true;
379 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
384 see if a directory is empty
386 bool pvfs_directory_empty(struct pvfs_state
*pvfs
, struct pvfs_filename
*name
)
389 DIR *dir
= opendir(name
->full_name
);
394 while ((de
= readdir(dir
))) {
395 if (!ISDOT(de
->d_name
) && !ISDOTDOT(de
->d_name
)) {