r3262: - new pvfs_dirlist code that reopens the directory between search
[bbaumbach/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   destroy an open search
29 */
30 static int pvfs_search_destructor(void *ptr)
31 {
32         struct pvfs_search_state *search = ptr;
33         idr_remove(search->pvfs->idtree_search, search->handle);
34         return 0;
35 }
36
37 /*
38   fill in a single search result for a given info level
39 */
40 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
41                                  enum smb_search_level level,
42                                  const char *unix_path,
43                                  const char *fname, 
44                                  struct pvfs_search_state *search,
45                                  uint32_t dir_index,
46                                  union smb_search_data *file)
47 {
48         struct pvfs_filename *name;
49         NTSTATUS status;
50         const char *shortname;
51
52         status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
53         if (!NT_STATUS_IS_OK(status)) {
54                 return status;
55         }
56
57         status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
58         if (!NT_STATUS_IS_OK(status)) {
59                 return status;
60         }
61
62         switch (level) {
63         case RAW_SEARCH_SEARCH:
64         case RAW_SEARCH_FFIRST:
65         case RAW_SEARCH_FUNIQUE:
66                 shortname = pvfs_short_name(pvfs, name, name);
67                 file->search.attrib           = name->dos.attrib;
68                 file->search.write_time       = nt_time_to_unix(name->dos.write_time);
69                 file->search.size             = name->st.st_size;
70                 file->search.name             = shortname;
71                 file->search.id.reserved      = 8;
72                 memset(file->search.id.name, ' ', sizeof(file->search.id.name));
73                 memcpy(file->search.id.name, shortname, 
74                        MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
75                 file->search.id.handle        = search->handle;
76                 file->search.id.server_cookie = dir_index;
77                 file->search.id.client_cookie = 0;
78                 return NT_STATUS_OK;
79
80         case RAW_SEARCH_STANDARD:
81                 file->standard.resume_key   = dir_index;
82                 file->standard.create_time  = nt_time_to_unix(name->dos.create_time);
83                 file->standard.access_time  = nt_time_to_unix(name->dos.access_time);
84                 file->standard.write_time   = nt_time_to_unix(name->dos.write_time);
85                 file->standard.size         = name->st.st_size;
86                 file->standard.alloc_size   = name->dos.alloc_size;
87                 file->standard.attrib       = name->dos.attrib;
88                 file->standard.name.s       = fname;
89                 return NT_STATUS_OK;
90
91         case RAW_SEARCH_EA_SIZE:
92                 file->ea_size.resume_key   = dir_index;
93                 file->ea_size.create_time  = nt_time_to_unix(name->dos.create_time);
94                 file->ea_size.access_time  = nt_time_to_unix(name->dos.access_time);
95                 file->ea_size.write_time   = nt_time_to_unix(name->dos.write_time);
96                 file->ea_size.size         = name->st.st_size;
97                 file->ea_size.alloc_size   = name->dos.alloc_size;
98                 file->ea_size.attrib       = name->dos.attrib;
99                 file->ea_size.ea_size      = name->dos.ea_size;
100                 file->ea_size.name.s       = fname;
101                 return NT_STATUS_OK;
102
103         case RAW_SEARCH_DIRECTORY_INFO:
104                 file->directory_info.file_index   = dir_index;
105                 file->directory_info.create_time  = name->dos.create_time;
106                 file->directory_info.access_time  = name->dos.access_time;
107                 file->directory_info.write_time   = name->dos.write_time;
108                 file->directory_info.change_time  = name->dos.change_time;
109                 file->directory_info.size         = name->st.st_size;
110                 file->directory_info.alloc_size   = name->dos.alloc_size;
111                 file->directory_info.attrib       = name->dos.attrib;
112                 file->directory_info.name.s       = fname;
113                 return NT_STATUS_OK;
114
115         case RAW_SEARCH_FULL_DIRECTORY_INFO:
116                 file->full_directory_info.file_index   = dir_index;
117                 file->full_directory_info.create_time  = name->dos.create_time;
118                 file->full_directory_info.access_time  = name->dos.access_time;
119                 file->full_directory_info.write_time   = name->dos.write_time;
120                 file->full_directory_info.change_time  = name->dos.change_time;
121                 file->full_directory_info.size         = name->st.st_size;
122                 file->full_directory_info.alloc_size   = name->dos.alloc_size;
123                 file->full_directory_info.attrib       = name->dos.attrib;
124                 file->full_directory_info.ea_size      = name->dos.ea_size;
125                 file->full_directory_info.name.s       = fname;
126                 return NT_STATUS_OK;
127
128         case RAW_SEARCH_NAME_INFO:
129                 file->name_info.file_index   = dir_index;
130                 file->name_info.name.s       = fname;
131                 return NT_STATUS_OK;
132
133         case RAW_SEARCH_BOTH_DIRECTORY_INFO:
134                 file->both_directory_info.file_index   = dir_index;
135                 file->both_directory_info.create_time  = name->dos.create_time;
136                 file->both_directory_info.access_time  = name->dos.access_time;
137                 file->both_directory_info.write_time   = name->dos.write_time;
138                 file->both_directory_info.change_time  = name->dos.change_time;
139                 file->both_directory_info.size         = name->st.st_size;
140                 file->both_directory_info.alloc_size   = name->dos.alloc_size;
141                 file->both_directory_info.attrib       = name->dos.attrib;
142                 file->both_directory_info.ea_size      = name->dos.ea_size;
143                 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
144                 file->both_directory_info.name.s       = fname;
145                 return NT_STATUS_OK;
146
147         case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
148                 file->id_full_directory_info.file_index   = dir_index;
149                 file->id_full_directory_info.create_time  = name->dos.create_time;
150                 file->id_full_directory_info.access_time  = name->dos.access_time;
151                 file->id_full_directory_info.write_time   = name->dos.write_time;
152                 file->id_full_directory_info.change_time  = name->dos.change_time;
153                 file->id_full_directory_info.size         = name->st.st_size;
154                 file->id_full_directory_info.alloc_size   = name->dos.alloc_size;
155                 file->id_full_directory_info.attrib       = name->dos.attrib;
156                 file->id_full_directory_info.ea_size      = name->dos.ea_size;
157                 file->id_full_directory_info.file_id      = name->dos.file_id;
158                 file->id_full_directory_info.name.s       = fname;
159                 return NT_STATUS_OK;
160
161         case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
162                 file->id_both_directory_info.file_index   = dir_index;
163                 file->id_both_directory_info.create_time  = name->dos.create_time;
164                 file->id_both_directory_info.access_time  = name->dos.access_time;
165                 file->id_both_directory_info.write_time   = name->dos.write_time;
166                 file->id_both_directory_info.change_time  = name->dos.change_time;
167                 file->id_both_directory_info.size         = name->st.st_size;
168                 file->id_both_directory_info.alloc_size   = name->dos.alloc_size;
169                 file->id_both_directory_info.attrib       = name->dos.attrib;
170                 file->id_both_directory_info.ea_size      = name->dos.ea_size;
171                 file->id_both_directory_info.file_id      = name->dos.file_id;
172                 file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
173                 file->id_both_directory_info.name.s       = fname;
174                 return NT_STATUS_OK;
175
176         case RAW_SEARCH_GENERIC:
177                 break;
178         }
179
180         return NT_STATUS_INVALID_LEVEL;
181 }
182
183
184 /*
185   the search fill loop
186 */
187 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, 
188                                  uint_t max_count, 
189                                  struct pvfs_search_state *search,
190                                  enum smb_search_level level,
191                                  uint_t *reply_count,
192                                  void *search_private, 
193                                  BOOL (*callback)(void *, union smb_search_data *))
194 {
195         struct pvfs_dir *dir = search->dir;
196         NTSTATUS status;
197
198         *reply_count = 0;
199
200         if (max_count == 0) {
201                 max_count = 1;
202         }
203
204         while ((*reply_count) < max_count) {
205                 union smb_search_data *file;
206                 const char *name;
207                 uint_t ofs = search->current_index;
208
209                 name = pvfs_list_next(dir, &search->current_index);
210                 if (name == NULL) break;
211
212                 file = talloc_p(mem_ctx, union smb_search_data);
213                 if (!file) {
214                         return NT_STATUS_NO_MEMORY;
215                 }
216
217                 status = fill_search_info(pvfs, level, 
218                                           pvfs_list_unix_path(dir), name, 
219                                           search, search->current_index, file);
220                 if (!NT_STATUS_IS_OK(status)) {
221                         talloc_free(file);
222                         continue;
223                 }
224
225                 if (!callback(search_private, file)) {
226                         talloc_free(file);
227                         search->current_index = ofs;
228                         break;
229                 }
230
231                 (*reply_count)++;
232                 talloc_free(file);
233         }
234
235         pvfs_list_hibernate(dir);
236
237         return NT_STATUS_OK;
238 }
239
240 /* 
241    list files in a directory matching a wildcard pattern - old SMBsearch interface
242 */
243 static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
244                                       struct smbsrv_request *req, union smb_search_first *io, 
245                                       void *search_private, 
246                                       BOOL (*callback)(void *, union smb_search_data *))
247 {
248         struct pvfs_dir *dir;
249         struct pvfs_state *pvfs = ntvfs->private_data;
250         struct pvfs_search_state *search;
251         uint_t reply_count;
252         uint16_t search_attrib;
253         const char *pattern;
254         NTSTATUS status;
255         struct pvfs_filename *name;
256         int id;
257
258         search_attrib = io->search_first.in.search_attrib;
259         pattern       = io->search_first.in.pattern;
260
261         /* resolve the cifs name to a posix name */
262         status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
263         if (!NT_STATUS_IS_OK(status)) {
264                 return status;
265         }
266
267         if (!name->has_wildcard && !name->exists) {
268                 return STATUS_NO_MORE_FILES;
269         }
270
271         /* we initially make search a child of the request, then if we
272            need to keep it long term we steal it for the private
273            structure */
274         search = talloc_p(req, struct pvfs_search_state);
275         if (!search) {
276                 return NT_STATUS_NO_MEMORY;
277         }
278
279         /* do the actual directory listing */
280         status = pvfs_list_start(pvfs, name, search, &dir);
281         if (!NT_STATUS_IS_OK(status)) {
282                 return status;
283         }
284
285         /* we need to give a handle back to the client so it
286            can continue a search */
287         id = idr_get_new(pvfs->idtree_search, search, UINT8_MAX);
288         if (id == -1) {
289                 return NT_STATUS_INSUFFICIENT_RESOURCES;
290         }
291
292         search->pvfs = pvfs;
293         search->handle = id;
294         search->dir = dir;
295         search->current_index = 0;
296         search->search_attrib = search_attrib & 0xFF;
297         search->must_attrib = (search_attrib>>8) & 0xFF;
298
299         talloc_set_destructor(search, pvfs_search_destructor);
300
301         status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.level,
302                                   &reply_count, search_private, callback);
303         if (!NT_STATUS_IS_OK(status)) {
304                 return status;
305         }
306
307         io->search_first.out.count = reply_count;
308
309         /* not matching any entries is an error */
310         if (reply_count == 0) {
311                 return STATUS_NO_MORE_FILES;
312         }
313
314         talloc_steal(pvfs, search);
315
316         return NT_STATUS_OK;
317 }
318
319 /* continue a old style search */
320 static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
321                                      struct smbsrv_request *req, union smb_search_next *io, 
322                                      void *search_private, 
323                                      BOOL (*callback)(void *, union smb_search_data *))
324 {
325         struct pvfs_state *pvfs = ntvfs->private_data;
326         struct pvfs_search_state *search;
327         struct pvfs_dir *dir;
328         uint_t reply_count, max_count;
329         uint16_t handle;
330         NTSTATUS status;
331
332         handle    = io->search_next.in.id.handle;
333         max_count = io->search_next.in.max_count;
334
335         search = idr_find(pvfs->idtree_search, handle);
336         if (search == NULL) {
337                 /* we didn't find the search handle */
338                 return NT_STATUS_INVALID_HANDLE;
339         }
340
341         search->current_index = io->search_next.in.id.server_cookie;
342         dir = search->dir;
343
344         status = pvfs_list_wakeup(dir, &search->current_index);
345         if (!NT_STATUS_IS_OK(status)) {
346                 return status;
347         }
348
349         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
350                                   &reply_count, search_private, callback);
351         if (!NT_STATUS_IS_OK(status)) {
352                 return status;
353         }
354
355         io->search_next.out.count = reply_count;
356
357         /* not matching any entries means end of search */
358         if (reply_count == 0) {
359                 talloc_free(search);
360         }
361
362         return NT_STATUS_OK;
363 }
364
365 /* 
366    list files in a directory matching a wildcard pattern
367 */
368 NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
369                            struct smbsrv_request *req, union smb_search_first *io, 
370                            void *search_private, 
371                            BOOL (*callback)(void *, union smb_search_data *))
372 {
373         struct pvfs_dir *dir;
374         struct pvfs_state *pvfs = ntvfs->private_data;
375         struct pvfs_search_state *search;
376         uint_t reply_count;
377         uint16_t search_attrib, max_count;
378         const char *pattern;
379         NTSTATUS status;
380         struct pvfs_filename *name;
381         int id;
382
383         if (io->generic.level >= RAW_SEARCH_SEARCH) {
384                 return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
385         }
386
387         search_attrib = io->t2ffirst.in.search_attrib;
388         pattern       = io->t2ffirst.in.pattern;
389         max_count     = io->t2ffirst.in.max_count;
390
391         /* resolve the cifs name to a posix name */
392         status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
393         if (!NT_STATUS_IS_OK(status)) {
394                 return status;
395         }
396
397         if (!name->has_wildcard && !name->exists) {
398                 return NT_STATUS_NO_SUCH_FILE;
399         }
400
401         /* we initially make search a child of the request, then if we
402            need to keep it long term we steal it for the private
403            structure */
404         search = talloc_p(req, struct pvfs_search_state);
405         if (!search) {
406                 return NT_STATUS_NO_MEMORY;
407         }
408
409         /* do the actual directory listing */
410         status = pvfs_list_start(pvfs, name, search, &dir);
411         if (!NT_STATUS_IS_OK(status)) {
412                 return status;
413         }
414
415         id = idr_get_new(pvfs->idtree_search, search, UINT16_MAX);
416         if (id == -1) {
417                 return NT_STATUS_INSUFFICIENT_RESOURCES;
418         }
419
420         search->pvfs = pvfs;
421         search->handle = id;
422         search->dir = dir;
423         search->current_index = 0;
424         search->search_attrib = search_attrib;
425         search->must_attrib = 0;
426
427         talloc_set_destructor(search, pvfs_search_destructor);
428
429         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
430                                   &reply_count, search_private, callback);
431         if (!NT_STATUS_IS_OK(status)) {
432                 return status;
433         }
434
435         /* not matching any entries is an error */
436         if (reply_count == 0) {
437                 return NT_STATUS_NO_SUCH_FILE;
438         }
439
440         io->t2ffirst.out.count = reply_count;
441         io->t2ffirst.out.handle = search->handle;
442         io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
443
444         /* work out if we are going to keep the search state
445            and allow for a search continue */
446         if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
447             ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
448              io->t2ffirst.out.end_of_search)) {
449                 talloc_free(search);
450         } else {
451                 talloc_steal(pvfs, search);
452         }
453
454         return NT_STATUS_OK;
455 }
456
457 /* continue a search */
458 NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
459                           struct smbsrv_request *req, union smb_search_next *io, 
460                           void *search_private, 
461                           BOOL (*callback)(void *, union smb_search_data *))
462 {
463         struct pvfs_state *pvfs = ntvfs->private_data;
464         struct pvfs_search_state *search;
465         struct pvfs_dir *dir;
466         uint_t reply_count;
467         uint16_t handle;
468         NTSTATUS status;
469
470         if (io->generic.level >= RAW_SEARCH_SEARCH) {
471                 return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
472         }
473
474         handle = io->t2fnext.in.handle;
475
476         search = idr_find(pvfs->idtree_search, handle);
477         if (search == NULL) {
478                 /* we didn't find the search handle */
479                 return NT_STATUS_INVALID_HANDLE;
480         }
481
482         dir = search->dir;
483
484         /* work out what type of continuation is being used */
485         if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
486                 status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
487                 if (!NT_STATUS_IS_OK(status)) {
488                         return status;
489                 }
490         } else if (io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) {
491                 /* plain continue - nothing to do */
492         } else {
493                 search->current_index = io->t2fnext.in.resume_key;
494         }
495
496         status = pvfs_list_wakeup(dir, &search->current_index);
497         if (!NT_STATUS_IS_OK(status)) {
498                 return status;
499         }
500
501         status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.level,
502                                   &reply_count, search_private, callback);
503         if (!NT_STATUS_IS_OK(status)) {
504                 return status;
505         }
506
507         /* not matching any entries is an error */
508         if (reply_count == 0) {
509                 return NT_STATUS_NO_MORE_ENTRIES;
510         }
511
512         io->t2fnext.out.count = reply_count;
513         io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
514
515         /* work out if we are going to keep the search state */
516         if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
517             ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
518              io->t2fnext.out.end_of_search)) {
519                 talloc_free(search);
520         }
521
522         return NT_STATUS_OK;
523 }
524
525 /* close a search */
526 NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
527                            struct smbsrv_request *req, union smb_search_close *io)
528 {
529         struct pvfs_state *pvfs = ntvfs->private_data;
530         struct pvfs_search_state *search;
531         uint16_t handle;
532
533         if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
534                 handle = io->fclose.in.id.handle;
535         } else {
536                 handle = io->findclose.in.handle;
537         }
538
539         search = idr_find(pvfs->idtree_search, handle);
540         if (search == NULL) {
541                 /* we didn't find the search handle */
542                 return NT_STATUS_INVALID_HANDLE;
543         }
544
545         talloc_free(search);
546
547         return NT_STATUS_OK;
548 }
549