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
= event_add_timed(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
:
227 return NT_STATUS_INVALID_LEVEL
;
234 static NTSTATUS
pvfs_search_fill(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
235 unsigned int max_count
,
236 struct pvfs_search_state
*search
,
237 enum smb_search_data_level level
,
238 unsigned int *reply_count
,
239 void *search_private
,
240 bool (*callback
)(void *, const union smb_search_data
*))
242 struct pvfs_dir
*dir
= search
->dir
;
247 if (max_count
== 0) {
251 while ((*reply_count
) < max_count
) {
252 union smb_search_data
*file
;
254 off_t ofs
= search
->current_index
;
256 name
= pvfs_list_next(dir
, &search
->current_index
);
257 if (name
== NULL
) break;
259 file
= talloc(mem_ctx
, union smb_search_data
);
261 return NT_STATUS_NO_MEMORY
;
264 status
= fill_search_info(pvfs
, level
,
265 pvfs_list_unix_path(dir
), name
,
266 search
, search
->current_index
, file
);
267 if (!NT_STATUS_IS_OK(status
)) {
272 if (!callback(search_private
, file
)) {
274 search
->current_index
= ofs
;
282 pvfs_search_setup_timer(search
);
288 we've run out of search handles - cleanup those that the client forgot
291 static void pvfs_search_cleanup(struct pvfs_state
*pvfs
)
294 time_t t
= time_mono(NULL
);
296 for (i
=0;i
<MAX_OLD_SEARCHES
;i
++) {
297 struct pvfs_search_state
*search
;
298 void *p
= idr_find(pvfs
->search
.idtree
, i
);
300 if (p
== NULL
) return;
302 search
= talloc_get_type(p
, struct pvfs_search_state
);
303 if (pvfs_list_eos(search
->dir
, search
->current_index
) &&
304 search
->last_used
!= 0 &&
305 t
> search
->last_used
+ 30) {
306 /* its almost certainly been forgotten
315 list files in a directory matching a wildcard pattern - old SMBsearch interface
317 static NTSTATUS
pvfs_search_first_old(struct ntvfs_module_context
*ntvfs
,
318 struct ntvfs_request
*req
, union smb_search_first
*io
,
319 void *search_private
,
320 bool (*callback
)(void *, const union smb_search_data
*))
322 struct pvfs_dir
*dir
;
323 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
325 struct pvfs_search_state
*search
;
326 unsigned int reply_count
;
327 uint16_t search_attrib
;
330 struct pvfs_filename
*name
;
333 search_attrib
= io
->search_first
.in
.search_attrib
;
334 pattern
= io
->search_first
.in
.pattern
;
336 /* resolve the cifs name to a posix name */
337 status
= pvfs_resolve_name(pvfs
, req
, pattern
, PVFS_RESOLVE_WILDCARD
, &name
);
338 if (!NT_STATUS_IS_OK(status
)) {
342 if (!name
->has_wildcard
&& !name
->exists
) {
343 return STATUS_NO_MORE_FILES
;
346 status
= pvfs_access_check_parent(pvfs
, req
, name
, SEC_DIR_TRAVERSE
| SEC_DIR_LIST
);
347 if (!NT_STATUS_IS_OK(status
)) {
351 /* we initially make search a child of the request, then if we
352 need to keep it long term we steal it for the private
354 search
= talloc(req
, struct pvfs_search_state
);
356 return NT_STATUS_NO_MEMORY
;
359 /* do the actual directory listing */
360 status
= pvfs_list_start(pvfs
, name
, search
, &dir
);
361 if (!NT_STATUS_IS_OK(status
)) {
365 /* we need to give a handle back to the client so it
366 can continue a search */
367 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_OLD_SEARCHES
);
369 pvfs_search_cleanup(pvfs
);
370 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_OLD_SEARCHES
);
373 return NT_STATUS_INSUFFICIENT_RESOURCES
;
379 search
->current_index
= 0;
380 search
->search_attrib
= search_attrib
& 0xFF;
381 search
->must_attrib
= (search_attrib
>>8) & 0xFF;
382 search
->last_used
= time_mono(NULL
);
385 DLIST_ADD(pvfs
->search
.list
, search
);
387 talloc_set_destructor(search
, pvfs_search_destructor
);
389 status
= pvfs_search_fill(pvfs
, req
, io
->search_first
.in
.max_count
, search
, io
->generic
.data_level
,
390 &reply_count
, search_private
, callback
);
391 if (!NT_STATUS_IS_OK(status
)) {
395 io
->search_first
.out
.count
= reply_count
;
397 /* not matching any entries is an error */
398 if (reply_count
== 0) {
399 return STATUS_NO_MORE_FILES
;
402 talloc_steal(pvfs
, search
);
407 /* continue a old style search */
408 static NTSTATUS
pvfs_search_next_old(struct ntvfs_module_context
*ntvfs
,
409 struct ntvfs_request
*req
, union smb_search_next
*io
,
410 void *search_private
,
411 bool (*callback
)(void *, const union smb_search_data
*))
413 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
416 struct pvfs_search_state
*search
;
417 struct pvfs_dir
*dir
;
418 unsigned int reply_count
, max_count
;
422 handle
= io
->search_next
.in
.id
.handle
| (io
->search_next
.in
.id
.reserved
<<8);
423 max_count
= io
->search_next
.in
.max_count
;
425 p
= idr_find(pvfs
->search
.idtree
, handle
);
427 /* we didn't find the search handle */
428 return NT_STATUS_INVALID_HANDLE
;
431 search
= talloc_get_type(p
, struct pvfs_search_state
);
435 status
= pvfs_list_seek_ofs(dir
, io
->search_next
.in
.id
.server_cookie
,
436 &search
->current_index
);
437 if (!NT_STATUS_IS_OK(status
)) {
440 search
->last_used
= time_mono(NULL
);
442 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->generic
.data_level
,
443 &reply_count
, search_private
, callback
);
444 if (!NT_STATUS_IS_OK(status
)) {
448 io
->search_next
.out
.count
= reply_count
;
450 /* not matching any entries means end of search */
451 if (reply_count
== 0) {
459 list files in a directory matching a wildcard pattern
461 static NTSTATUS
pvfs_search_first_trans2(struct ntvfs_module_context
*ntvfs
,
462 struct ntvfs_request
*req
, union smb_search_first
*io
,
463 void *search_private
,
464 bool (*callback
)(void *, const union smb_search_data
*))
466 struct pvfs_dir
*dir
;
467 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
469 struct pvfs_search_state
*search
;
470 unsigned int reply_count
;
471 uint16_t search_attrib
, max_count
;
474 struct pvfs_filename
*name
;
477 search_attrib
= io
->t2ffirst
.in
.search_attrib
;
478 pattern
= io
->t2ffirst
.in
.pattern
;
479 max_count
= io
->t2ffirst
.in
.max_count
;
481 /* resolve the cifs name to a posix name */
482 status
= pvfs_resolve_name(pvfs
, req
, pattern
, PVFS_RESOLVE_WILDCARD
, &name
);
483 if (!NT_STATUS_IS_OK(status
)) {
487 if (!name
->has_wildcard
&& !name
->exists
) {
488 return NT_STATUS_NO_SUCH_FILE
;
491 status
= pvfs_access_check_parent(pvfs
, req
, name
, SEC_DIR_TRAVERSE
| SEC_DIR_LIST
);
492 if (!NT_STATUS_IS_OK(status
)) {
496 /* we initially make search a child of the request, then if we
497 need to keep it long term we steal it for the private
499 search
= talloc(req
, struct pvfs_search_state
);
501 return NT_STATUS_NO_MEMORY
;
504 /* do the actual directory listing */
505 status
= pvfs_list_start(pvfs
, name
, search
, &dir
);
506 if (!NT_STATUS_IS_OK(status
)) {
510 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_SEARCH_HANDLES
);
512 return NT_STATUS_INSUFFICIENT_RESOURCES
;
518 search
->current_index
= 0;
519 search
->search_attrib
= search_attrib
;
520 search
->must_attrib
= 0;
521 search
->last_used
= 0;
522 search
->num_ea_names
= io
->t2ffirst
.in
.num_names
;
523 search
->ea_names
= io
->t2ffirst
.in
.ea_names
;
526 DLIST_ADD(pvfs
->search
.list
, search
);
527 talloc_set_destructor(search
, pvfs_search_destructor
);
529 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->generic
.data_level
,
530 &reply_count
, search_private
, callback
);
531 if (!NT_STATUS_IS_OK(status
)) {
535 /* not matching any entries is an error */
536 if (reply_count
== 0) {
537 return NT_STATUS_NO_SUCH_FILE
;
540 io
->t2ffirst
.out
.count
= reply_count
;
541 io
->t2ffirst
.out
.handle
= search
->handle
;
542 io
->t2ffirst
.out
.end_of_search
= pvfs_list_eos(dir
, search
->current_index
) ? 1 : 0;
544 /* work out if we are going to keep the search state
545 and allow for a search continue */
546 if ((io
->t2ffirst
.in
.flags
& FLAG_TRANS2_FIND_CLOSE
) ||
547 ((io
->t2ffirst
.in
.flags
& FLAG_TRANS2_FIND_CLOSE_IF_END
) &&
548 io
->t2ffirst
.out
.end_of_search
)) {
551 talloc_steal(pvfs
, search
);
557 /* continue a search */
558 static NTSTATUS
pvfs_search_next_trans2(struct ntvfs_module_context
*ntvfs
,
559 struct ntvfs_request
*req
, union smb_search_next
*io
,
560 void *search_private
,
561 bool (*callback
)(void *, const union smb_search_data
*))
563 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
566 struct pvfs_search_state
*search
;
567 struct pvfs_dir
*dir
;
568 unsigned int reply_count
;
572 handle
= io
->t2fnext
.in
.handle
;
574 p
= idr_find(pvfs
->search
.idtree
, handle
);
576 /* we didn't find the search handle */
577 return NT_STATUS_INVALID_HANDLE
;
580 search
= talloc_get_type(p
, struct pvfs_search_state
);
584 status
= NT_STATUS_OK
;
586 /* work out what type of continuation is being used */
587 if (io
->t2fnext
.in
.last_name
&& *io
->t2fnext
.in
.last_name
) {
588 status
= pvfs_list_seek(dir
, io
->t2fnext
.in
.last_name
, &search
->current_index
);
589 if (!NT_STATUS_IS_OK(status
) && io
->t2fnext
.in
.resume_key
) {
590 status
= pvfs_list_seek_ofs(dir
, io
->t2fnext
.in
.resume_key
,
591 &search
->current_index
);
593 } else if (!(io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CONTINUE
)) {
594 status
= pvfs_list_seek_ofs(dir
, io
->t2fnext
.in
.resume_key
,
595 &search
->current_index
);
597 if (!NT_STATUS_IS_OK(status
)) {
601 search
->num_ea_names
= io
->t2fnext
.in
.num_names
;
602 search
->ea_names
= io
->t2fnext
.in
.ea_names
;
604 status
= pvfs_search_fill(pvfs
, req
, io
->t2fnext
.in
.max_count
, search
, io
->generic
.data_level
,
605 &reply_count
, search_private
, callback
);
606 if (!NT_STATUS_IS_OK(status
)) {
610 io
->t2fnext
.out
.count
= reply_count
;
611 io
->t2fnext
.out
.end_of_search
= pvfs_list_eos(dir
, search
->current_index
) ? 1 : 0;
613 /* work out if we are going to keep the search state */
614 if ((io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CLOSE
) ||
615 ((io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CLOSE_IF_END
) &&
616 io
->t2fnext
.out
.end_of_search
)) {
623 static NTSTATUS
pvfs_search_first_smb2(struct ntvfs_module_context
*ntvfs
,
624 struct ntvfs_request
*req
, const struct smb2_find
*io
,
625 void *search_private
,
626 bool (*callback
)(void *, const union smb_search_data
*))
628 struct pvfs_dir
*dir
;
629 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
631 struct pvfs_search_state
*search
;
632 unsigned int reply_count
;
636 struct pvfs_filename
*name
;
639 f
= pvfs_find_fd(pvfs
, req
, io
->in
.file
.ntvfs
);
641 return NT_STATUS_FILE_CLOSED
;
644 /* its only valid for directories */
645 if (f
->handle
->fd
!= -1) {
646 return NT_STATUS_INVALID_PARAMETER
;
649 if (!(f
->access_mask
& SEC_DIR_LIST
)) {
650 return NT_STATUS_ACCESS_DENIED
;
654 talloc_free(f
->search
);
658 if (strequal(io
->in
.pattern
, "")) {
659 return NT_STATUS_OBJECT_NAME_INVALID
;
661 if (strchr_m(io
->in
.pattern
, '\\')) {
662 return NT_STATUS_OBJECT_NAME_INVALID
;
664 if (strchr_m(io
->in
.pattern
, '/')) {
665 return NT_STATUS_OBJECT_NAME_INVALID
;
668 if (strequal("", f
->handle
->name
->original_name
)) {
669 pattern
= talloc_asprintf(req
, "%s", io
->in
.pattern
);
670 NT_STATUS_HAVE_NO_MEMORY(pattern
);
672 pattern
= talloc_asprintf(req
, "%s\\%s",
673 f
->handle
->name
->original_name
,
675 NT_STATUS_HAVE_NO_MEMORY(pattern
);
678 /* resolve the cifs name to a posix name */
679 status
= pvfs_resolve_name(pvfs
, req
, pattern
, PVFS_RESOLVE_WILDCARD
, &name
);
680 NT_STATUS_NOT_OK_RETURN(status
);
682 if (!name
->has_wildcard
&& !name
->exists
) {
683 return NT_STATUS_NO_SUCH_FILE
;
686 /* we initially make search a child of the request, then if we
687 need to keep it long term we steal it for the private
689 search
= talloc(req
, struct pvfs_search_state
);
690 NT_STATUS_HAVE_NO_MEMORY(search
);
692 /* do the actual directory listing */
693 status
= pvfs_list_start(pvfs
, name
, search
, &dir
);
694 NT_STATUS_NOT_OK_RETURN(status
);
697 search
->handle
= INVALID_SEARCH_HANDLE
;
699 search
->current_index
= 0;
700 search
->search_attrib
= 0x0000FFFF;
701 search
->must_attrib
= 0;
702 search
->last_used
= 0;
703 search
->num_ea_names
= 0;
704 search
->ea_names
= NULL
;
707 if (io
->in
.continue_flags
& SMB2_CONTINUE_FLAG_SINGLE
) {
710 max_count
= UINT16_MAX
;
713 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->data_level
,
714 &reply_count
, search_private
, callback
);
715 NT_STATUS_NOT_OK_RETURN(status
);
717 /* not matching any entries is an error */
718 if (reply_count
== 0) {
719 return NT_STATUS_NO_SUCH_FILE
;
722 f
->search
= talloc_steal(f
, search
);
727 static NTSTATUS
pvfs_search_next_smb2(struct ntvfs_module_context
*ntvfs
,
728 struct ntvfs_request
*req
, const struct smb2_find
*io
,
729 void *search_private
,
730 bool (*callback
)(void *, const union smb_search_data
*))
732 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
734 struct pvfs_search_state
*search
;
735 unsigned int reply_count
;
740 f
= pvfs_find_fd(pvfs
, req
, io
->in
.file
.ntvfs
);
742 return NT_STATUS_FILE_CLOSED
;
745 /* its only valid for directories */
746 if (f
->handle
->fd
!= -1) {
747 return NT_STATUS_INVALID_PARAMETER
;
750 /* if there's no search started on the dir handle, it's like a search_first */
753 return pvfs_search_first_smb2(ntvfs
, req
, io
, search_private
, callback
);
756 if (io
->in
.continue_flags
& SMB2_CONTINUE_FLAG_RESTART
) {
757 search
->current_index
= 0;
760 if (io
->in
.continue_flags
& SMB2_CONTINUE_FLAG_SINGLE
) {
763 max_count
= UINT16_MAX
;
766 status
= pvfs_search_fill(pvfs
, req
, max_count
, search
, io
->data_level
,
767 &reply_count
, search_private
, callback
);
768 NT_STATUS_NOT_OK_RETURN(status
);
770 /* not matching any entries is an error */
771 if (reply_count
== 0) {
772 return STATUS_NO_MORE_FILES
;
779 list files in a directory matching a wildcard pattern
781 NTSTATUS
pvfs_search_first(struct ntvfs_module_context
*ntvfs
,
782 struct ntvfs_request
*req
, union smb_search_first
*io
,
783 void *search_private
,
784 bool (*callback
)(void *, const union smb_search_data
*))
786 switch (io
->generic
.level
) {
787 case RAW_SEARCH_SEARCH
:
788 case RAW_SEARCH_FFIRST
:
789 case RAW_SEARCH_FUNIQUE
:
790 return pvfs_search_first_old(ntvfs
, req
, io
, search_private
, callback
);
792 case RAW_SEARCH_TRANS2
:
793 return pvfs_search_first_trans2(ntvfs
, req
, io
, search_private
, callback
);
795 case RAW_SEARCH_SMB2
:
796 return pvfs_search_first_smb2(ntvfs
, req
, &io
->smb2
, search_private
, callback
);
799 return NT_STATUS_INVALID_LEVEL
;
802 /* continue a search */
803 NTSTATUS
pvfs_search_next(struct ntvfs_module_context
*ntvfs
,
804 struct ntvfs_request
*req
, union smb_search_next
*io
,
805 void *search_private
,
806 bool (*callback
)(void *, const union smb_search_data
*))
808 switch (io
->generic
.level
) {
809 case RAW_SEARCH_SEARCH
:
810 case RAW_SEARCH_FFIRST
:
811 return pvfs_search_next_old(ntvfs
, req
, io
, search_private
, callback
);
813 case RAW_SEARCH_FUNIQUE
:
814 return NT_STATUS_INVALID_LEVEL
;
816 case RAW_SEARCH_TRANS2
:
817 return pvfs_search_next_trans2(ntvfs
, req
, io
, search_private
, callback
);
819 case RAW_SEARCH_SMB2
:
820 return pvfs_search_next_smb2(ntvfs
, req
, &io
->smb2
, search_private
, callback
);
823 return NT_STATUS_INVALID_LEVEL
;
828 NTSTATUS
pvfs_search_close(struct ntvfs_module_context
*ntvfs
,
829 struct ntvfs_request
*req
, union smb_search_close
*io
)
831 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
834 struct pvfs_search_state
*search
;
835 uint16_t handle
= INVALID_SEARCH_HANDLE
;
837 switch (io
->generic
.level
) {
838 case RAW_FINDCLOSE_GENERIC
:
839 return NT_STATUS_INVALID_LEVEL
;
841 case RAW_FINDCLOSE_FCLOSE
:
842 handle
= io
->fclose
.in
.id
.handle
;
845 case RAW_FINDCLOSE_FINDCLOSE
:
846 handle
= io
->findclose
.in
.handle
;
850 p
= idr_find(pvfs
->search
.idtree
, handle
);
852 /* we didn't find the search handle */
853 return NT_STATUS_INVALID_HANDLE
;
856 search
= talloc_get_type(p
, struct pvfs_search_state
);