vfs_shadow_copy2: Use VFS interface to derive mount point
[samba4-gss.git] / source4 / ntvfs / posix / pvfs_search.c
blobf352a6a474aebc0246c4144296a8557f819bd106
1 /*
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 3 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, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "vfs_posix.h"
24 #include "system/time.h"
25 #include "librpc/gen_ndr/security.h"
26 #include "samba/service_stream.h"
27 #include "lib/events/events.h"
28 #include "../lib/util/dlinklist.h"
29 #include "lib/util/idtree.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);
44 return 0;
48 called when a search timer goes off
50 static void pvfs_search_timer(struct tevent_context *ev, struct tevent_timer *te,
51 struct timeval t, void *ptr)
53 struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
54 talloc_free(search);
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 tevent_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
63 if (search->handle == INVALID_SEARCH_HANDLE) return;
64 talloc_free(search->te);
65 search->te = tevent_add_timer(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,
76 const char *fname,
77 struct pvfs_search_state *search,
78 off_t dir_offset,
79 union smb_search_data *file)
81 struct pvfs_filename *name;
82 NTSTATUS status;
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, 0, &name);
89 if (!NT_STATUS_IS_OK(status)) {
90 return status;
93 status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
94 if (!NT_STATUS_IS_OK(status)) {
95 return status;
98 switch (level) {
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;
112 return NT_STATUS_OK;
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;
123 return NT_STATUS_OK;
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;
135 return NT_STATUS_OK;
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,
148 search->ea_names,
149 &file->ea_list.eas);
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;
161 return NT_STATUS_OK;
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;
174 return NT_STATUS_OK;
176 case RAW_SEARCH_DATA_NAME_INFO:
177 file->name_info.file_index = dir_index;
178 file->name_info.name.s = fname;
179 return NT_STATUS_OK;
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;
193 return NT_STATUS_OK;
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;
207 return NT_STATUS_OK;
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;
222 return NT_STATUS_OK;
224 case RAW_SEARCH_DATA_GENERIC:
225 case RAW_SEARCH_DATA_UNIX_INFO:
226 case RAW_SEARCH_DATA_UNIX_INFO2:
227 return NT_STATUS_INVALID_LEVEL;
230 return NT_STATUS_INVALID_LEVEL;
235 the search fill loop
237 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
238 unsigned int max_count,
239 struct pvfs_search_state *search,
240 enum smb_search_data_level level,
241 unsigned int *reply_count,
242 void *search_private,
243 bool (*callback)(void *, const union smb_search_data *))
245 struct pvfs_dir *dir = search->dir;
246 NTSTATUS status;
248 *reply_count = 0;
250 if (max_count == 0) {
251 max_count = 1;
254 while ((*reply_count) < max_count) {
255 union smb_search_data *file;
256 const char *name;
257 off_t ofs = search->current_index;
259 name = pvfs_list_next(dir, &search->current_index);
260 if (name == NULL) break;
262 file = talloc(mem_ctx, union smb_search_data);
263 if (!file) {
264 return NT_STATUS_NO_MEMORY;
267 status = fill_search_info(pvfs, level,
268 pvfs_list_unix_path(dir), name,
269 search, search->current_index, file);
270 if (!NT_STATUS_IS_OK(status)) {
271 talloc_free(file);
272 continue;
275 if (!callback(search_private, file)) {
276 talloc_free(file);
277 search->current_index = ofs;
278 break;
281 (*reply_count)++;
282 talloc_free(file);
285 pvfs_search_setup_timer(search);
287 return NT_STATUS_OK;
291 we've run out of search handles - cleanup those that the client forgot
292 to close
294 static void pvfs_search_cleanup(struct pvfs_state *pvfs)
296 int i;
297 time_t t = time_mono(NULL);
299 for (i=0;i<MAX_OLD_SEARCHES;i++) {
300 struct pvfs_search_state *search;
301 void *p = idr_find(pvfs->search.idtree, i);
303 if (p == NULL) return;
305 search = talloc_get_type(p, struct pvfs_search_state);
306 if (pvfs_list_eos(search->dir, search->current_index) &&
307 search->last_used != 0 &&
308 t > search->last_used + 30) {
309 /* its almost certainly been forgotten
310 about */
311 talloc_free(search);
318 list files in a directory matching a wildcard pattern - old SMBsearch interface
320 static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
321 struct ntvfs_request *req, union smb_search_first *io,
322 void *search_private,
323 bool (*callback)(void *, const union smb_search_data *))
325 struct pvfs_dir *dir;
326 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
327 struct pvfs_state);
328 struct pvfs_search_state *search;
329 unsigned int reply_count;
330 uint16_t search_attrib;
331 const char *pattern;
332 NTSTATUS status;
333 struct pvfs_filename *name;
334 int id;
336 search_attrib = io->search_first.in.search_attrib;
337 pattern = io->search_first.in.pattern;
339 /* resolve the cifs name to a posix name */
340 status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
341 if (!NT_STATUS_IS_OK(status)) {
342 return status;
345 if (!name->has_wildcard && !name->exists) {
346 return STATUS_NO_MORE_FILES;
349 status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
350 if (!NT_STATUS_IS_OK(status)) {
351 return status;
354 /* we initially make search a child of the request, then if we
355 need to keep it long term we steal it for the private
356 structure */
357 search = talloc(req, struct pvfs_search_state);
358 if (!search) {
359 return NT_STATUS_NO_MEMORY;
362 /* do the actual directory listing */
363 status = pvfs_list_start(pvfs, name, search, &dir);
364 if (!NT_STATUS_IS_OK(status)) {
365 return status;
368 /* we need to give a handle back to the client so it
369 can continue a search */
370 id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
371 if (id == -1) {
372 pvfs_search_cleanup(pvfs);
373 id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
375 if (id == -1) {
376 return NT_STATUS_INSUFFICIENT_RESOURCES;
379 search->pvfs = pvfs;
380 search->handle = id;
381 search->dir = dir;
382 search->current_index = 0;
383 search->search_attrib = search_attrib & 0xFF;
384 search->must_attrib = (search_attrib>>8) & 0xFF;
385 search->last_used = time_mono(NULL);
386 search->te = NULL;
388 DLIST_ADD(pvfs->search.list, search);
390 talloc_set_destructor(search, pvfs_search_destructor);
392 status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
393 &reply_count, search_private, callback);
394 if (!NT_STATUS_IS_OK(status)) {
395 return status;
398 io->search_first.out.count = reply_count;
400 /* not matching any entries is an error */
401 if (reply_count == 0) {
402 return STATUS_NO_MORE_FILES;
405 talloc_steal(pvfs, search);
407 return NT_STATUS_OK;
410 /* continue a old style search */
411 static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
412 struct ntvfs_request *req, union smb_search_next *io,
413 void *search_private,
414 bool (*callback)(void *, const union smb_search_data *))
416 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
417 struct pvfs_state);
418 void *p;
419 struct pvfs_search_state *search;
420 struct pvfs_dir *dir;
421 unsigned int reply_count, max_count;
422 uint16_t handle;
423 NTSTATUS status;
425 handle = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
426 max_count = io->search_next.in.max_count;
428 p = idr_find(pvfs->search.idtree, handle);
429 if (p == NULL) {
430 /* we didn't find the search handle */
431 return NT_STATUS_INVALID_HANDLE;
434 search = talloc_get_type(p, struct pvfs_search_state);
436 dir = search->dir;
438 status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie,
439 &search->current_index);
440 if (!NT_STATUS_IS_OK(status)) {
441 return status;
443 search->last_used = time_mono(NULL);
445 status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
446 &reply_count, search_private, callback);
447 if (!NT_STATUS_IS_OK(status)) {
448 return status;
451 io->search_next.out.count = reply_count;
453 /* not matching any entries means end of search */
454 if (reply_count == 0) {
455 talloc_free(search);
458 return NT_STATUS_OK;
462 list files in a directory matching a wildcard pattern
464 static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
465 struct ntvfs_request *req, union smb_search_first *io,
466 void *search_private,
467 bool (*callback)(void *, const union smb_search_data *))
469 struct pvfs_dir *dir;
470 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
471 struct pvfs_state);
472 struct pvfs_search_state *search;
473 unsigned int reply_count;
474 uint16_t search_attrib, max_count;
475 const char *pattern;
476 NTSTATUS status;
477 struct pvfs_filename *name;
478 int id;
480 search_attrib = io->t2ffirst.in.search_attrib;
481 pattern = io->t2ffirst.in.pattern;
482 max_count = io->t2ffirst.in.max_count;
484 /* resolve the cifs name to a posix name */
485 status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
486 if (!NT_STATUS_IS_OK(status)) {
487 return status;
490 if (!name->has_wildcard && !name->exists) {
491 return NT_STATUS_NO_SUCH_FILE;
494 status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
495 if (!NT_STATUS_IS_OK(status)) {
496 return status;
499 /* we initially make search a child of the request, then if we
500 need to keep it long term we steal it for the private
501 structure */
502 search = talloc(req, struct pvfs_search_state);
503 if (!search) {
504 return NT_STATUS_NO_MEMORY;
507 /* do the actual directory listing */
508 status = pvfs_list_start(pvfs, name, search, &dir);
509 if (!NT_STATUS_IS_OK(status)) {
510 return status;
513 id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
514 if (id == -1) {
515 return NT_STATUS_INSUFFICIENT_RESOURCES;
518 search->pvfs = pvfs;
519 search->handle = id;
520 search->dir = dir;
521 search->current_index = 0;
522 search->search_attrib = search_attrib;
523 search->must_attrib = 0;
524 search->last_used = 0;
525 search->num_ea_names = io->t2ffirst.in.num_names;
526 search->ea_names = io->t2ffirst.in.ea_names;
527 search->te = NULL;
529 DLIST_ADD(pvfs->search.list, search);
530 talloc_set_destructor(search, pvfs_search_destructor);
532 status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
533 &reply_count, search_private, callback);
534 if (!NT_STATUS_IS_OK(status)) {
535 return status;
538 /* not matching any entries is an error */
539 if (reply_count == 0) {
540 return NT_STATUS_NO_SUCH_FILE;
543 io->t2ffirst.out.count = reply_count;
544 io->t2ffirst.out.handle = search->handle;
545 io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
547 /* work out if we are going to keep the search state
548 and allow for a search continue */
549 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
550 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
551 io->t2ffirst.out.end_of_search)) {
552 talloc_free(search);
553 } else {
554 talloc_steal(pvfs, search);
557 return NT_STATUS_OK;
560 /* continue a search */
561 static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
562 struct ntvfs_request *req, union smb_search_next *io,
563 void *search_private,
564 bool (*callback)(void *, const union smb_search_data *))
566 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
567 struct pvfs_state);
568 void *p;
569 struct pvfs_search_state *search;
570 struct pvfs_dir *dir;
571 unsigned int reply_count;
572 uint16_t handle;
573 NTSTATUS status;
575 handle = io->t2fnext.in.handle;
577 p = idr_find(pvfs->search.idtree, handle);
578 if (p == NULL) {
579 /* we didn't find the search handle */
580 return NT_STATUS_INVALID_HANDLE;
583 search = talloc_get_type(p, struct pvfs_search_state);
585 dir = search->dir;
587 status = NT_STATUS_OK;
589 /* work out what type of continuation is being used */
590 if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
591 status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
592 if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
593 status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
594 &search->current_index);
596 } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
597 status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
598 &search->current_index);
600 if (!NT_STATUS_IS_OK(status)) {
601 return status;
604 search->num_ea_names = io->t2fnext.in.num_names;
605 search->ea_names = io->t2fnext.in.ea_names;
607 status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
608 &reply_count, search_private, callback);
609 if (!NT_STATUS_IS_OK(status)) {
610 return status;
613 io->t2fnext.out.count = reply_count;
614 io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
616 /* work out if we are going to keep the search state */
617 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
618 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
619 io->t2fnext.out.end_of_search)) {
620 talloc_free(search);
623 return NT_STATUS_OK;
626 static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
627 struct ntvfs_request *req, const struct smb2_find *io,
628 void *search_private,
629 bool (*callback)(void *, const union smb_search_data *))
631 struct pvfs_dir *dir;
632 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
633 struct pvfs_state);
634 struct pvfs_search_state *search;
635 unsigned int reply_count;
636 uint16_t max_count;
637 const char *pattern;
638 NTSTATUS status;
639 struct pvfs_filename *name;
640 struct pvfs_file *f;
642 f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
643 if (!f) {
644 return NT_STATUS_FILE_CLOSED;
647 /* its only valid for directories */
648 if (f->handle->fd != -1) {
649 return NT_STATUS_INVALID_PARAMETER;
652 if (!(f->access_mask & SEC_DIR_LIST)) {
653 return NT_STATUS_ACCESS_DENIED;
656 if (f->search) {
657 talloc_free(f->search);
658 f->search = NULL;
661 if (strequal(io->in.pattern, "")) {
662 return NT_STATUS_OBJECT_NAME_INVALID;
664 if (strchr_m(io->in.pattern, '\\')) {
665 return NT_STATUS_OBJECT_NAME_INVALID;
667 if (strchr_m(io->in.pattern, '/')) {
668 return NT_STATUS_OBJECT_NAME_INVALID;
671 if (strequal("", f->handle->name->original_name)) {
672 pattern = talloc_asprintf(req, "%s", io->in.pattern);
673 NT_STATUS_HAVE_NO_MEMORY(pattern);
674 } else {
675 pattern = talloc_asprintf(req, "%s\\%s",
676 f->handle->name->original_name,
677 io->in.pattern);
678 NT_STATUS_HAVE_NO_MEMORY(pattern);
681 /* resolve the cifs name to a posix name */
682 status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
683 NT_STATUS_NOT_OK_RETURN(status);
685 if (!name->has_wildcard && !name->exists) {
686 return NT_STATUS_NO_SUCH_FILE;
689 /* we initially make search a child of the request, then if we
690 need to keep it long term we steal it for the private
691 structure */
692 search = talloc(req, struct pvfs_search_state);
693 NT_STATUS_HAVE_NO_MEMORY(search);
695 /* do the actual directory listing */
696 status = pvfs_list_start(pvfs, name, search, &dir);
697 NT_STATUS_NOT_OK_RETURN(status);
699 search->pvfs = pvfs;
700 search->handle = INVALID_SEARCH_HANDLE;
701 search->dir = dir;
702 search->current_index = 0;
703 search->search_attrib = 0x0000FFFF;
704 search->must_attrib = 0;
705 search->last_used = 0;
706 search->num_ea_names = 0;
707 search->ea_names = NULL;
708 search->te = NULL;
710 if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
711 max_count = 1;
712 } else {
713 max_count = UINT16_MAX;
716 status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
717 &reply_count, search_private, callback);
718 NT_STATUS_NOT_OK_RETURN(status);
720 /* not matching any entries is an error */
721 if (reply_count == 0) {
722 return NT_STATUS_NO_SUCH_FILE;
725 f->search = talloc_steal(f, search);
727 return NT_STATUS_OK;
730 static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
731 struct ntvfs_request *req, const struct smb2_find *io,
732 void *search_private,
733 bool (*callback)(void *, const union smb_search_data *))
735 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
736 struct pvfs_state);
737 struct pvfs_search_state *search;
738 unsigned int reply_count;
739 uint16_t max_count;
740 NTSTATUS status;
741 struct pvfs_file *f;
743 f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
744 if (!f) {
745 return NT_STATUS_FILE_CLOSED;
748 /* its only valid for directories */
749 if (f->handle->fd != -1) {
750 return NT_STATUS_INVALID_PARAMETER;
753 /* if there's no search started on the dir handle, it's like a search_first */
754 search = f->search;
755 if (!search) {
756 return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
759 if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
760 search->current_index = 0;
763 if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
764 max_count = 1;
765 } else {
766 max_count = UINT16_MAX;
769 status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
770 &reply_count, search_private, callback);
771 NT_STATUS_NOT_OK_RETURN(status);
773 /* not matching any entries is an error */
774 if (reply_count == 0) {
775 return STATUS_NO_MORE_FILES;
778 return NT_STATUS_OK;
782 list files in a directory matching a wildcard pattern
784 NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
785 struct ntvfs_request *req, union smb_search_first *io,
786 void *search_private,
787 bool (*callback)(void *, const union smb_search_data *))
789 switch (io->generic.level) {
790 case RAW_SEARCH_SEARCH:
791 case RAW_SEARCH_FFIRST:
792 case RAW_SEARCH_FUNIQUE:
793 return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
795 case RAW_SEARCH_TRANS2:
796 return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
798 case RAW_SEARCH_SMB2:
799 return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
802 return NT_STATUS_INVALID_LEVEL;
805 /* continue a search */
806 NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
807 struct ntvfs_request *req, union smb_search_next *io,
808 void *search_private,
809 bool (*callback)(void *, const union smb_search_data *))
811 switch (io->generic.level) {
812 case RAW_SEARCH_SEARCH:
813 case RAW_SEARCH_FFIRST:
814 return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
816 case RAW_SEARCH_FUNIQUE:
817 return NT_STATUS_INVALID_LEVEL;
819 case RAW_SEARCH_TRANS2:
820 return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
822 case RAW_SEARCH_SMB2:
823 return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
826 return NT_STATUS_INVALID_LEVEL;
830 /* close a search */
831 NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
832 struct ntvfs_request *req, union smb_search_close *io)
834 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
835 struct pvfs_state);
836 void *p;
837 struct pvfs_search_state *search;
838 uint16_t handle = INVALID_SEARCH_HANDLE;
840 switch (io->generic.level) {
841 case RAW_FINDCLOSE_GENERIC:
842 return NT_STATUS_INVALID_LEVEL;
844 case RAW_FINDCLOSE_FCLOSE:
845 handle = io->fclose.in.id.handle;
846 break;
848 case RAW_FINDCLOSE_FINDCLOSE:
849 handle = io->findclose.in.handle;
850 break;
853 p = idr_find(pvfs->search.idtree, handle);
854 if (p == NULL) {
855 /* we didn't find the search handle */
856 return NT_STATUS_INVALID_HANDLE;
859 search = talloc_get_type(p, struct pvfs_search_state);
861 talloc_free(search);
863 return NT_STATUS_OK;