Merge commit 'release-4-0-0alpha15' into master4-tmp
[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 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 tevent_context *ev, struct tevent_timer *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 tevent_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, 0, &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         case RAW_SEARCH_DATA_UNIX_INFO:
225         case RAW_SEARCH_DATA_UNIX_INFO2:
226                 return NT_STATUS_INVALID_LEVEL;
227         }
228
229         return NT_STATUS_INVALID_LEVEL;
230 }
231
232
233 /*
234   the search fill loop
235 */
236 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, 
237                                  unsigned int max_count,
238                                  struct pvfs_search_state *search,
239                                  enum smb_search_data_level level,
240                                  unsigned int *reply_count,
241                                  void *search_private, 
242                                  bool (*callback)(void *, const union smb_search_data *))
243 {
244         struct pvfs_dir *dir = search->dir;
245         NTSTATUS status;
246
247         *reply_count = 0;
248
249         if (max_count == 0) {
250                 max_count = 1;
251         }
252
253         while ((*reply_count) < max_count) {
254                 union smb_search_data *file;
255                 const char *name;
256                 off_t ofs = search->current_index;
257
258                 name = pvfs_list_next(dir, &search->current_index);
259                 if (name == NULL) break;
260
261                 file = talloc(mem_ctx, union smb_search_data);
262                 if (!file) {
263                         return NT_STATUS_NO_MEMORY;
264                 }
265
266                 status = fill_search_info(pvfs, level, 
267                                           pvfs_list_unix_path(dir), name, 
268                                           search, search->current_index, file);
269                 if (!NT_STATUS_IS_OK(status)) {
270                         talloc_free(file);
271                         continue;
272                 }
273
274                 if (!callback(search_private, file)) {
275                         talloc_free(file);
276                         search->current_index = ofs;
277                         break;
278                 }
279
280                 (*reply_count)++;
281                 talloc_free(file);
282         }
283
284         pvfs_search_setup_timer(search);
285
286         return NT_STATUS_OK;
287 }
288
289 /*
290   we've run out of search handles - cleanup those that the client forgot
291   to close
292 */
293 static void pvfs_search_cleanup(struct pvfs_state *pvfs)
294 {
295         int i;
296         time_t t = time_mono(NULL);
297
298         for (i=0;i<MAX_OLD_SEARCHES;i++) {
299                 struct pvfs_search_state *search;
300                 void *p = idr_find(pvfs->search.idtree, i);
301
302                 if (p == NULL) return;
303
304                 search = talloc_get_type(p, struct pvfs_search_state);
305                 if (pvfs_list_eos(search->dir, search->current_index) &&
306                     search->last_used != 0 &&
307                     t > search->last_used + 30) {
308                         /* its almost certainly been forgotten
309                          about */
310                         talloc_free(search);
311                 }
312         }
313 }
314
315
316 /* 
317    list files in a directory matching a wildcard pattern - old SMBsearch interface
318 */
319 static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
320                                       struct ntvfs_request *req, union smb_search_first *io, 
321                                       void *search_private, 
322                                       bool (*callback)(void *, const union smb_search_data *))
323 {
324         struct pvfs_dir *dir;
325         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
326                                   struct pvfs_state);
327         struct pvfs_search_state *search;
328         unsigned int reply_count;
329         uint16_t search_attrib;
330         const char *pattern;
331         NTSTATUS status;
332         struct pvfs_filename *name;
333         int id;
334
335         search_attrib = io->search_first.in.search_attrib;
336         pattern       = io->search_first.in.pattern;
337
338         /* resolve the cifs name to a posix name */
339         status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
340         if (!NT_STATUS_IS_OK(status)) {
341                 return status;
342         }
343
344         if (!name->has_wildcard && !name->exists) {
345                 return STATUS_NO_MORE_FILES;
346         }
347
348         status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
349         if (!NT_STATUS_IS_OK(status)) {
350                 return status;
351         }
352
353         /* we initially make search a child of the request, then if we
354            need to keep it long term we steal it for the private
355            structure */
356         search = talloc(req, struct pvfs_search_state);
357         if (!search) {
358                 return NT_STATUS_NO_MEMORY;
359         }
360
361         /* do the actual directory listing */
362         status = pvfs_list_start(pvfs, name, search, &dir);
363         if (!NT_STATUS_IS_OK(status)) {
364                 return status;
365         }
366
367         /* we need to give a handle back to the client so it
368            can continue a search */
369         id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
370         if (id == -1) {
371                 pvfs_search_cleanup(pvfs);
372                 id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
373         }
374         if (id == -1) {
375                 return NT_STATUS_INSUFFICIENT_RESOURCES;
376         }
377
378         search->pvfs = pvfs;
379         search->handle = id;
380         search->dir = dir;
381         search->current_index = 0;
382         search->search_attrib = search_attrib & 0xFF;
383         search->must_attrib = (search_attrib>>8) & 0xFF;
384         search->last_used = time_mono(NULL);
385         search->te = NULL;
386
387         DLIST_ADD(pvfs->search.list, search);
388
389         talloc_set_destructor(search, pvfs_search_destructor);
390
391         status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
392                                   &reply_count, search_private, callback);
393         if (!NT_STATUS_IS_OK(status)) {
394                 return status;
395         }
396
397         io->search_first.out.count = reply_count;
398
399         /* not matching any entries is an error */
400         if (reply_count == 0) {
401                 return STATUS_NO_MORE_FILES;
402         }
403
404         talloc_steal(pvfs, search);
405
406         return NT_STATUS_OK;
407 }
408
409 /* continue a old style search */
410 static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
411                                      struct ntvfs_request *req, union smb_search_next *io, 
412                                      void *search_private, 
413                                      bool (*callback)(void *, const union smb_search_data *))
414 {
415         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
416                                   struct pvfs_state);
417         void *p;
418         struct pvfs_search_state *search;
419         struct pvfs_dir *dir;
420         unsigned int reply_count, max_count;
421         uint16_t handle;
422         NTSTATUS status;
423
424         handle    = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
425         max_count = io->search_next.in.max_count;
426
427         p = idr_find(pvfs->search.idtree, handle);
428         if (p == NULL) {
429                 /* we didn't find the search handle */
430                 return NT_STATUS_INVALID_HANDLE;
431         }
432
433         search = talloc_get_type(p, struct pvfs_search_state);
434
435         dir = search->dir;
436
437         status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie, 
438                                     &search->current_index);
439         if (!NT_STATUS_IS_OK(status)) {
440                 return status;
441         }
442         search->last_used = time_mono(NULL);
443
444         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
445                                   &reply_count, search_private, callback);
446         if (!NT_STATUS_IS_OK(status)) {
447                 return status;
448         }
449
450         io->search_next.out.count = reply_count;
451
452         /* not matching any entries means end of search */
453         if (reply_count == 0) {
454                 talloc_free(search);
455         }
456
457         return NT_STATUS_OK;
458 }
459
460 /* 
461    list files in a directory matching a wildcard pattern
462 */
463 static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
464                                          struct ntvfs_request *req, union smb_search_first *io, 
465                                          void *search_private, 
466                                          bool (*callback)(void *, const union smb_search_data *))
467 {
468         struct pvfs_dir *dir;
469         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
470                                   struct pvfs_state);
471         struct pvfs_search_state *search;
472         unsigned int reply_count;
473         uint16_t search_attrib, max_count;
474         const char *pattern;
475         NTSTATUS status;
476         struct pvfs_filename *name;
477         int id;
478
479         search_attrib = io->t2ffirst.in.search_attrib;
480         pattern       = io->t2ffirst.in.pattern;
481         max_count     = io->t2ffirst.in.max_count;
482
483         /* resolve the cifs name to a posix name */
484         status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
485         if (!NT_STATUS_IS_OK(status)) {
486                 return status;
487         }
488
489         if (!name->has_wildcard && !name->exists) {
490                 return NT_STATUS_NO_SUCH_FILE;
491         }
492
493         status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
494         if (!NT_STATUS_IS_OK(status)) {
495                 return status;
496         }
497
498         /* we initially make search a child of the request, then if we
499            need to keep it long term we steal it for the private
500            structure */
501         search = talloc(req, struct pvfs_search_state);
502         if (!search) {
503                 return NT_STATUS_NO_MEMORY;
504         }
505
506         /* do the actual directory listing */
507         status = pvfs_list_start(pvfs, name, search, &dir);
508         if (!NT_STATUS_IS_OK(status)) {
509                 return status;
510         }
511
512         id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
513         if (id == -1) {
514                 return NT_STATUS_INSUFFICIENT_RESOURCES;
515         }
516
517         search->pvfs = pvfs;
518         search->handle = id;
519         search->dir = dir;
520         search->current_index = 0;
521         search->search_attrib = search_attrib;
522         search->must_attrib = 0;
523         search->last_used = 0;
524         search->num_ea_names = io->t2ffirst.in.num_names;
525         search->ea_names = io->t2ffirst.in.ea_names;
526         search->te = NULL;
527
528         DLIST_ADD(pvfs->search.list, search);
529         talloc_set_destructor(search, pvfs_search_destructor);
530
531         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
532                                   &reply_count, search_private, callback);
533         if (!NT_STATUS_IS_OK(status)) {
534                 return status;
535         }
536
537         /* not matching any entries is an error */
538         if (reply_count == 0) {
539                 return NT_STATUS_NO_SUCH_FILE;
540         }
541
542         io->t2ffirst.out.count = reply_count;
543         io->t2ffirst.out.handle = search->handle;
544         io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
545
546         /* work out if we are going to keep the search state
547            and allow for a search continue */
548         if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
549             ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
550              io->t2ffirst.out.end_of_search)) {
551                 talloc_free(search);
552         } else {
553                 talloc_steal(pvfs, search);
554         }
555
556         return NT_STATUS_OK;
557 }
558
559 /* continue a search */
560 static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
561                                         struct ntvfs_request *req, union smb_search_next *io, 
562                                         void *search_private, 
563                                         bool (*callback)(void *, const union smb_search_data *))
564 {
565         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
566                                   struct pvfs_state);
567         void *p;
568         struct pvfs_search_state *search;
569         struct pvfs_dir *dir;
570         unsigned int reply_count;
571         uint16_t handle;
572         NTSTATUS status;
573
574         handle = io->t2fnext.in.handle;
575
576         p = idr_find(pvfs->search.idtree, handle);
577         if (p == NULL) {
578                 /* we didn't find the search handle */
579                 return NT_STATUS_INVALID_HANDLE;
580         }
581
582         search = talloc_get_type(p, struct pvfs_search_state);
583
584         dir = search->dir;
585         
586         status = NT_STATUS_OK;
587
588         /* work out what type of continuation is being used */
589         if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
590                 status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
591                 if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
592                         status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
593                                                     &search->current_index);
594                 }
595         } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
596                 status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
597                                             &search->current_index);
598         }
599         if (!NT_STATUS_IS_OK(status)) {
600                 return status;
601         }
602
603         search->num_ea_names = io->t2fnext.in.num_names;
604         search->ea_names = io->t2fnext.in.ea_names;
605
606         status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
607                                   &reply_count, search_private, callback);
608         if (!NT_STATUS_IS_OK(status)) {
609                 return status;
610         }
611
612         io->t2fnext.out.count = reply_count;
613         io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
614
615         /* work out if we are going to keep the search state */
616         if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
617             ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
618              io->t2fnext.out.end_of_search)) {
619                 talloc_free(search);
620         }
621
622         return NT_STATUS_OK;
623 }
624
625 static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
626                                        struct ntvfs_request *req, const struct smb2_find *io, 
627                                        void *search_private, 
628                                        bool (*callback)(void *, const union smb_search_data *))
629 {
630         struct pvfs_dir *dir;
631         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
632                                   struct pvfs_state);
633         struct pvfs_search_state *search;
634         unsigned int reply_count;
635         uint16_t max_count;
636         const char *pattern;
637         NTSTATUS status;
638         struct pvfs_filename *name;
639         struct pvfs_file *f;
640
641         f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
642         if (!f) {
643                 return NT_STATUS_FILE_CLOSED;
644         }
645
646         /* its only valid for directories */
647         if (f->handle->fd != -1) {
648                 return NT_STATUS_INVALID_PARAMETER;
649         }
650
651         if (!(f->access_mask & SEC_DIR_LIST)) {
652                 return NT_STATUS_ACCESS_DENIED;
653         }
654
655         if (f->search) {
656                 talloc_free(f->search);
657                 f->search = NULL;
658         }
659
660         if (strequal(io->in.pattern, "")) {
661                 return NT_STATUS_OBJECT_NAME_INVALID;
662         }
663         if (strchr_m(io->in.pattern, '\\')) {
664                 return NT_STATUS_OBJECT_NAME_INVALID;
665         }
666         if (strchr_m(io->in.pattern, '/')) {
667                 return NT_STATUS_OBJECT_NAME_INVALID;
668         }
669
670         if (strequal("", f->handle->name->original_name)) {
671                 pattern = talloc_asprintf(req, "%s", io->in.pattern);
672                 NT_STATUS_HAVE_NO_MEMORY(pattern);
673         } else {
674                 pattern = talloc_asprintf(req, "%s\\%s",
675                                           f->handle->name->original_name,
676                                           io->in.pattern);
677                 NT_STATUS_HAVE_NO_MEMORY(pattern);
678         }
679
680         /* resolve the cifs name to a posix name */
681         status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
682         NT_STATUS_NOT_OK_RETURN(status);
683
684         if (!name->has_wildcard && !name->exists) {
685                 return NT_STATUS_NO_SUCH_FILE;
686         }
687
688         /* we initially make search a child of the request, then if we
689            need to keep it long term we steal it for the private
690            structure */
691         search = talloc(req, struct pvfs_search_state);
692         NT_STATUS_HAVE_NO_MEMORY(search);
693
694         /* do the actual directory listing */
695         status = pvfs_list_start(pvfs, name, search, &dir);
696         NT_STATUS_NOT_OK_RETURN(status);
697
698         search->pvfs            = pvfs;
699         search->handle          = INVALID_SEARCH_HANDLE;
700         search->dir             = dir;
701         search->current_index   = 0;
702         search->search_attrib   = 0x0000FFFF;
703         search->must_attrib     = 0;
704         search->last_used       = 0;
705         search->num_ea_names    = 0;
706         search->ea_names        = NULL;
707         search->te              = NULL;
708
709         if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
710                 max_count = 1;
711         } else {
712                 max_count = UINT16_MAX;
713         }
714
715         status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
716                                   &reply_count, search_private, callback);
717         NT_STATUS_NOT_OK_RETURN(status);
718
719         /* not matching any entries is an error */
720         if (reply_count == 0) {
721                 return NT_STATUS_NO_SUCH_FILE;
722         }
723
724         f->search = talloc_steal(f, search);
725
726         return NT_STATUS_OK;
727 }
728
729 static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
730                                       struct ntvfs_request *req, const struct smb2_find *io, 
731                                       void *search_private, 
732                                       bool (*callback)(void *, const union smb_search_data *))
733 {
734         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
735                                   struct pvfs_state);
736         struct pvfs_search_state *search;
737         unsigned int reply_count;
738         uint16_t max_count;
739         NTSTATUS status;
740         struct pvfs_file *f;
741
742         f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
743         if (!f) {
744                 return NT_STATUS_FILE_CLOSED;
745         }
746
747         /* its only valid for directories */
748         if (f->handle->fd != -1) {
749                 return NT_STATUS_INVALID_PARAMETER;
750         }
751
752         /* if there's no search started on the dir handle, it's like a search_first */
753         search = f->search;
754         if (!search) {
755                 return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
756         }
757
758         if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
759                 search->current_index = 0;
760         }
761
762         if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
763                 max_count = 1;
764         } else {
765                 max_count = UINT16_MAX;
766         }
767
768         status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
769                                   &reply_count, search_private, callback);
770         NT_STATUS_NOT_OK_RETURN(status);
771
772         /* not matching any entries is an error */
773         if (reply_count == 0) {
774                 return STATUS_NO_MORE_FILES;
775         }
776
777         return NT_STATUS_OK;
778 }
779
780 /* 
781    list files in a directory matching a wildcard pattern
782 */
783 NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
784                            struct ntvfs_request *req, union smb_search_first *io, 
785                            void *search_private, 
786                            bool (*callback)(void *, const union smb_search_data *))
787 {
788         switch (io->generic.level) {
789         case RAW_SEARCH_SEARCH:
790         case RAW_SEARCH_FFIRST:
791         case RAW_SEARCH_FUNIQUE:
792                 return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
793
794         case RAW_SEARCH_TRANS2:
795                 return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
796
797         case RAW_SEARCH_SMB2:
798                 return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
799         }
800
801         return NT_STATUS_INVALID_LEVEL;
802 }
803
804 /* continue a search */
805 NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
806                           struct ntvfs_request *req, union smb_search_next *io, 
807                           void *search_private, 
808                           bool (*callback)(void *, const union smb_search_data *))
809 {
810         switch (io->generic.level) {
811         case RAW_SEARCH_SEARCH:
812         case RAW_SEARCH_FFIRST:
813                 return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
814
815         case RAW_SEARCH_FUNIQUE:
816                 return NT_STATUS_INVALID_LEVEL;
817
818         case RAW_SEARCH_TRANS2:
819                 return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
820
821         case RAW_SEARCH_SMB2:
822                 return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
823         }
824
825         return NT_STATUS_INVALID_LEVEL;
826 }
827
828
829 /* close a search */
830 NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
831                            struct ntvfs_request *req, union smb_search_close *io)
832 {
833         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
834                                   struct pvfs_state);
835         void *p;
836         struct pvfs_search_state *search;
837         uint16_t handle = INVALID_SEARCH_HANDLE;
838
839         switch (io->generic.level) {
840         case RAW_FINDCLOSE_GENERIC:
841                 return NT_STATUS_INVALID_LEVEL;
842
843         case RAW_FINDCLOSE_FCLOSE:
844                 handle = io->fclose.in.id.handle;
845                 break;
846
847         case RAW_FINDCLOSE_FINDCLOSE:
848                 handle = io->findclose.in.handle;
849                 break;
850         }
851
852         p = idr_find(pvfs->search.idtree, handle);
853         if (p == NULL) {
854                 /* we didn't find the search handle */
855                 return NT_STATUS_INVALID_HANDLE;
856         }
857
858         search = talloc_get_type(p, struct pvfs_search_state);
859
860         talloc_free(search);
861
862         return NT_STATUS_OK;
863 }
864