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