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