r4163: 2nd attempt at fixing the OS/2 "del *" problem
[samba.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         dir->end_of_search = True;
183         pvfs_list_hibernate(dir);
184         return NULL;
185 }
186
187 /* 
188    put the directory to sleep. Used between search calls to give the
189    right directory change semantics
190 */
191 void pvfs_list_hibernate(struct pvfs_dir *dir)
192 {
193         if (dir->dir) {
194                 closedir(dir->dir);
195                 dir->dir = NULL;
196         }
197 }
198
199
200 /* 
201    wake up the directory search
202 */
203 NTSTATUS pvfs_list_wakeup(struct pvfs_dir *dir, uint_t *ofs)
204 {
205         if (dir->no_wildcard ||
206             dir->dir != NULL) {
207                 return NT_STATUS_OK;
208         }
209
210         dir->dir = opendir(dir->unix_path);
211         if (dir->dir == NULL) {
212                 dir->end_of_search = True;
213                 return pvfs_map_errno(dir->pvfs, errno);
214         }
215
216         seekdir(dir->dir, *ofs);
217         dir->offset = telldir(dir->dir);
218         if (dir->offset != *ofs) {
219                 DEBUG(0,("pvfs_list_wakeup: search offset changed %u -> %u\n", 
220                          *ofs, (unsigned)dir->offset));
221         }
222
223         return NT_STATUS_OK;
224 }
225
226
227
228 /*
229   return unix directory of an open search
230 */
231 const char *pvfs_list_unix_path(struct pvfs_dir *dir)
232 {
233         return dir->unix_path;
234 }
235
236 /*
237   return True if end of search has been reached
238 */
239 BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
240 {
241         return dir->end_of_search;
242 }
243
244 /*
245   seek to the given name
246 */
247 NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
248 {
249         struct dirent *de;
250         NTSTATUS status;
251
252         status = pvfs_list_wakeup(dir, ofs);
253         if (!NT_STATUS_IS_OK(status)) {
254                 return status;
255         }
256
257         if (dir->last_name &&
258             StrCaseCmp(name, dir->last_name) == 0) {
259                 *ofs = dir->offset;
260                 return NT_STATUS_OK;
261         }
262
263         rewinddir(dir->dir);
264
265         while ((de = readdir(dir->dir))) {
266                 if (StrCaseCmp(name, de->d_name) == 0) {
267                         dir->offset = telldir(dir->dir);
268                         *ofs = dir->offset;
269                         if (dir->last_name) talloc_free(dir->last_name);
270                         dir->last_name = talloc_strdup(dir, de->d_name);
271                         return NT_STATUS_OK;
272                 }
273         }
274
275         dir->end_of_search = True;
276
277         pvfs_list_hibernate(dir);
278
279         /* it is not an error to give a bad name (it may have been deleted). Instead
280            just continue from end of directory */
281         return NT_STATUS_OK;
282 }