2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - directory search functions
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
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.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "vfs_posix.h"
25 #include "system/time.h"
26 #include "librpc/gen_ndr/security.h"
27 #include "smbd/service_stream.h"
28 #include "lib/events/events.h"
29 #include "lib/util/dlinklist.h"
31 /* place a reasonable limit on old-style searches as clients tend to
32 not send search close requests */
33 #define MAX_OLD_SEARCHES 2000
34 #define MAX_SEARCH_HANDLES (UINT16_MAX - 1)
35 #define INVALID_SEARCH_HANDLE UINT16_MAX
38 destroy an open search
40 static int pvfs_search_destructor(struct pvfs_search_state *search)
42 DLIST_REMOVE(search->pvfs->search.list, search);
43 idr_remove(search->pvfs->search.idtree, search->handle);
48 called when a search timer goes off
50 static void pvfs_search_timer(struct event_context *ev, struct timed_event *te,
51 struct timeval t, void *ptr)
53 struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
58 setup a timer to destroy a open search after a inactivity period
60 static void pvfs_search_setup_timer(struct pvfs_search_state *search)
62 struct event_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
63 if (search->handle == INVALID_SEARCH_HANDLE) return;
64 talloc_free(search->te);
65 search->te = event_add_timed(ev, search,
66 timeval_current_ofs(search->pvfs->search.inactivity_time, 0),
67 pvfs_search_timer, search);
71 fill in a single search result for a given info level
73 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
74 enum smb_search_data_level level,
75 const char *unix_path,
77 struct pvfs_search_state *search,
79 union smb_search_data *file)
81 struct pvfs_filename *name;
83 const char *shortname;
84 uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code
85 in pvfs_list_seek_ofs() for
86 how we cope with this */
88 status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
89 if (!NT_STATUS_IS_OK(status)) {
93 status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
94 if (!NT_STATUS_IS_OK(status)) {
99 case RAW_SEARCH_DATA_SEARCH:
100 shortname = pvfs_short_name(pvfs, name, name);
101 file->search.attrib = name->dos.attrib;
102 file->search.write_time = nt_time_to_unix(name->dos.write_time);
103 file->search.size = name->st.st_size;
104 file->search.name = shortname;
105 file->search.id.reserved = search->handle >> 8;
106 memset(file->search.id.name, ' ', sizeof(file->search.id.name));
107 memcpy(file->search.id.name, shortname,
108 MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
109 file->search.id.handle = search->handle & 0xFF;
110 file->search.id.server_cookie = dir_index;
111 file->search.id.client_cookie = 0;
114 case RAW_SEARCH_DATA_STANDARD:
115 file->standard.resume_key = dir_index;
116 file->standard.create_time = nt_time_to_unix(name->dos.create_time);
117 file->standard.access_time = nt_time_to_unix(name->dos.access_time);
118 file->standard.write_time = nt_time_to_unix(name->dos.write_time);
119 file->standard.size = name->st.st_size;
120 file->standard.alloc_size = name->dos.alloc_size;
121 file->standard.attrib = name->dos.attrib;
122 file->standard.name.s = fname;
125 case RAW_SEARCH_DATA_EA_SIZE:
126 file->ea_size.resume_key = dir_index;
127 file->ea_size.create_time = nt_time_to_unix(name->dos.create_time);
128 file->ea_size.access_time = nt_time_to_unix(name->dos.access_time);
129 file->ea_size.write_time = nt_time_to_unix(name->dos.write_time);
130 file->ea_size.size = name->st.st_size;
131 file->ea_size.alloc_size = name->dos.alloc_size;
132 file->ea_size.attrib = name->dos.attrib;
133 file->ea_size.ea_size = name->dos.ea_size;
134 file->ea_size.name.s = fname;
137 case RAW_SEARCH_DATA_EA_LIST:
138 file->ea_list.resume_key = dir_index;
139 file->ea_list.create_time = nt_time_to_unix(name->dos.create_time);
140 file->ea_list.access_time = nt_time_to_unix(name->dos.access_time);
141 file->ea_list.write_time = nt_time_to_unix(name->dos.write_time);
142 file->ea_list.size = name->st.st_size;
143 file->ea_list.alloc_size = name->dos.alloc_size;
144 file->ea_list.attrib = name->dos.attrib;
145 file->ea_list.name.s = fname;
146 return pvfs_query_ea_list(pvfs, file, name, -1,
147 search->num_ea_names,
151 case RAW_SEARCH_DATA_DIRECTORY_INFO:
152 file->directory_info.file_index = dir_index;
153 file->directory_info.create_time = name->dos.create_time;
154 file->directory_info.access_time = name->dos.access_time;
155 file->directory_info.write_time = name->dos.write_time;
156 file->directory_info.change_time = name->dos.change_time;
157 file->directory_info.size = name->st.st_size;
158 file->directory_info.alloc_size = name->dos.alloc_size;
159 file->directory_info.attrib = name->dos.attrib;
160 file->directory_info.name.s = fname;
163 case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
164 file->full_directory_info.file_index = dir_index;
165 file->full_directory_info.create_time = name->dos.create_time;
166 file->full_directory_info.access_time = name->dos.access_time;
167 file->full_directory_info.write_time = name->dos.write_time;
168 file->full_directory_info.change_time = name->dos.change_time;
169 file->full_directory_info.size = name->st.st_size;
170 file->full_directory_info.alloc_size = name->dos.alloc_size;
171 file->full_directory_info.attrib = name->dos.attrib;
172 file->full_directory_info.ea_size = name->dos.ea_size;
173 file->full_directory_info.name.s = fname;
176 case RAW_SEARCH_DATA_NAME_INFO:
177 file->name_info.file_index = dir_index;
178 file->name_info.name.s = fname;
181 case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
182 file->both_directory_info.file_index = dir_index;
183 file->both_directory_info.create_time = name->dos.create_time;
184 file->both_directory_info.access_time = name->dos.access_time;
185 file->both_directory_info.write_time = name->dos.write_time;
186 file->both_directory_info.change_time = name->dos.change_time;
187 file->both_directory_info.size = name->st.st_size;
188 file->both_directory_info.alloc_size = name->dos.alloc_size;
189 file->both_directory_info.attrib = name->dos.attrib;
190 file->both_directory_info.ea_size = name->dos.ea_size;
191 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
192 file->both_directory_info.name.s = fname;
195 case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
196 file->id_full_directory_info.file_index = dir_index;
197 file->id_full_directory_info.create_time = name->dos.create_time;
198 file->id_full_directory_info.access_time = name->dos.access_time;
199 file->id_full_directory_info.write_time = name->dos.write_time;
200 file->id_full_directory_info.change_time = name->dos.change_time;
201 file->id_full_directory_info.size = name->st.st_size;
202 file->id_full_directory_info.alloc_size = name->dos.alloc_size;
203 file->id_full_directory_info.attrib = name->dos.attrib;
204 file->id_full_directory_info.ea_size = name->dos.ea_size;
205 file->id_full_directory_info.file_id = name->dos.file_id;
206 file->id_full_directory_info.name.s = fname;
209 case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
210 file->id_both_directory_info.file_index = dir_index;
211 file->id_both_directory_info.create_time = name->dos.create_time;
212 file->id_both_directory_info.access_time = name->dos.access_time;
213 file->id_both_directory_info.write_time = name->dos.write_time;
214 file->id_both_directory_info.change_time = name->dos.change_time;
215 file->id_both_directory_info.size = name->st.st_size;
216 file->id_both_directory_info.alloc_size = name->dos.alloc_size;
217 file->id_both_directory_info.attrib = name->dos.attrib;
218 file->id_both_directory_info.ea_size = name->dos.ea_size;
219 file->id_both_directory_info.file_id = name->dos.file_id;
220 file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
221 file->id_both_directory_info.name.s = fname;
224 case RAW_SEARCH_DATA_GENERIC:
228 return NT_STATUS_INVALID_LEVEL;
235 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
237 struct pvfs_search_state *search,
238 enum smb_search_data_level level,
240 void *search_private,
241 BOOL (*callback)(void *, union smb_search_data *))
243 struct pvfs_dir *dir = search->dir;
248 if (max_count == 0) {
252 while ((*reply_count) < max_count) {
253 union smb_search_data *file;
255 off_t ofs = search->current_index;
257 name = pvfs_list_next(dir, &search->current_index);
258 if (name == NULL) break;
260 file = talloc(mem_ctx, union smb_search_data);
262 return NT_STATUS_NO_MEMORY;
265 status = fill_search_info(pvfs, level,
266 pvfs_list_unix_path(dir), name,
267 search, search->current_index, file);
268 if (!NT_STATUS_IS_OK(status)) {
273 if (!callback(search_private, file)) {
275 search->current_index = ofs;
283 pvfs_search_setup_timer(search);
289 we've run out of search handles - cleanup those that the client forgot
292 static void pvfs_search_cleanup(struct pvfs_state *pvfs)
295 time_t t = time(NULL);
297 for (i=0;i<MAX_OLD_SEARCHES;i++) {
298 struct pvfs_search_state *search = idr_find(pvfs->search.idtree, i);
299 if (search == NULL) return;
300 if (pvfs_list_eos(search->dir, search->current_index) &&
301 search->last_used != 0 &&
302 t > search->last_used + 30) {
303 /* its almost certainly been forgotten
312 list files in a directory matching a wildcard pattern - old SMBsearch interface
314 static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
315 struct ntvfs_request *req, union smb_search_first *io,
316 void *search_private,
317 BOOL (*callback)(void *, union smb_search_data *))
319 struct pvfs_dir *dir;
320 struct pvfs_state *pvfs = ntvfs->private_data;
321 struct pvfs_search_state *search;
323 uint16_t search_attrib;
326 struct pvfs_filename *name;
329 search_attrib = io->search_first.in.search_attrib;
330 pattern = io->search_first.in.pattern;
332 /* resolve the cifs name to a posix name */
333 status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
334 if (!NT_STATUS_IS_OK(status)) {
338 if (!name->has_wildcard && !name->exists) {
339 return STATUS_NO_MORE_FILES;
342 status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
343 if (!NT_STATUS_IS_OK(status)) {
347 /* we initially make search a child of the request, then if we
348 need to keep it long term we steal it for the private
350 search = talloc(req, struct pvfs_search_state);
352 return NT_STATUS_NO_MEMORY;
355 /* do the actual directory listing */
356 status = pvfs_list_start(pvfs, name, search, &dir);
357 if (!NT_STATUS_IS_OK(status)) {
361 /* we need to give a handle back to the client so it
362 can continue a search */
363 id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
365 pvfs_search_cleanup(pvfs);
366 id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
369 return NT_STATUS_INSUFFICIENT_RESOURCES;
375 search->current_index = 0;
376 search->search_attrib = search_attrib & 0xFF;
377 search->must_attrib = (search_attrib>>8) & 0xFF;
378 search->last_used = time(NULL);
381 DLIST_ADD(pvfs->search.list, search);
383 talloc_set_destructor(search, pvfs_search_destructor);
385 status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
386 &reply_count, search_private, callback);
387 if (!NT_STATUS_IS_OK(status)) {
391 io->search_first.out.count = reply_count;
393 /* not matching any entries is an error */
394 if (reply_count == 0) {
395 return STATUS_NO_MORE_FILES;
398 talloc_steal(pvfs, search);
403 /* continue a old style search */
404 static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
405 struct ntvfs_request *req, union smb_search_next *io,
406 void *search_private,
407 BOOL (*callback)(void *, union smb_search_data *))
409 struct pvfs_state *pvfs = ntvfs->private_data;
410 struct pvfs_search_state *search;
411 struct pvfs_dir *dir;
412 uint_t reply_count, max_count;
416 handle = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
417 max_count = io->search_next.in.max_count;
419 search = idr_find(pvfs->search.idtree, handle);
420 if (search == NULL) {
421 /* we didn't find the search handle */
422 return NT_STATUS_INVALID_HANDLE;
427 status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie,
428 &search->current_index);
429 if (!NT_STATUS_IS_OK(status)) {
432 search->last_used = time(NULL);
434 status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
435 &reply_count, search_private, callback);
436 if (!NT_STATUS_IS_OK(status)) {
440 io->search_next.out.count = reply_count;
442 /* not matching any entries means end of search */
443 if (reply_count == 0) {
451 list files in a directory matching a wildcard pattern
453 static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
454 struct ntvfs_request *req, union smb_search_first *io,
455 void *search_private,
456 BOOL (*callback)(void *, union smb_search_data *))
458 struct pvfs_dir *dir;
459 struct pvfs_state *pvfs = ntvfs->private_data;
460 struct pvfs_search_state *search;
462 uint16_t search_attrib, max_count;
465 struct pvfs_filename *name;
468 search_attrib = io->t2ffirst.in.search_attrib;
469 pattern = io->t2ffirst.in.pattern;
470 max_count = io->t2ffirst.in.max_count;
472 /* resolve the cifs name to a posix name */
473 status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
474 if (!NT_STATUS_IS_OK(status)) {
478 if (!name->has_wildcard && !name->exists) {
479 return NT_STATUS_NO_SUCH_FILE;
482 status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
483 if (!NT_STATUS_IS_OK(status)) {
487 /* we initially make search a child of the request, then if we
488 need to keep it long term we steal it for the private
490 search = talloc(req, struct pvfs_search_state);
492 return NT_STATUS_NO_MEMORY;
495 /* do the actual directory listing */
496 status = pvfs_list_start(pvfs, name, search, &dir);
497 if (!NT_STATUS_IS_OK(status)) {
501 id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
503 return NT_STATUS_INSUFFICIENT_RESOURCES;
509 search->current_index = 0;
510 search->search_attrib = search_attrib;
511 search->must_attrib = 0;
512 search->last_used = 0;
513 search->num_ea_names = io->t2ffirst.in.num_names;
514 search->ea_names = io->t2ffirst.in.ea_names;
517 DLIST_ADD(pvfs->search.list, search);
518 talloc_set_destructor(search, pvfs_search_destructor);
520 status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
521 &reply_count, search_private, callback);
522 if (!NT_STATUS_IS_OK(status)) {
526 /* not matching any entries is an error */
527 if (reply_count == 0) {
528 return NT_STATUS_NO_SUCH_FILE;
531 io->t2ffirst.out.count = reply_count;
532 io->t2ffirst.out.handle = search->handle;
533 io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
535 /* work out if we are going to keep the search state
536 and allow for a search continue */
537 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
538 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
539 io->t2ffirst.out.end_of_search)) {
542 talloc_steal(pvfs, search);
548 /* continue a search */
549 static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
550 struct ntvfs_request *req, union smb_search_next *io,
551 void *search_private,
552 BOOL (*callback)(void *, union smb_search_data *))
554 struct pvfs_state *pvfs = ntvfs->private_data;
555 struct pvfs_search_state *search;
556 struct pvfs_dir *dir;
561 handle = io->t2fnext.in.handle;
563 search = idr_find(pvfs->search.idtree, handle);
564 if (search == NULL) {
565 /* we didn't find the search handle */
566 return NT_STATUS_INVALID_HANDLE;
571 status = NT_STATUS_OK;
573 /* work out what type of continuation is being used */
574 if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
575 status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
576 if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
577 status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
578 &search->current_index);
580 } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
581 status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
582 &search->current_index);
584 if (!NT_STATUS_IS_OK(status)) {
588 search->num_ea_names = io->t2fnext.in.num_names;
589 search->ea_names = io->t2fnext.in.ea_names;
591 status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
592 &reply_count, search_private, callback);
593 if (!NT_STATUS_IS_OK(status)) {
597 io->t2fnext.out.count = reply_count;
598 io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
600 /* work out if we are going to keep the search state */
601 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
602 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
603 io->t2fnext.out.end_of_search)) {
610 static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
611 struct ntvfs_request *req, const struct smb2_find *io,
612 void *search_private,
613 BOOL (*callback)(void *, union smb_search_data *))
615 struct pvfs_dir *dir;
616 struct pvfs_state *pvfs = ntvfs->private_data;
617 struct pvfs_search_state *search;
622 struct pvfs_filename *name;
625 f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
627 return NT_STATUS_FILE_CLOSED;
630 /* its only valid for directories */
631 if (f->handle->fd != -1) {
632 return NT_STATUS_INVALID_PARAMETER;
635 if (!(f->access_mask & SEC_DIR_LIST)) {
636 return NT_STATUS_ACCESS_DENIED;
640 talloc_free(f->search);
644 if (strequal(io->in.pattern, "")) {
645 return NT_STATUS_OBJECT_NAME_INVALID;
647 if (strchr_m(io->in.pattern, '\\')) {
648 return NT_STATUS_OBJECT_NAME_INVALID;
650 if (strchr_m(io->in.pattern, '/')) {
651 return NT_STATUS_OBJECT_NAME_INVALID;
654 if (strequal("", f->handle->name->original_name)) {
655 pattern = talloc_asprintf(req, "\\%s", io->in.pattern);
656 NT_STATUS_HAVE_NO_MEMORY(pattern);
658 pattern = talloc_asprintf(req, "\\%s\\%s",
659 f->handle->name->original_name,
661 NT_STATUS_HAVE_NO_MEMORY(pattern);
664 /* resolve the cifs name to a posix name */
665 status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
666 NT_STATUS_NOT_OK_RETURN(status);
668 if (!name->has_wildcard && !name->exists) {
669 return NT_STATUS_NO_SUCH_FILE;
672 /* we initially make search a child of the request, then if we
673 need to keep it long term we steal it for the private
675 search = talloc(req, struct pvfs_search_state);
676 NT_STATUS_HAVE_NO_MEMORY(search);
678 /* do the actual directory listing */
679 status = pvfs_list_start(pvfs, name, search, &dir);
680 NT_STATUS_NOT_OK_RETURN(status);
683 search->handle = INVALID_SEARCH_HANDLE;
685 search->current_index = 0;
686 search->search_attrib = 0;
687 search->must_attrib = 0;
688 search->last_used = 0;
689 search->num_ea_names = 0;
690 search->ea_names = NULL;
693 if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
696 max_count = UINT16_MAX;
699 status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
700 &reply_count, search_private, callback);
701 NT_STATUS_NOT_OK_RETURN(status);
703 /* not matching any entries is an error */
704 if (reply_count == 0) {
705 return NT_STATUS_NO_SUCH_FILE;
708 f->search = talloc_steal(f, search);
713 static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
714 struct ntvfs_request *req, const struct smb2_find *io,
715 void *search_private,
716 BOOL (*callback)(void *, union smb_search_data *))
718 struct pvfs_state *pvfs = ntvfs->private_data;
719 struct pvfs_search_state *search;
725 f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
727 return NT_STATUS_FILE_CLOSED;
730 /* its only valid for directories */
731 if (f->handle->fd != -1) {
732 return NT_STATUS_INVALID_PARAMETER;
735 /* if there's no search started on the dir handle, it's like a search_first */
738 return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
741 if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
742 search->current_index = 0;
745 if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
748 max_count = UINT16_MAX;
751 status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
752 &reply_count, search_private, callback);
753 NT_STATUS_NOT_OK_RETURN(status);
755 /* not matching any entries is an error */
756 if (reply_count == 0) {
757 return STATUS_NO_MORE_FILES;
764 list files in a directory matching a wildcard pattern
766 NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
767 struct ntvfs_request *req, union smb_search_first *io,
768 void *search_private,
769 BOOL (*callback)(void *, union smb_search_data *))
771 switch (io->generic.level) {
772 case RAW_SEARCH_SEARCH:
773 case RAW_SEARCH_FFIRST:
774 case RAW_SEARCH_FUNIQUE:
775 return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
777 case RAW_SEARCH_TRANS2:
778 return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
780 case RAW_SEARCH_SMB2:
781 return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
784 return NT_STATUS_INVALID_LEVEL;
787 /* continue a search */
788 NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
789 struct ntvfs_request *req, union smb_search_next *io,
790 void *search_private,
791 BOOL (*callback)(void *, union smb_search_data *))
793 switch (io->generic.level) {
794 case RAW_SEARCH_SEARCH:
795 case RAW_SEARCH_FFIRST:
796 return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
798 case RAW_SEARCH_FUNIQUE:
799 return NT_STATUS_INVALID_LEVEL;
801 case RAW_SEARCH_TRANS2:
802 return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
804 case RAW_SEARCH_SMB2:
805 return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
808 return NT_STATUS_INVALID_LEVEL;
813 NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
814 struct ntvfs_request *req, union smb_search_close *io)
816 struct pvfs_state *pvfs = ntvfs->private_data;
817 struct pvfs_search_state *search;
818 uint16_t handle = INVALID_SEARCH_HANDLE;
820 switch (io->generic.level) {
821 case RAW_FINDCLOSE_GENERIC:
822 return NT_STATUS_INVALID_LEVEL;
824 case RAW_FINDCLOSE_FCLOSE:
825 handle = io->fclose.in.id.handle;
828 case RAW_FINDCLOSE_FINDCLOSE:
829 handle = io->findclose.in.handle;
833 search = idr_find(pvfs->search.idtree, handle);
834 if (search == NULL) {
835 /* we didn't find the search handle */
836 return NT_STATUS_INVALID_HANDLE;