r18319: fixed the directory search resume code on IRIX
[kai/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 "includes.h"
24 #include "vfs_posix.h"
25 #include "system/time.h"
26 #include "librpc/gen_ndr/security.h"
27 #include "smbd/service_stream.h"
28 #include "lib/events/events.h"
29 #include "lib/util/dlinklist.h"
30
31 /* place a reasonable limit on old-style searches as clients tend to
32    not send search close requests */
33 #define MAX_OLD_SEARCHES 2000
34 #define MAX_SEARCH_HANDLES (UINT16_MAX - 1)
35 #define INVALID_SEARCH_HANDLE UINT16_MAX
36
37 /*
38   destroy an open search
39 */
40 static int pvfs_search_destructor(struct pvfs_search_state *search)
41 {
42         DLIST_REMOVE(search->pvfs->search.list, search);
43         idr_remove(search->pvfs->search.idtree, search->handle);
44         return 0;
45 }
46
47 /*
48   called when a search timer goes off
49 */
50 static void pvfs_search_timer(struct event_context *ev, struct timed_event *te, 
51                                       struct timeval t, void *ptr)
52 {
53         struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
54         talloc_free(search);
55 }
56
57 /*
58   setup a timer to destroy a open search after a inactivity period
59 */
60 static void pvfs_search_setup_timer(struct pvfs_search_state *search)
61 {
62         struct event_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
63         if (search->handle == INVALID_SEARCH_HANDLE) return;
64         talloc_free(search->te);
65         search->te = event_add_timed(ev, search, 
66                                      timeval_current_ofs(search->pvfs->search.inactivity_time, 0), 
67                                      pvfs_search_timer, search);
68 }
69
70 /*
71   fill in a single search result for a given info level
72 */
73 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
74                                  enum smb_search_data_level level,
75                                  const char *unix_path,
76                                  const char *fname, 
77                                  struct pvfs_search_state *search,
78                                  off_t dir_offset,
79                                  union smb_search_data *file)
80 {
81         struct pvfs_filename *name;
82         NTSTATUS status;
83         const char *shortname;
84         uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code 
85                                                       in pvfs_list_seek_ofs() for 
86                                                       how we cope with this */
87
88         status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
89         if (!NT_STATUS_IS_OK(status)) {
90                 return status;
91         }
92
93         status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
94         if (!NT_STATUS_IS_OK(status)) {
95                 return status;
96         }
97
98         switch (level) {
99         case RAW_SEARCH_DATA_SEARCH:
100                 shortname = pvfs_short_name(pvfs, name, name);
101                 file->search.attrib           = name->dos.attrib;
102                 file->search.write_time       = nt_time_to_unix(name->dos.write_time);
103                 file->search.size             = name->st.st_size;
104                 file->search.name             = shortname;
105                 file->search.id.reserved      = search->handle >> 8;
106                 memset(file->search.id.name, ' ', sizeof(file->search.id.name));
107                 memcpy(file->search.id.name, shortname, 
108                        MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
109                 file->search.id.handle        = search->handle & 0xFF;
110                 file->search.id.server_cookie = dir_index;
111                 file->search.id.client_cookie = 0;
112                 return NT_STATUS_OK;
113
114         case RAW_SEARCH_DATA_STANDARD:
115                 file->standard.resume_key   = dir_index;
116                 file->standard.create_time  = nt_time_to_unix(name->dos.create_time);
117                 file->standard.access_time  = nt_time_to_unix(name->dos.access_time);
118                 file->standard.write_time   = nt_time_to_unix(name->dos.write_time);
119                 file->standard.size         = name->st.st_size;
120                 file->standard.alloc_size   = name->dos.alloc_size;
121                 file->standard.attrib       = name->dos.attrib;
122                 file->standard.name.s       = fname;
123                 return NT_STATUS_OK;
124
125         case RAW_SEARCH_DATA_EA_SIZE:
126                 file->ea_size.resume_key   = dir_index;
127                 file->ea_size.create_time  = nt_time_to_unix(name->dos.create_time);
128                 file->ea_size.access_time  = nt_time_to_unix(name->dos.access_time);
129                 file->ea_size.write_time   = nt_time_to_unix(name->dos.write_time);
130                 file->ea_size.size         = name->st.st_size;
131                 file->ea_size.alloc_size   = name->dos.alloc_size;
132                 file->ea_size.attrib       = name->dos.attrib;
133                 file->ea_size.ea_size      = name->dos.ea_size;
134                 file->ea_size.name.s       = fname;
135                 return NT_STATUS_OK;
136
137         case RAW_SEARCH_DATA_EA_LIST:
138                 file->ea_list.resume_key   = dir_index;
139                 file->ea_list.create_time  = nt_time_to_unix(name->dos.create_time);
140                 file->ea_list.access_time  = nt_time_to_unix(name->dos.access_time);
141                 file->ea_list.write_time   = nt_time_to_unix(name->dos.write_time);
142                 file->ea_list.size         = name->st.st_size;
143                 file->ea_list.alloc_size   = name->dos.alloc_size;
144                 file->ea_list.attrib       = name->dos.attrib;
145                 file->ea_list.name.s       = fname;
146                 return pvfs_query_ea_list(pvfs, file, name, -1, 
147                                           search->num_ea_names,
148                                           search->ea_names,
149                                           &file->ea_list.eas);
150
151         case RAW_SEARCH_DATA_DIRECTORY_INFO:
152                 file->directory_info.file_index   = dir_index;
153                 file->directory_info.create_time  = name->dos.create_time;
154                 file->directory_info.access_time  = name->dos.access_time;
155                 file->directory_info.write_time   = name->dos.write_time;
156                 file->directory_info.change_time  = name->dos.change_time;
157                 file->directory_info.size         = name->st.st_size;
158                 file->directory_info.alloc_size   = name->dos.alloc_size;
159                 file->directory_info.attrib       = name->dos.attrib;
160                 file->directory_info.name.s       = fname;
161                 return NT_STATUS_OK;
162
163         case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
164                 file->full_directory_info.file_index   = dir_index;
165                 file->full_directory_info.create_time  = name->dos.create_time;
166                 file->full_directory_info.access_time  = name->dos.access_time;
167                 file->full_directory_info.write_time   = name->dos.write_time;
168                 file->full_directory_info.change_time  = name->dos.change_time;
169                 file->full_directory_info.size         = name->st.st_size;
170                 file->full_directory_info.alloc_size   = name->dos.alloc_size;
171                 file->full_directory_info.attrib       = name->dos.attrib;
172                 file->full_directory_info.ea_size      = name->dos.ea_size;
173                 file->full_directory_info.name.s       = fname;
174                 return NT_STATUS_OK;
175
176         case RAW_SEARCH_DATA_NAME_INFO:
177                 file->name_info.file_index   = dir_index;
178                 file->name_info.name.s       = fname;
179                 return NT_STATUS_OK;
180
181         case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
182                 file->both_directory_info.file_index   = dir_index;
183                 file->both_directory_info.create_time  = name->dos.create_time;
184                 file->both_directory_info.access_time  = name->dos.access_time;
185                 file->both_directory_info.write_time   = name->dos.write_time;
186                 file->both_directory_info.change_time  = name->dos.change_time;
187                 file->both_directory_info.size         = name->st.st_size;
188                 file->both_directory_info.alloc_size   = name->dos.alloc_size;
189                 file->both_directory_info.attrib       = name->dos.attrib;
190                 file->both_directory_info.ea_size      = name->dos.ea_size;
191                 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
192                 file->both_directory_info.name.s       = fname;
193                 return NT_STATUS_OK;
194
195         case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
196                 file->id_full_directory_info.file_index   = dir_index;
197                 file->id_full_directory_info.create_time  = name->dos.create_time;
198                 file->id_full_directory_info.access_time  = name->dos.access_time;
199                 file->id_full_directory_info.write_time   = name->dos.write_time;
200                 file->id_full_directory_info.change_time  = name->dos.change_time;
201                 file->id_full_directory_info.size         = name->st.st_size;
202                 file->id_full_directory_info.alloc_size   = name->dos.alloc_size;
203                 file->id_full_directory_info.attrib       = name->dos.attrib;
204                 file->id_full_directory_info.ea_size      = name->dos.ea_size;
205                 file->id_full_directory_info.file_id      = name->dos.file_id;
206                 file->id_full_directory_info.name.s       = fname;
207                 return NT_STATUS_OK;
208
209         case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
210                 file->id_both_directory_info.file_index   = dir_index;
211                 file->id_both_directory_info.create_time  = name->dos.create_time;
212                 file->id_both_directory_info.access_time  = name->dos.access_time;
213                 file->id_both_directory_info.write_time   = name->dos.write_time;
214                 file->id_both_directory_info.change_time  = name->dos.change_time;
215                 file->id_both_directory_info.size         = name->st.st_size;
216                 file->id_both_directory_info.alloc_size   = name->dos.alloc_size;
217                 file->id_both_directory_info.attrib       = name->dos.attrib;
218                 file->id_both_directory_info.ea_size      = name->dos.ea_size;
219                 file->id_both_directory_info.file_id      = name->dos.file_id;
220                 file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
221                 file->id_both_directory_info.name.s       = fname;
222                 return NT_STATUS_OK;
223
224         case RAW_SEARCH_DATA_GENERIC:
225                 break;
226         }
227
228         return NT_STATUS_INVALID_LEVEL;
229 }
230
231
232 /*
233   the search fill loop
234 */
235 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, 
236                                  uint_t max_count, 
237                                  struct pvfs_search_state *search,
238                                  enum smb_search_data_level level,
239                                  uint_t *reply_count,
240                                  void *search_private, 
241                                  BOOL (*callback)(void *, union smb_search_data *))
242 {
243         struct pvfs_dir *dir = search->dir;
244         NTSTATUS status;
245
246         *reply_count = 0;
247
248         if (max_count == 0) {
249                 max_count = 1;
250         }
251
252         while ((*reply_count) < max_count) {
253                 union smb_search_data *file;
254                 const char *name;
255                 off_t ofs = search->current_index;
256
257                 name = pvfs_list_next(dir, &search->current_index);
258                 if (name == NULL) break;
259
260                 file = talloc(mem_ctx, union smb_search_data);
261                 if (!file) {
262                         return NT_STATUS_NO_MEMORY;
263                 }
264
265                 status = fill_search_info(pvfs, level, 
266                                           pvfs_list_unix_path(dir), name, 
267                                           search, search->current_index, file);
268                 if (!NT_STATUS_IS_OK(status)) {
269                         talloc_free(file);
270                         continue;
271                 }
272
273                 if (!callback(search_private, file)) {
274                         talloc_free(file);
275                         search->current_index = ofs;
276                         break;
277                 }
278
279                 (*reply_count)++;
280                 talloc_free(file);
281         }
282
283         pvfs_search_setup_timer(search);
284
285         return NT_STATUS_OK;
286 }
287
288 /*
289   we've run out of search handles - cleanup those that the client forgot
290   to close
291 */
292 static void pvfs_search_cleanup(struct pvfs_state *pvfs)
293 {
294         int i;
295         time_t t = time(NULL);
296
297         for (i=0;i<MAX_OLD_SEARCHES;i++) {
298                 struct pvfs_search_state *search = idr_find(pvfs->search.idtree, i);
299                 if (search == NULL) return;
300                 if (pvfs_list_eos(search->dir, search->current_index) &&
301                     search->last_used != 0 &&
302                     t > search->last_used + 30) {
303                         /* its almost certainly been forgotten
304                          about */
305                         talloc_free(search);
306                 }
307         }
308 }
309
310
311 /* 
312    list files in a directory matching a wildcard pattern - old SMBsearch interface
313 */
314 static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
315                                       struct ntvfs_request *req, union smb_search_first *io, 
316                                       void *search_private, 
317                                       BOOL (*callback)(void *, union smb_search_data *))
318 {
319         struct pvfs_dir *dir;
320         struct pvfs_state *pvfs = ntvfs->private_data;
321         struct pvfs_search_state *search;
322         uint_t reply_count;
323         uint16_t search_attrib;
324         const char *pattern;
325         NTSTATUS status;
326         struct pvfs_filename *name;
327         int id;
328
329         search_attrib = io->search_first.in.search_attrib;
330         pattern       = io->search_first.in.pattern;
331
332         /* resolve the cifs name to a posix name */
333         status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
334         if (!NT_STATUS_IS_OK(status)) {
335                 return status;
336         }
337
338         if (!name->has_wildcard && !name->exists) {
339                 return STATUS_NO_MORE_FILES;
340         }
341
342         status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
343         if (!NT_STATUS_IS_OK(status)) {
344                 return status;
345         }
346
347         /* we initially make search a child of the request, then if we
348            need to keep it long term we steal it for the private
349            structure */
350         search = talloc(req, struct pvfs_search_state);
351         if (!search) {
352                 return NT_STATUS_NO_MEMORY;
353         }
354
355         /* do the actual directory listing */
356         status = pvfs_list_start(pvfs, name, search, &dir);
357         if (!NT_STATUS_IS_OK(status)) {
358                 return status;
359         }
360
361         /* we need to give a handle back to the client so it
362            can continue a search */
363         id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
364         if (id == -1) {
365                 pvfs_search_cleanup(pvfs);
366                 id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
367         }
368         if (id == -1) {
369                 return NT_STATUS_INSUFFICIENT_RESOURCES;
370         }
371
372         search->pvfs = pvfs;
373         search->handle = id;
374         search->dir = dir;
375         search->current_index = 0;
376         search->search_attrib = search_attrib & 0xFF;
377         search->must_attrib = (search_attrib>>8) & 0xFF;
378         search->last_used = time(NULL);
379         search->te = NULL;
380
381         DLIST_ADD(pvfs->search.list, search);
382
383         talloc_set_destructor(search, pvfs_search_destructor);
384
385         status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
386                                   &reply_count, search_private, callback);
387         if (!NT_STATUS_IS_OK(status)) {
388                 return status;
389         }
390
391         io->search_first.out.count = reply_count;
392
393         /* not matching any entries is an error */
394         if (reply_count == 0) {
395                 return STATUS_NO_MORE_FILES;
396         }
397
398         talloc_steal(pvfs, search);
399
400         return NT_STATUS_OK;
401 }
402
403 /* continue a old style search */
404 static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
405                                      struct ntvfs_request *req, union smb_search_next *io, 
406                                      void *search_private, 
407                                      BOOL (*callback)(void *, union smb_search_data *))
408 {
409         struct pvfs_state *pvfs = ntvfs->private_data;
410         struct pvfs_search_state *search;
411         struct pvfs_dir *dir;
412         uint_t reply_count, max_count;
413         uint16_t handle;
414         NTSTATUS status;
415
416         handle    = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
417         max_count = io->search_next.in.max_count;
418
419         search = idr_find(pvfs->search.idtree, handle);
420         if (search == NULL) {
421                 /* we didn't find the search handle */
422                 return NT_STATUS_INVALID_HANDLE;
423         }
424
425         dir = search->dir;
426
427         status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie, 
428                                     &search->current_index);
429         if (!NT_STATUS_IS_OK(status)) {
430                 return status;
431         }
432         search->last_used = time(NULL);
433
434         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
435                                   &reply_count, search_private, callback);
436         if (!NT_STATUS_IS_OK(status)) {
437                 return status;
438         }
439
440         io->search_next.out.count = reply_count;
441
442         /* not matching any entries means end of search */
443         if (reply_count == 0) {
444                 talloc_free(search);
445         }
446
447         return NT_STATUS_OK;
448 }
449
450 /* 
451    list files in a directory matching a wildcard pattern
452 */
453 static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
454                                          struct ntvfs_request *req, union smb_search_first *io, 
455                                          void *search_private, 
456                                          BOOL (*callback)(void *, union smb_search_data *))
457 {
458         struct pvfs_dir *dir;
459         struct pvfs_state *pvfs = ntvfs->private_data;
460         struct pvfs_search_state *search;
461         uint_t reply_count;
462         uint16_t search_attrib, max_count;
463         const char *pattern;
464         NTSTATUS status;
465         struct pvfs_filename *name;
466         int id;
467
468         search_attrib = io->t2ffirst.in.search_attrib;
469         pattern       = io->t2ffirst.in.pattern;
470         max_count     = io->t2ffirst.in.max_count;
471
472         /* resolve the cifs name to a posix name */
473         status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
474         if (!NT_STATUS_IS_OK(status)) {
475                 return status;
476         }
477
478         if (!name->has_wildcard && !name->exists) {
479                 return NT_STATUS_NO_SUCH_FILE;
480         }
481
482         status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
483         if (!NT_STATUS_IS_OK(status)) {
484                 return status;
485         }
486
487         /* we initially make search a child of the request, then if we
488            need to keep it long term we steal it for the private
489            structure */
490         search = talloc(req, struct pvfs_search_state);
491         if (!search) {
492                 return NT_STATUS_NO_MEMORY;
493         }
494
495         /* do the actual directory listing */
496         status = pvfs_list_start(pvfs, name, search, &dir);
497         if (!NT_STATUS_IS_OK(status)) {
498                 return status;
499         }
500
501         id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
502         if (id == -1) {
503                 return NT_STATUS_INSUFFICIENT_RESOURCES;
504         }
505
506         search->pvfs = pvfs;
507         search->handle = id;
508         search->dir = dir;
509         search->current_index = 0;
510         search->search_attrib = search_attrib;
511         search->must_attrib = 0;
512         search->last_used = 0;
513         search->num_ea_names = io->t2ffirst.in.num_names;
514         search->ea_names = io->t2ffirst.in.ea_names;
515         search->te = NULL;
516
517         DLIST_ADD(pvfs->search.list, search);
518         talloc_set_destructor(search, pvfs_search_destructor);
519
520         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
521                                   &reply_count, search_private, callback);
522         if (!NT_STATUS_IS_OK(status)) {
523                 return status;
524         }
525
526         /* not matching any entries is an error */
527         if (reply_count == 0) {
528                 return NT_STATUS_NO_SUCH_FILE;
529         }
530
531         io->t2ffirst.out.count = reply_count;
532         io->t2ffirst.out.handle = search->handle;
533         io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
534
535         /* work out if we are going to keep the search state
536            and allow for a search continue */
537         if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
538             ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
539              io->t2ffirst.out.end_of_search)) {
540                 talloc_free(search);
541         } else {
542                 talloc_steal(pvfs, search);
543         }
544
545         return NT_STATUS_OK;
546 }
547
548 /* continue a search */
549 static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
550                                         struct ntvfs_request *req, union smb_search_next *io, 
551                                         void *search_private, 
552                                         BOOL (*callback)(void *, union smb_search_data *))
553 {
554         struct pvfs_state *pvfs = ntvfs->private_data;
555         struct pvfs_search_state *search;
556         struct pvfs_dir *dir;
557         uint_t reply_count;
558         uint16_t handle;
559         NTSTATUS status;
560
561         handle = io->t2fnext.in.handle;
562
563         search = idr_find(pvfs->search.idtree, handle);
564         if (search == NULL) {
565                 /* we didn't find the search handle */
566                 return NT_STATUS_INVALID_HANDLE;
567         }
568         
569         dir = search->dir;
570         
571         status = NT_STATUS_OK;
572
573         /* work out what type of continuation is being used */
574         if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
575                 status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
576                 if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
577                         status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
578                                                     &search->current_index);
579                 }
580         } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
581                 status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
582                                             &search->current_index);
583         }
584         if (!NT_STATUS_IS_OK(status)) {
585                 return status;
586         }
587
588         search->num_ea_names = io->t2fnext.in.num_names;
589         search->ea_names = io->t2fnext.in.ea_names;
590
591         status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
592                                   &reply_count, search_private, callback);
593         if (!NT_STATUS_IS_OK(status)) {
594                 return status;
595         }
596
597         io->t2fnext.out.count = reply_count;
598         io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
599
600         /* work out if we are going to keep the search state */
601         if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
602             ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
603              io->t2fnext.out.end_of_search)) {
604                 talloc_free(search);
605         }
606
607         return NT_STATUS_OK;
608 }
609
610 static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
611                                        struct ntvfs_request *req, const struct smb2_find *io, 
612                                        void *search_private, 
613                                        BOOL (*callback)(void *, union smb_search_data *))
614 {
615         struct pvfs_dir *dir;
616         struct pvfs_state *pvfs = ntvfs->private_data;
617         struct pvfs_search_state *search;
618         uint_t reply_count;
619         uint16_t max_count;
620         const char *pattern;
621         NTSTATUS status;
622         struct pvfs_filename *name;
623         struct pvfs_file *f;
624
625         f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
626         if (!f) {
627                 return NT_STATUS_FILE_CLOSED;
628         }
629
630         /* its only valid for directories */
631         if (f->handle->fd != -1) {
632                 return NT_STATUS_INVALID_PARAMETER;
633         }
634
635         if (!(f->access_mask & SEC_DIR_LIST)) {
636                 return NT_STATUS_ACCESS_DENIED;
637         }
638
639         if (f->search) {
640                 talloc_free(f->search);
641                 f->search = NULL;
642         }
643
644         if (strequal(io->in.pattern, "")) {
645                 return NT_STATUS_OBJECT_NAME_INVALID;
646         }
647         if (strchr_m(io->in.pattern, '\\')) {
648                 return NT_STATUS_OBJECT_NAME_INVALID;
649         }
650         if (strchr_m(io->in.pattern, '/')) {
651                 return NT_STATUS_OBJECT_NAME_INVALID;
652         }
653
654         if (strequal("", f->handle->name->original_name)) {
655                 pattern = talloc_asprintf(req, "\\%s", io->in.pattern);
656                 NT_STATUS_HAVE_NO_MEMORY(pattern);
657         } else {
658                 pattern = talloc_asprintf(req, "\\%s\\%s",
659                                           f->handle->name->original_name,
660                                           io->in.pattern);
661                 NT_STATUS_HAVE_NO_MEMORY(pattern);
662         }
663
664         /* resolve the cifs name to a posix name */
665         status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
666         NT_STATUS_NOT_OK_RETURN(status);
667
668         if (!name->has_wildcard && !name->exists) {
669                 return NT_STATUS_NO_SUCH_FILE;
670         }
671
672         /* we initially make search a child of the request, then if we
673            need to keep it long term we steal it for the private
674            structure */
675         search = talloc(req, struct pvfs_search_state);
676         NT_STATUS_HAVE_NO_MEMORY(search);
677
678         /* do the actual directory listing */
679         status = pvfs_list_start(pvfs, name, search, &dir);
680         NT_STATUS_NOT_OK_RETURN(status);
681
682         search->pvfs            = pvfs;
683         search->handle          = INVALID_SEARCH_HANDLE;
684         search->dir             = dir;
685         search->current_index   = 0;
686         search->search_attrib   = 0;
687         search->must_attrib     = 0;
688         search->last_used       = 0;
689         search->num_ea_names    = 0;
690         search->ea_names        = NULL;
691         search->te              = NULL;
692
693         if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
694                 max_count = 1;
695         } else {
696                 max_count = UINT16_MAX;
697         }
698
699         status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
700                                   &reply_count, search_private, callback);
701         NT_STATUS_NOT_OK_RETURN(status);
702
703         /* not matching any entries is an error */
704         if (reply_count == 0) {
705                 return NT_STATUS_NO_SUCH_FILE;
706         }
707
708         f->search = talloc_steal(f, search);
709
710         return NT_STATUS_OK;
711 }
712
713 static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
714                                       struct ntvfs_request *req, const struct smb2_find *io, 
715                                       void *search_private, 
716                                       BOOL (*callback)(void *, union smb_search_data *))
717 {
718         struct pvfs_state *pvfs = ntvfs->private_data;
719         struct pvfs_search_state *search;
720         uint_t reply_count;
721         uint16_t max_count;
722         NTSTATUS status;
723         struct pvfs_file *f;
724
725         f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
726         if (!f) {
727                 return NT_STATUS_FILE_CLOSED;
728         }
729
730         /* its only valid for directories */
731         if (f->handle->fd != -1) {
732                 return NT_STATUS_INVALID_PARAMETER;
733         }
734
735         /* if there's no search started on the dir handle, it's like a search_first */
736         search = f->search;
737         if (!search) {
738                 return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
739         }
740
741         if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
742                 search->current_index = 0;
743         }
744
745         if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
746                 max_count = 1;
747         } else {
748                 max_count = UINT16_MAX;
749         }
750
751         status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
752                                   &reply_count, search_private, callback);
753         NT_STATUS_NOT_OK_RETURN(status);
754
755         /* not matching any entries is an error */
756         if (reply_count == 0) {
757                 return STATUS_NO_MORE_FILES;
758         }
759
760         return NT_STATUS_OK;
761 }
762
763 /* 
764    list files in a directory matching a wildcard pattern
765 */
766 NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
767                            struct ntvfs_request *req, union smb_search_first *io, 
768                            void *search_private, 
769                            BOOL (*callback)(void *, union smb_search_data *))
770 {
771         switch (io->generic.level) {
772         case RAW_SEARCH_SEARCH:
773         case RAW_SEARCH_FFIRST:
774         case RAW_SEARCH_FUNIQUE:
775                 return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
776
777         case RAW_SEARCH_TRANS2:
778                 return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
779
780         case RAW_SEARCH_SMB2:
781                 return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
782         }
783
784         return NT_STATUS_INVALID_LEVEL;
785 }
786
787 /* continue a search */
788 NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
789                           struct ntvfs_request *req, union smb_search_next *io, 
790                           void *search_private, 
791                           BOOL (*callback)(void *, union smb_search_data *))
792 {
793         switch (io->generic.level) {
794         case RAW_SEARCH_SEARCH:
795         case RAW_SEARCH_FFIRST:
796                 return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
797
798         case RAW_SEARCH_FUNIQUE:
799                 return NT_STATUS_INVALID_LEVEL;
800
801         case RAW_SEARCH_TRANS2:
802                 return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
803
804         case RAW_SEARCH_SMB2:
805                 return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
806         }
807
808         return NT_STATUS_INVALID_LEVEL;
809 }
810
811
812 /* close a search */
813 NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
814                            struct ntvfs_request *req, union smb_search_close *io)
815 {
816         struct pvfs_state *pvfs = ntvfs->private_data;
817         struct pvfs_search_state *search;
818         uint16_t handle = INVALID_SEARCH_HANDLE;
819
820         switch (io->generic.level) {
821         case RAW_FINDCLOSE_GENERIC:
822                 return NT_STATUS_INVALID_LEVEL;
823
824         case RAW_FINDCLOSE_FCLOSE:
825                 handle = io->fclose.in.id.handle;
826                 break;
827
828         case RAW_FINDCLOSE_FINDCLOSE:
829                 handle = io->findclose.in.handle;
830                 break;
831         }
832
833         search = idr_find(pvfs->search.idtree, handle);
834         if (search == NULL) {
835                 /* we didn't find the search handle */
836                 return NT_STATUS_INVALID_HANDLE;
837         }
838
839         talloc_free(search);
840
841         return NT_STATUS_OK;
842 }
843