r3447: more include/system/XXX.h include files
[jelmer/samba4-debian.git] / source / ntvfs / posix / pvfs_dirlist.c
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 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   directory listing functions for posix backend
22 */
23
24 #include "includes.h"
25 #include "vfs_posix.h"
26 #include "system/dir.h"
27
28 struct pvfs_dir {
29         struct pvfs_state *pvfs;
30         BOOL no_wildcard;
31         char *last_name;
32         const char *pattern;
33         off_t offset;
34         DIR *dir;
35         const char *unix_path;
36         BOOL end_of_search;
37 };
38
39 /*
40   a special directory listing case where the pattern has no wildcard. We can just do a single stat()
41   thus avoiding the more expensive directory scan
42 */
43 static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name, 
44                                       const char *pattern, struct pvfs_dir *dir)
45 {
46         if (!name->exists) {
47                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
48         }
49
50         dir->pvfs = pvfs;
51         dir->no_wildcard = True;
52         dir->end_of_search = False;
53         dir->unix_path = talloc_strdup(dir, name->full_name);
54         if (!dir->unix_path) {
55                 return NT_STATUS_NO_MEMORY;
56         }
57
58         dir->last_name = talloc_strdup(dir, pattern);
59         if (!dir->last_name) {
60                 return NT_STATUS_NO_MEMORY;
61         }
62
63         dir->dir = NULL;
64         dir->offset = 0;
65         dir->pattern = NULL;
66
67         return NT_STATUS_OK;
68 }
69
70 /*
71   destroy an open search
72 */
73 static int pvfs_dirlist_destructor(void *ptr)
74 {
75         struct pvfs_dir *dir = ptr;
76         if (dir->dir) closedir(dir->dir);
77         return 0;
78 }
79
80 /*
81   start to read a directory 
82
83   if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
84 */
85 NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, 
86                          TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
87 {
88         char *pattern;
89         struct pvfs_dir *dir;
90
91         (*dirp) = talloc_p(mem_ctx, struct pvfs_dir);
92         if (*dirp == NULL) {
93                 return NT_STATUS_NO_MEMORY;
94         }
95         
96         dir = *dirp;
97
98         /* split the unix path into a directory + pattern */
99         pattern = strrchr(name->full_name, '/');
100         if (!pattern) {
101                 /* this should not happen, as pvfs_unix_path is supposed to 
102                    return an absolute path */
103                 return NT_STATUS_UNSUCCESSFUL;
104         }
105
106         *pattern++ = 0;
107
108         if (!name->has_wildcard) {
109                 return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
110         }
111
112         dir->unix_path = talloc_strdup(dir, name->full_name);
113         if (!dir->unix_path) {
114                 return NT_STATUS_NO_MEMORY;
115         }
116
117         dir->pattern = talloc_strdup(dir, pattern);
118         if (dir->pattern == NULL) {
119                 return NT_STATUS_NO_MEMORY;
120         }
121         
122         dir->dir = opendir(name->full_name);
123         if (!dir->dir) { 
124                 return pvfs_map_errno(pvfs, errno); 
125         }
126
127         dir->pvfs = pvfs;
128         dir->no_wildcard = False;
129         dir->last_name = NULL;
130         dir->end_of_search = False;
131         dir->offset = 0;
132
133         talloc_set_destructor(dir, pvfs_dirlist_destructor);
134
135         return NT_STATUS_OK;
136 }
137
138 /* 
139    return the next entry
140 */
141 const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
142 {
143         struct dirent *de;
144
145         /* non-wildcard searches are easy */
146         if (dir->no_wildcard) {
147                 dir->end_of_search = True;
148                 if (*ofs != 0) return NULL;
149                 (*ofs)++;
150                 return dir->last_name;
151         }
152
153         if (*ofs != dir->offset) {
154                 seekdir(dir->dir, *ofs);
155                 dir->offset = *ofs;
156         }
157         
158         while ((de = readdir(dir->dir))) {
159                 const char *dname = de->d_name;
160
161                 if (ms_fnmatch(dir->pattern, dname, 
162                                dir->pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
163                         char *short_name = pvfs_short_name_component(dir->pvfs, dname);
164                         if (short_name == NULL ||
165                             ms_fnmatch(dir->pattern, short_name, 
166                                        dir->pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
167                                 talloc_free(short_name);
168                                 continue;
169                         }
170                         talloc_free(short_name);
171                 }
172
173                 dir->offset = telldir(dir->dir);
174                 (*ofs) = dir->offset;
175
176                 if (dir->last_name) talloc_free(dir->last_name);
177                 dir->last_name = talloc_strdup(dir, de->d_name);
178
179                 return dir->last_name;
180         }
181
182         if (dir->last_name) talloc_free(dir->last_name);
183         dir->last_name = NULL;
184         dir->end_of_search = True;
185         pvfs_list_hibernate(dir);
186         return NULL;
187 }
188
189 /* 
190    put the directory to sleep. Used between search calls to give the
191    right directory change semantics
192 */
193 void pvfs_list_hibernate(struct pvfs_dir *dir)
194 {
195         if (dir->dir) {
196                 closedir(dir->dir);
197                 dir->dir = NULL;
198         }
199         if (!dir->no_wildcard && dir->last_name) {
200                 talloc_free(dir->last_name);
201                 dir->last_name = NULL;
202         }
203 }
204
205
206 /* 
207    wake up the directory search
208 */
209 NTSTATUS pvfs_list_wakeup(struct pvfs_dir *dir, uint_t *ofs)
210 {
211         if (dir->no_wildcard ||
212             dir->dir != NULL) {
213                 return NT_STATUS_OK;
214         }
215
216         dir->dir = opendir(dir->unix_path);
217         if (dir->dir == NULL) {
218                 dir->end_of_search = True;
219                 return pvfs_map_errno(dir->pvfs, errno);
220         }
221
222         seekdir(dir->dir, *ofs);
223         dir->offset = telldir(dir->dir);
224         if (dir->offset != *ofs) {
225                 DEBUG(0,("pvfs_list_wakeup: search offset changed %u -> %u\n", 
226                          *ofs, (unsigned)dir->offset));
227         }
228
229         return NT_STATUS_OK;
230 }
231
232
233
234 /*
235   return unix directory of an open search
236 */
237 const char *pvfs_list_unix_path(struct pvfs_dir *dir)
238 {
239         return dir->unix_path;
240 }
241
242 /*
243   return True if end of search has been reached
244 */
245 BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
246 {
247         return dir->end_of_search;
248 }
249
250 /*
251   seek to the given name
252 */
253 NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
254 {
255         struct dirent *de;
256         NTSTATUS status;
257
258         status = pvfs_list_wakeup(dir, ofs);
259         if (!NT_STATUS_IS_OK(status)) {
260                 return status;
261         }
262
263         if (dir->last_name &&
264             StrCaseCmp(name, dir->last_name) == 0) {
265                 *ofs = dir->offset;
266                 return NT_STATUS_OK;
267         }
268
269         rewinddir(dir->dir);
270
271         while ((de = readdir(dir->dir))) {
272                 if (StrCaseCmp(name, de->d_name) == 0) {
273                         dir->offset = telldir(dir->dir);
274                         *ofs = dir->offset;
275                         if (dir->last_name) talloc_free(dir->last_name);
276                         dir->last_name = talloc_strdup(dir, de->d_name);
277                         return NT_STATUS_OK;
278                 }
279         }
280
281         dir->end_of_search = True;
282
283         pvfs_list_hibernate(dir);
284
285         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
286 }