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/>.
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"
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
37 destroy an open search
39 static int pvfs_search_destructor(struct pvfs_search_state
*search
)
41 DLIST_REMOVE(search
->pvfs
->search
.list
, search
);
42 idr_remove(search
->pvfs
->search
.idtree
, search
->handle
);
47 called when a search timer goes off
49 static void pvfs_search_timer(struct tevent_context
*ev
, struct tevent_timer
*te
,
50 struct timeval t
, void *ptr
)
52 struct pvfs_search_state
*search
= talloc_get_type(ptr
, struct pvfs_search_state
);
57 setup a timer to destroy a open search after a inactivity period
59 static void pvfs_search_setup_timer(struct pvfs_search_state
*search
)
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
= tevent_add_timer(ev
, search
,
65 timeval_current_ofs(search
->pvfs
->search
.inactivity_time
, 0),
66 pvfs_search_timer
, search
);
70 fill in a single search result for a given info level
72 static NTSTATUS
fill_search_info(struct pvfs_state
*pvfs
,
73 enum smb_search_data_level level
,
74 const char *unix_path
,
76 struct pvfs_search_state
*search
,
78 union smb_search_data
*file
)
80 struct pvfs_filename
*name
;
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 */
87 status
= pvfs_resolve_partial(pvfs
, file
, unix_path
, fname
, 0, &name
);
88 if (!NT_STATUS_IS_OK(status
)) {
92 status
= pvfs_match_attrib(pvfs
, name
, search
->search_attrib
, search
->must_attrib
);
93 if (!NT_STATUS_IS_OK(status
)) {
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;
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
;
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
;
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
,
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
;
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
;
175 case RAW_SEARCH_DATA_NAME_INFO
:
176 file
->name_info
.file_index
= dir_index
;
177 file
->name_info
.name
.s
= fname
;
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
;
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
;
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
;
223 case RAW_SEARCH_DATA_GENERIC
:
224 case RAW_SEARCH_DATA_UNIX_INFO
:
225 case RAW_SEARCH_DATA_UNIX_INFO2
:
226 return NT_STATUS_INVALID_LEVEL
;
229 return NT_STATUS_INVALID_LEVEL
;
236 static NTSTATUS
pvfs_search_fill(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
237 unsigned int max_count
,
238 struct pvfs_search_state
*search
,
239 enum smb_search_data_level level
,
240 unsigned int *reply_count
,
241 void *search_private
,
242 bool (*callback
)(void *, const union smb_search_data
*))
244 struct pvfs_dir
*dir
= search
->dir
;
249 if (max_count
== 0) {
253 while ((*reply_count
) < max_count
) {
254 union smb_search_data
*file
;
256 off_t ofs
= search
->current_index
;
258 name
= pvfs_list_next(dir
, &search
->current_index
);
259 if (name
== NULL
) break;
261 file
= talloc(mem_ctx
, union smb_search_data
);
263 return NT_STATUS_NO_MEMORY
;
266 status
= fill_search_info(pvfs
, level
,
267 pvfs_list_unix_path(dir
), name
,
268 search
, search
->current_index
, file
);
269 if (!NT_STATUS_IS_OK(status
)) {
274 if (!callback(search_private
, file
)) {
276 search
->current_index
= ofs
;
284 pvfs_search_setup_timer(search
);
290 we've run out of search handles - cleanup those that the client forgot
293 static void pvfs_search_cleanup(struct pvfs_state
*pvfs
)
296 time_t t
= time_mono(NULL
);
298 for (i
=0;i
<MAX_OLD_SEARCHES
;i
++) {
299 struct pvfs_search_state
*search
;
300 void *p
= idr_find(pvfs
->search
.idtree
, i
);
302 if (p
== NULL
) return;
304 search
= talloc_get_type(p
, struct pvfs_search_state
);
305 if (pvfs_list_eos(search
->dir
, search
->current_index
) &&
306 search
->last_used
!= 0 &&
307 t
> search
->last_used
+ 30) {
308 /* its almost certainly been forgotten
317 list files in a directory matching a wildcard pattern - old SMBsearch interface
319 static NTSTATUS
pvfs_search_first_old(struct ntvfs_module_context
*ntvfs
,
320 struct ntvfs_request
*req
, union smb_search_first
*io
,
321 void *search_private
,
322 bool (*callback
)(void *, const union smb_search_data
*))
324 struct pvfs_dir
*dir
;
325 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
327 struct pvfs_search_state
*search
;
328 unsigned int reply_count
;
329 uint16_t search_attrib
;
332 struct pvfs_filename
*name
;
335 search_attrib
= io
->search_first
.in
.search_attrib
;
336 pattern
= io
->search_first
.in
.pattern
;
338 /* resolve the cifs name to a posix name */
339 status
= pvfs_resolve_name(pvfs
, req
, pattern
, PVFS_RESOLVE_WILDCARD
, &name
);
340 if (!NT_STATUS_IS_OK(status
)) {
344 if (!name
->has_wildcard
&& !name
->exists
) {
345 return STATUS_NO_MORE_FILES
;
348 status
= pvfs_access_check_parent(pvfs
, req
, name
, SEC_DIR_TRAVERSE
| SEC_DIR_LIST
);
349 if (!NT_STATUS_IS_OK(status
)) {
353 /* we initially make search a child of the request, then if we
354 need to keep it long term we steal it for the private
356 search
= talloc(req
, struct pvfs_search_state
);
358 return NT_STATUS_NO_MEMORY
;
361 /* do the actual directory listing */
362 status
= pvfs_list_start(pvfs
, name
, search
, &dir
);
363 if (!NT_STATUS_IS_OK(status
)) {
367 /* we need to give a handle back to the client so it
368 can continue a search */
369 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_OLD_SEARCHES
);
371 pvfs_search_cleanup(pvfs
);
372 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_OLD_SEARCHES
);
375 return NT_STATUS_INSUFFICIENT_RESOURCES
;
381 search
->current_index
= 0;
382 search
->search_attrib
= search_attrib
& 0xFF;
383 search
->must_attrib
= (search_attrib
>>8) & 0xFF;
384 search
->last_used
= time_mono(NULL
);
387 DLIST_ADD(pvfs
->search
.list
, search
);
389 talloc_set_destructor(search
, pvfs_search_destructor
);
391 status
= pvfs_search_fill(pvfs
, req
, io
->search_first
.in
.max_count
, search
, io
->generic
.data_level
,
392 &reply_count
, search_private
, callback
);
393 if (!NT_STATUS_IS_OK(status
)) {
397 io
->search_first
.out
.count
= reply_count
;
399 /* not matching any entries is an error */
400 if (reply_count
== 0) {
401 return STATUS_NO_MORE_FILES
;
404 talloc_steal(pvfs
, search
);
409 /* continue a old style search */
410 static NTSTATUS
pvfs_search_next_old(struct ntvfs_module_context
*ntvfs
,
411 struct ntvfs_request
*req
, union smb_search_next
*io
,
412 void *search_private
,
413 bool (*callback
)(void *, const union smb_search_data
*))
415 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
418 struct pvfs_search_state
*search
;
419 struct pvfs_dir
*dir
;
420 unsigned int reply_count
, max_count
;
424 handle
= io
->search_next
.in
.id
.handle
| (io
->search_next
.in
.id
.reserved
<<8);
425 max_count
= io
->search_next
.in
.max_count
;
427 p
= idr_find(pvfs
->search
.idtree
, handle
);
429 /* we didn't find the search handle */
430 return NT_STATUS_INVALID_HANDLE
;
433 search
= talloc_get_type(p
, struct pvfs_search_state
);
437 status
= pvfs_list_seek_ofs(dir
, io
->search_next
.in
.id
.server_cookie
,
438 &search
->current_index
);
439 if (!NT_STATUS_IS_OK(status
)) {
442 search
->last_used
= time_mono(NULL
);
444 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->generic
.data_level
,
445 &reply_count
, search_private
, callback
);
446 if (!NT_STATUS_IS_OK(status
)) {
450 io
->search_next
.out
.count
= reply_count
;
452 /* not matching any entries means end of search */
453 if (reply_count
== 0) {
461 list files in a directory matching a wildcard pattern
463 static NTSTATUS
pvfs_search_first_trans2(struct ntvfs_module_context
*ntvfs
,
464 struct ntvfs_request
*req
, union smb_search_first
*io
,
465 void *search_private
,
466 bool (*callback
)(void *, const union smb_search_data
*))
468 struct pvfs_dir
*dir
;
469 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
471 struct pvfs_search_state
*search
;
472 unsigned int reply_count
;
473 uint16_t search_attrib
, max_count
;
476 struct pvfs_filename
*name
;
479 search_attrib
= io
->t2ffirst
.in
.search_attrib
;
480 pattern
= io
->t2ffirst
.in
.pattern
;
481 max_count
= io
->t2ffirst
.in
.max_count
;
483 /* resolve the cifs name to a posix name */
484 status
= pvfs_resolve_name(pvfs
, req
, pattern
, PVFS_RESOLVE_WILDCARD
, &name
);
485 if (!NT_STATUS_IS_OK(status
)) {
489 if (!name
->has_wildcard
&& !name
->exists
) {
490 return NT_STATUS_NO_SUCH_FILE
;
493 status
= pvfs_access_check_parent(pvfs
, req
, name
, SEC_DIR_TRAVERSE
| SEC_DIR_LIST
);
494 if (!NT_STATUS_IS_OK(status
)) {
498 /* we initially make search a child of the request, then if we
499 need to keep it long term we steal it for the private
501 search
= talloc(req
, struct pvfs_search_state
);
503 return NT_STATUS_NO_MEMORY
;
506 /* do the actual directory listing */
507 status
= pvfs_list_start(pvfs
, name
, search
, &dir
);
508 if (!NT_STATUS_IS_OK(status
)) {
512 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_SEARCH_HANDLES
);
514 return NT_STATUS_INSUFFICIENT_RESOURCES
;
520 search
->current_index
= 0;
521 search
->search_attrib
= search_attrib
;
522 search
->must_attrib
= 0;
523 search
->last_used
= 0;
524 search
->num_ea_names
= io
->t2ffirst
.in
.num_names
;
525 search
->ea_names
= io
->t2ffirst
.in
.ea_names
;
528 DLIST_ADD(pvfs
->search
.list
, search
);
529 talloc_set_destructor(search
, pvfs_search_destructor
);
531 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->generic
.data_level
,
532 &reply_count
, search_private
, callback
);
533 if (!NT_STATUS_IS_OK(status
)) {
537 /* not matching any entries is an error */
538 if (reply_count
== 0) {
539 return NT_STATUS_NO_SUCH_FILE
;
542 io
->t2ffirst
.out
.count
= reply_count
;
543 io
->t2ffirst
.out
.handle
= search
->handle
;
544 io
->t2ffirst
.out
.end_of_search
= pvfs_list_eos(dir
, search
->current_index
) ? 1 : 0;
546 /* work out if we are going to keep the search state
547 and allow for a search continue */
548 if ((io
->t2ffirst
.in
.flags
& FLAG_TRANS2_FIND_CLOSE
) ||
549 ((io
->t2ffirst
.in
.flags
& FLAG_TRANS2_FIND_CLOSE_IF_END
) &&
550 io
->t2ffirst
.out
.end_of_search
)) {
553 talloc_steal(pvfs
, search
);
559 /* continue a search */
560 static NTSTATUS
pvfs_search_next_trans2(struct ntvfs_module_context
*ntvfs
,
561 struct ntvfs_request
*req
, union smb_search_next
*io
,
562 void *search_private
,
563 bool (*callback
)(void *, const union smb_search_data
*))
565 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
568 struct pvfs_search_state
*search
;
569 struct pvfs_dir
*dir
;
570 unsigned int reply_count
;
574 handle
= io
->t2fnext
.in
.handle
;
576 p
= idr_find(pvfs
->search
.idtree
, handle
);
578 /* we didn't find the search handle */
579 return NT_STATUS_INVALID_HANDLE
;
582 search
= talloc_get_type(p
, struct pvfs_search_state
);
586 status
= NT_STATUS_OK
;
588 /* work out what type of continuation is being used */
589 if (io
->t2fnext
.in
.last_name
&& *io
->t2fnext
.in
.last_name
) {
590 status
= pvfs_list_seek(dir
, io
->t2fnext
.in
.last_name
, &search
->current_index
);
591 if (!NT_STATUS_IS_OK(status
) && io
->t2fnext
.in
.resume_key
) {
592 status
= pvfs_list_seek_ofs(dir
, io
->t2fnext
.in
.resume_key
,
593 &search
->current_index
);
595 } else if (!(io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CONTINUE
)) {
596 status
= pvfs_list_seek_ofs(dir
, io
->t2fnext
.in
.resume_key
,
597 &search
->current_index
);
599 if (!NT_STATUS_IS_OK(status
)) {
603 search
->num_ea_names
= io
->t2fnext
.in
.num_names
;
604 search
->ea_names
= io
->t2fnext
.in
.ea_names
;
606 status
= pvfs_search_fill(pvfs
, req
, io
->t2fnext
.in
.max_count
, search
, io
->generic
.data_level
,
607 &reply_count
, search_private
, callback
);
608 if (!NT_STATUS_IS_OK(status
)) {
612 io
->t2fnext
.out
.count
= reply_count
;
613 io
->t2fnext
.out
.end_of_search
= pvfs_list_eos(dir
, search
->current_index
) ? 1 : 0;
615 /* work out if we are going to keep the search state */
616 if ((io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CLOSE
) ||
617 ((io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CLOSE_IF_END
) &&
618 io
->t2fnext
.out
.end_of_search
)) {
625 static NTSTATUS
pvfs_search_first_smb2(struct ntvfs_module_context
*ntvfs
,
626 struct ntvfs_request
*req
, const struct smb2_find
*io
,
627 void *search_private
,
628 bool (*callback
)(void *, const union smb_search_data
*))
630 struct pvfs_dir
*dir
;
631 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
633 struct pvfs_search_state
*search
;
634 unsigned int reply_count
;
638 struct pvfs_filename
*name
;
641 f
= pvfs_find_fd(pvfs
, req
, io
->in
.file
.ntvfs
);
643 return NT_STATUS_FILE_CLOSED
;
646 /* its only valid for directories */
647 if (f
->handle
->fd
!= -1) {
648 return NT_STATUS_INVALID_PARAMETER
;
651 if (!(f
->access_mask
& SEC_DIR_LIST
)) {
652 return NT_STATUS_ACCESS_DENIED
;
656 talloc_free(f
->search
);
660 if (strequal(io
->in
.pattern
, "")) {
661 return NT_STATUS_OBJECT_NAME_INVALID
;
663 if (strchr_m(io
->in
.pattern
, '\\')) {
664 return NT_STATUS_OBJECT_NAME_INVALID
;
666 if (strchr_m(io
->in
.pattern
, '/')) {
667 return NT_STATUS_OBJECT_NAME_INVALID
;
670 if (strequal("", f
->handle
->name
->original_name
)) {
671 pattern
= talloc_asprintf(req
, "%s", io
->in
.pattern
);
672 NT_STATUS_HAVE_NO_MEMORY(pattern
);
674 pattern
= talloc_asprintf(req
, "%s\\%s",
675 f
->handle
->name
->original_name
,
677 NT_STATUS_HAVE_NO_MEMORY(pattern
);
680 /* resolve the cifs name to a posix name */
681 status
= pvfs_resolve_name(pvfs
, req
, pattern
, PVFS_RESOLVE_WILDCARD
, &name
);
682 NT_STATUS_NOT_OK_RETURN(status
);
684 if (!name
->has_wildcard
&& !name
->exists
) {
685 return NT_STATUS_NO_SUCH_FILE
;
688 /* we initially make search a child of the request, then if we
689 need to keep it long term we steal it for the private
691 search
= talloc(req
, struct pvfs_search_state
);
692 NT_STATUS_HAVE_NO_MEMORY(search
);
694 /* do the actual directory listing */
695 status
= pvfs_list_start(pvfs
, name
, search
, &dir
);
696 NT_STATUS_NOT_OK_RETURN(status
);
699 search
->handle
= INVALID_SEARCH_HANDLE
;
701 search
->current_index
= 0;
702 search
->search_attrib
= 0x0000FFFF;
703 search
->must_attrib
= 0;
704 search
->last_used
= 0;
705 search
->num_ea_names
= 0;
706 search
->ea_names
= NULL
;
709 if (io
->in
.continue_flags
& SMB2_CONTINUE_FLAG_SINGLE
) {
712 max_count
= UINT16_MAX
;
715 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->data_level
,
716 &reply_count
, search_private
, callback
);
717 NT_STATUS_NOT_OK_RETURN(status
);
719 /* not matching any entries is an error */
720 if (reply_count
== 0) {
721 return NT_STATUS_NO_SUCH_FILE
;
724 f
->search
= talloc_steal(f
, search
);
729 static NTSTATUS
pvfs_search_next_smb2(struct ntvfs_module_context
*ntvfs
,
730 struct ntvfs_request
*req
, const struct smb2_find
*io
,
731 void *search_private
,
732 bool (*callback
)(void *, const union smb_search_data
*))
734 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
736 struct pvfs_search_state
*search
;
737 unsigned int reply_count
;
742 f
= pvfs_find_fd(pvfs
, req
, io
->in
.file
.ntvfs
);
744 return NT_STATUS_FILE_CLOSED
;
747 /* its only valid for directories */
748 if (f
->handle
->fd
!= -1) {
749 return NT_STATUS_INVALID_PARAMETER
;
752 /* if there's no search started on the dir handle, it's like a search_first */
755 return pvfs_search_first_smb2(ntvfs
, req
, io
, search_private
, callback
);
758 if (io
->in
.continue_flags
& SMB2_CONTINUE_FLAG_RESTART
) {
759 search
->current_index
= 0;
762 if (io
->in
.continue_flags
& SMB2_CONTINUE_FLAG_SINGLE
) {
765 max_count
= UINT16_MAX
;
768 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->data_level
,
769 &reply_count
, search_private
, callback
);
770 NT_STATUS_NOT_OK_RETURN(status
);
772 /* not matching any entries is an error */
773 if (reply_count
== 0) {
774 return STATUS_NO_MORE_FILES
;
781 list files in a directory matching a wildcard pattern
783 NTSTATUS
pvfs_search_first(struct ntvfs_module_context
*ntvfs
,
784 struct ntvfs_request
*req
, union smb_search_first
*io
,
785 void *search_private
,
786 bool (*callback
)(void *, const union smb_search_data
*))
788 switch (io
->generic
.level
) {
789 case RAW_SEARCH_SEARCH
:
790 case RAW_SEARCH_FFIRST
:
791 case RAW_SEARCH_FUNIQUE
:
792 return pvfs_search_first_old(ntvfs
, req
, io
, search_private
, callback
);
794 case RAW_SEARCH_TRANS2
:
795 return pvfs_search_first_trans2(ntvfs
, req
, io
, search_private
, callback
);
797 case RAW_SEARCH_SMB2
:
798 return pvfs_search_first_smb2(ntvfs
, req
, &io
->smb2
, search_private
, callback
);
801 return NT_STATUS_INVALID_LEVEL
;
804 /* continue a search */
805 NTSTATUS
pvfs_search_next(struct ntvfs_module_context
*ntvfs
,
806 struct ntvfs_request
*req
, union smb_search_next
*io
,
807 void *search_private
,
808 bool (*callback
)(void *, const union smb_search_data
*))
810 switch (io
->generic
.level
) {
811 case RAW_SEARCH_SEARCH
:
812 case RAW_SEARCH_FFIRST
:
813 return pvfs_search_next_old(ntvfs
, req
, io
, search_private
, callback
);
815 case RAW_SEARCH_FUNIQUE
:
816 return NT_STATUS_INVALID_LEVEL
;
818 case RAW_SEARCH_TRANS2
:
819 return pvfs_search_next_trans2(ntvfs
, req
, io
, search_private
, callback
);
821 case RAW_SEARCH_SMB2
:
822 return pvfs_search_next_smb2(ntvfs
, req
, &io
->smb2
, search_private
, callback
);
825 return NT_STATUS_INVALID_LEVEL
;
830 NTSTATUS
pvfs_search_close(struct ntvfs_module_context
*ntvfs
,
831 struct ntvfs_request
*req
, union smb_search_close
*io
)
833 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
836 struct pvfs_search_state
*search
;
837 uint16_t handle
= INVALID_SEARCH_HANDLE
;
839 switch (io
->generic
.level
) {
840 case RAW_FINDCLOSE_GENERIC
:
841 return NT_STATUS_INVALID_LEVEL
;
843 case RAW_FINDCLOSE_FCLOSE
:
844 handle
= io
->fclose
.in
.id
.handle
;
847 case RAW_FINDCLOSE_FINDCLOSE
:
848 handle
= io
->findclose
.in
.handle
;
852 p
= idr_find(pvfs
->search
.idtree
, handle
);
854 /* we didn't find the search handle */
855 return NT_STATUS_INVALID_HANDLE
;
858 search
= talloc_get_type(p
, struct pvfs_search_state
);