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