e51c7477f2ed4d3f851d9f81c00bab02bbe37150
[gd/samba-autobuild/.git] / source4 / ntvfs / posix / pvfs_search.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - directory search functions
5
6    Copyright (C) Andrew Tridgell 2004
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "include/includes.h"
24 #include "vfs_posix.h"
25
26
27 /* 
28    list files in a directory matching a wildcard pattern - old SMBsearch interface
29 */
30 static NTSTATUS pvfs_search_first_old(struct smbsrv_request *req, union smb_search_first *io, 
31                                       void *search_private, 
32                                       BOOL (*callback)(void *, union smb_search_data *))
33 {
34         return NT_STATUS_NOT_IMPLEMENTED;
35 }
36
37 /* continue a old style search */
38 static NTSTATUS pvfs_search_next_old(struct smbsrv_request *req, union smb_search_next *io, 
39                                      void *search_private, 
40                                      BOOL (*callback)(void *, union smb_search_data *))
41 {
42         return NT_STATUS_NOT_IMPLEMENTED;
43 }
44
45 /* close a old style search */
46 static NTSTATUS pvfs_search_close_old(struct smbsrv_request *req, union smb_search_close *io)
47 {
48         return NT_STATUS_NOT_IMPLEMENTED;
49 }
50
51 /*
52   fill in a single search result for a given info level
53 */
54 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
55                                  enum smb_search_level level,
56                                  const char *unix_path,
57                                  const char *fname, 
58                                  uint16_t search_attrib,
59                                  uint32_t dir_index,
60                                  union smb_search_data *file)
61 {
62         struct pvfs_filename *name;
63         NTSTATUS status;
64
65         status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
66         if (!NT_STATUS_IS_OK(status)) {
67                 return status;
68         }
69
70         if (!pvfs_match_attrib(pvfs, name, search_attrib)) {
71                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
72         }
73
74         switch (level) {
75         case RAW_SEARCH_STANDARD:
76                 file->standard.resume_key   = dir_index;
77                 file->standard.create_time  = nt_time_to_unix(name->dos.create_time);
78                 file->standard.access_time  = nt_time_to_unix(name->dos.access_time);
79                 file->standard.write_time   = nt_time_to_unix(name->dos.write_time);
80                 file->standard.size         = name->st.st_size;
81                 file->standard.alloc_size   = name->dos.alloc_size;
82                 file->standard.attrib       = name->dos.attrib;
83                 file->standard.name.s       = fname;
84                 break;
85
86         case RAW_SEARCH_EA_SIZE:
87                 file->ea_size.resume_key   = dir_index;
88                 file->ea_size.create_time  = nt_time_to_unix(name->dos.create_time);
89                 file->ea_size.access_time  = nt_time_to_unix(name->dos.access_time);
90                 file->ea_size.write_time   = nt_time_to_unix(name->dos.write_time);
91                 file->ea_size.size         = name->st.st_size;
92                 file->ea_size.alloc_size   = name->dos.alloc_size;
93                 file->ea_size.attrib       = name->dos.attrib;
94                 file->ea_size.ea_size      = name->dos.ea_size;
95                 file->ea_size.name.s       = fname;
96                 break;
97
98         case RAW_SEARCH_DIRECTORY_INFO:
99                 file->directory_info.file_index   = dir_index;
100                 file->directory_info.create_time  = name->dos.create_time;
101                 file->directory_info.access_time  = name->dos.access_time;
102                 file->directory_info.write_time   = name->dos.write_time;
103                 file->directory_info.change_time  = name->dos.change_time;
104                 file->directory_info.size         = name->st.st_size;
105                 file->directory_info.alloc_size   = name->dos.alloc_size;
106                 file->directory_info.attrib       = name->dos.attrib;
107                 file->directory_info.name.s       = fname;
108                 break;
109
110         case RAW_SEARCH_FULL_DIRECTORY_INFO:
111                 file->full_directory_info.file_index   = dir_index;
112                 file->full_directory_info.create_time  = name->dos.create_time;
113                 file->full_directory_info.access_time  = name->dos.access_time;
114                 file->full_directory_info.write_time   = name->dos.write_time;
115                 file->full_directory_info.change_time  = name->dos.change_time;
116                 file->full_directory_info.size         = name->st.st_size;
117                 file->full_directory_info.alloc_size   = name->dos.alloc_size;
118                 file->full_directory_info.attrib       = name->dos.attrib;
119                 file->full_directory_info.ea_size      = name->dos.ea_size;
120                 file->full_directory_info.name.s       = fname;
121                 break;
122
123         case RAW_SEARCH_NAME_INFO:
124                 file->name_info.file_index   = dir_index;
125                 file->name_info.name.s       = fname;
126                 break;
127
128         case RAW_SEARCH_BOTH_DIRECTORY_INFO:
129                 file->both_directory_info.file_index   = dir_index;
130                 file->both_directory_info.create_time  = name->dos.create_time;
131                 file->both_directory_info.access_time  = name->dos.access_time;
132                 file->both_directory_info.write_time   = name->dos.write_time;
133                 file->both_directory_info.change_time  = name->dos.change_time;
134                 file->both_directory_info.size         = name->st.st_size;
135                 file->both_directory_info.alloc_size   = name->dos.alloc_size;
136                 file->both_directory_info.attrib       = name->dos.attrib;
137                 file->both_directory_info.ea_size      = name->dos.ea_size;
138                 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, name);
139                 file->both_directory_info.name.s       = fname;
140                 break;
141
142         case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
143                 file->id_full_directory_info.file_index   = dir_index;
144                 file->id_full_directory_info.create_time  = name->dos.create_time;
145                 file->id_full_directory_info.access_time  = name->dos.access_time;
146                 file->id_full_directory_info.write_time   = name->dos.write_time;
147                 file->id_full_directory_info.change_time  = name->dos.change_time;
148                 file->id_full_directory_info.size         = name->st.st_size;
149                 file->id_full_directory_info.alloc_size   = name->dos.alloc_size;
150                 file->id_full_directory_info.attrib       = name->dos.attrib;
151                 file->id_full_directory_info.ea_size      = name->dos.ea_size;
152                 file->id_full_directory_info.file_id      = name->dos.file_id;
153                 file->id_full_directory_info.name.s       = fname;
154                 break;
155
156         case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
157                 file->id_both_directory_info.file_index   = dir_index;
158                 file->id_both_directory_info.create_time  = name->dos.create_time;
159                 file->id_both_directory_info.access_time  = name->dos.access_time;
160                 file->id_both_directory_info.write_time   = name->dos.write_time;
161                 file->id_both_directory_info.change_time  = name->dos.change_time;
162                 file->id_both_directory_info.size         = name->st.st_size;
163                 file->id_both_directory_info.alloc_size   = name->dos.alloc_size;
164                 file->id_both_directory_info.attrib       = name->dos.attrib;
165                 file->id_both_directory_info.ea_size      = name->dos.ea_size;
166                 file->id_both_directory_info.file_id      = name->dos.file_id;
167                 file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, name);
168                 file->id_both_directory_info.name.s       = fname;
169                 break;
170
171         default:
172                 return NT_STATUS_INVALID_LEVEL;
173         }
174
175         return NT_STATUS_OK;
176 }
177
178 /*
179   return the next available search handle
180 */
181 static NTSTATUS pvfs_next_search_handle(struct pvfs_state *pvfs, uint16_t *handle)
182 {
183         struct pvfs_search_state *search;
184
185         if (pvfs->search.num_active_searches >= 0x10000) {
186                 return NT_STATUS_INSUFFICIENT_RESOURCES;
187         }
188
189         (*handle) = pvfs->search.next_search_handle;
190         for (search=pvfs->search.open_searches;search;search=search->next) {
191                 if (*handle == search->handle) {
192                         *handle = ((*handle)+1) & 0xFFFF;
193                         continue;
194                 } 
195         }
196         pvfs->search.next_search_handle = ((*handle)+1) & 0xFFFF;
197
198         return NT_STATUS_OK;
199 }
200
201
202 /*
203   the search fill loop
204 */
205 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, 
206                                  uint_t max_count, 
207                                  struct pvfs_search_state *search,
208                                  enum smb_search_level level,
209                                  uint_t *reply_count,
210                                  void *search_private, 
211                                  BOOL (*callback)(void *, union smb_search_data *))
212 {
213         int i;
214         struct pvfs_dir *dir = search->dir;
215         NTSTATUS status;
216
217         *reply_count = 0;
218
219         for (i = search->current_index; i < dir->count;i++) {
220                 union smb_search_data *file;
221
222                 file = talloc_p(mem_ctx, union smb_search_data);
223                 if (!file) {
224                         return NT_STATUS_NO_MEMORY;
225                 }
226
227                 status = fill_search_info(pvfs, level, dir->unix_path, dir->names[i], 
228                                           search->search_attrib, i, file);
229                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
230                         talloc_free(file);
231                         continue;
232                 }
233
234                 if (!NT_STATUS_IS_OK(status)) {
235                         talloc_free(file);
236                         search->current_index = i;
237                         return status;
238                 }
239
240                 if (!callback(search_private, file)) {
241                         talloc_free(file);
242                         break;
243                 }
244                 (*reply_count)++;
245                 talloc_free(file);
246
247                 /* note that this deliberately allows a reply_count of
248                    1 for a max_count of 0. w2k3 allows this too. */
249                 if (*reply_count >= max_count) break;
250         }
251
252         search->current_index = i;
253
254         return NT_STATUS_OK;
255 }
256
257 /* 
258    list files in a directory matching a wildcard pattern
259 */
260 NTSTATUS pvfs_search_first(struct smbsrv_request *req, union smb_search_first *io, 
261                            void *search_private, 
262                            BOOL (*callback)(void *, union smb_search_data *))
263 {
264         struct pvfs_dir *dir;
265         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
266         struct pvfs_search_state *search;
267         uint_t reply_count;
268         uint16_t search_attrib;
269         const char *pattern;
270         NTSTATUS status;
271         struct pvfs_filename *name;
272
273         if (io->generic.level >= RAW_SEARCH_SEARCH) {
274                 return pvfs_search_first_old(req, io, search_private, callback);
275         }
276
277         search_attrib = io->t2ffirst.in.search_attrib;
278         pattern       = io->t2ffirst.in.pattern;
279
280         /* resolve the cifs name to a posix name */
281         status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
282         if (!NT_STATUS_IS_OK(status)) {
283                 return status;
284         }
285
286         if (!name->has_wildcard && !name->exists) {
287                 return NT_STATUS_NO_SUCH_FILE;
288         }
289
290         /* we initially make search a child of the request, then if we
291            need to keep it long term we steal it for the private
292            structure */
293         search = talloc_p(req, struct pvfs_search_state);
294         if (!search) {
295                 return NT_STATUS_NO_MEMORY;
296         }
297
298         dir = talloc_p(search, struct pvfs_dir);
299         if (!dir) {
300                 return NT_STATUS_NO_MEMORY;
301         }
302
303         /* do the actual directory listing */
304         status = pvfs_list(pvfs, name, dir);
305         if (!NT_STATUS_IS_OK(status)) {
306                 return status;
307         }
308
309         /* we need to give a handle back to the client so it
310            can continue a search */
311         status = pvfs_next_search_handle(pvfs, &search->handle);
312         if (!NT_STATUS_IS_OK(status)) {
313                 return status;
314         }
315         
316         search->dir = dir;
317         search->current_index = 0;
318         search->search_attrib = search_attrib;
319
320         status = pvfs_search_fill(pvfs, req, io->t2ffirst.in.max_count, search, io->generic.level,
321                                   &reply_count, search_private, callback);
322         if (!NT_STATUS_IS_OK(status)) {
323                 return status;
324         }
325
326         /* not matching any entries is an error */
327         if (reply_count == 0) {
328                 return NT_STATUS_NO_SUCH_FILE;
329         }
330
331         io->t2ffirst.out.count = reply_count;
332         io->t2ffirst.out.handle = search->handle;
333         io->t2ffirst.out.end_of_search = (search->current_index == dir->count) ? 1 : 0;
334
335         /* work out if we are going to keep the search state
336            and allow for a search continue */
337         if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
338             ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
339              io->t2ffirst.out.end_of_search)) {
340                 talloc_free(search);
341         } else {
342                 pvfs->search.num_active_searches++;
343                 pvfs->search.next_search_handle++;
344                 talloc_steal(pvfs, search);
345                 DLIST_ADD(pvfs->search.open_searches, search);
346         }
347
348         return NT_STATUS_OK;
349 }
350
351 /* continue a search */
352 NTSTATUS pvfs_search_next(struct smbsrv_request *req, union smb_search_next *io, 
353                           void *search_private, 
354                           BOOL (*callback)(void *, union smb_search_data *))
355 {
356         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
357         struct pvfs_search_state *search;
358         struct pvfs_dir *dir;
359         uint_t reply_count;
360         uint16_t handle;
361         NTSTATUS status;
362         int i;
363
364         if (io->generic.level >= RAW_SEARCH_SEARCH) {
365                 return pvfs_search_next_old(req, io, search_private, callback);
366         }
367
368         handle = io->t2fnext.in.handle;
369
370         for (search=pvfs->search.open_searches; search; search = search->next) {
371                 if (search->handle == handle) break;
372         }
373         
374         if (!search) {
375                 /* we didn't find the search handle */
376                 return NT_STATUS_INVALID_HANDLE;
377         }
378
379         dir = search->dir;
380
381         /* the client might be asking for something other than just continuing
382            with the search */
383         if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
384             io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
385                 /* look backwards first */
386                 for (i=search->current_index; i > 0; i--) {
387                         if (strcmp(io->t2fnext.in.last_name, dir->names[i-1]) == 0) {
388                                 search->current_index = i;
389                                 goto found;
390                         }
391                 }
392
393                 /* then look forwards */
394                 for (i=search->current_index+1; i <= dir->count; i++) {
395                         if (strcmp(io->t2fnext.in.last_name, dir->names[i-1]) == 0) {
396                                 search->current_index = i;
397                                 goto found;
398                         }
399                 }
400         }
401
402 found:  
403         status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.level,
404                                   &reply_count, search_private, callback);
405         if (!NT_STATUS_IS_OK(status)) {
406                 return status;
407         }
408
409         /* not matching any entries is an error */
410         if (reply_count == 0) {
411                 return NT_STATUS_NO_MORE_ENTRIES;
412         }
413
414         io->t2fnext.out.count = reply_count;
415         io->t2fnext.out.end_of_search = (search->current_index == dir->count) ? 1 : 0;
416
417         /* work out if we are going to keep the search state */
418         if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
419             ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
420              io->t2fnext.out.end_of_search)) {
421                 DLIST_REMOVE(pvfs->search.open_searches, search);
422                 talloc_free(search);
423         }
424
425         return NT_STATUS_OK;
426 }
427
428 /* close a search */
429 NTSTATUS pvfs_search_close(struct smbsrv_request *req, union smb_search_close *io)
430 {
431         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
432         struct pvfs_search_state *search;
433         uint16_t handle;
434
435         if (io->generic.level >= RAW_SEARCH_SEARCH) {
436                 return pvfs_search_close_old(req, io);
437         }
438
439         handle = io->findclose.in.handle;
440
441         for (search=pvfs->search.open_searches; search; search = search->next) {
442                 if (search->handle == handle) break;
443         }
444         
445         if (!search) {
446                 /* we didn't find the search handle */
447                 return NT_STATUS_INVALID_HANDLE;
448         }
449
450         DLIST_REMOVE(pvfs->search.open_searches, search);
451         talloc_free(search);
452
453         return NT_STATUS_OK;
454 }
455