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 "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
);
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
);
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
,
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
, 0, &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
:
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
;
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
;
250 if (max_count
== 0) {
254 while ((*reply_count
) < max_count
) {
255 union smb_search_data
*file
;
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
);
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
)) {
275 if (!callback(search_private
, file
)) {
277 search
->current_index
= ofs
;
285 pvfs_search_setup_timer(search
);
291 we've run out of search handles - cleanup those that the client forgot
294 static void pvfs_search_cleanup(struct pvfs_state
*pvfs
)
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
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
,
328 struct pvfs_search_state
*search
;
329 unsigned int reply_count
;
330 uint16_t search_attrib
;
333 struct pvfs_filename
*name
;
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
)) {
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
)) {
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
357 search
= talloc(req
, struct pvfs_search_state
);
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
)) {
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
);
372 pvfs_search_cleanup(pvfs
);
373 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_OLD_SEARCHES
);
376 return NT_STATUS_INSUFFICIENT_RESOURCES
;
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
);
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
)) {
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
);
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
,
419 struct pvfs_search_state
*search
;
420 struct pvfs_dir
*dir
;
421 unsigned int reply_count
, max_count
;
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
);
430 /* we didn't find the search handle */
431 return NT_STATUS_INVALID_HANDLE
;
434 search
= talloc_get_type(p
, struct pvfs_search_state
);
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
)) {
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
)) {
451 io
->search_next
.out
.count
= reply_count
;
453 /* not matching any entries means end of search */
454 if (reply_count
== 0) {
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
,
472 struct pvfs_search_state
*search
;
473 unsigned int reply_count
;
474 uint16_t search_attrib
, max_count
;
477 struct pvfs_filename
*name
;
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
)) {
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
)) {
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
502 search
= talloc(req
, struct pvfs_search_state
);
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
)) {
513 id
= idr_get_new(pvfs
->search
.idtree
, search
, MAX_SEARCH_HANDLES
);
515 return NT_STATUS_INSUFFICIENT_RESOURCES
;
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
;
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
)) {
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
)) {
554 talloc_steal(pvfs
, search
);
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
,
569 struct pvfs_search_state
*search
;
570 struct pvfs_dir
*dir
;
571 unsigned int reply_count
;
575 handle
= io
->t2fnext
.in
.handle
;
577 p
= idr_find(pvfs
->search
.idtree
, handle
);
579 /* we didn't find the search handle */
580 return NT_STATUS_INVALID_HANDLE
;
583 search
= talloc_get_type(p
, struct pvfs_search_state
);
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
)) {
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
)) {
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
)) {
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
,
634 struct pvfs_search_state
*search
;
635 unsigned int reply_count
;
639 struct pvfs_filename
*name
;
642 f
= pvfs_find_fd(pvfs
, req
, io
->in
.file
.ntvfs
);
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
;
657 talloc_free(f
->search
);
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
);
675 pattern
= talloc_asprintf(req
, "%s\\%s",
676 f
->handle
->name
->original_name
,
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
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
);
700 search
->handle
= INVALID_SEARCH_HANDLE
;
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
;
710 if (io
->in
.continue_flags
& SMB2_CONTINUE_FLAG_SINGLE
) {
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
);
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
,
737 struct pvfs_search_state
*search
;
738 unsigned int reply_count
;
743 f
= pvfs_find_fd(pvfs
, req
, io
->in
.file
.ntvfs
);
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 */
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
) {
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
;
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
;
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
,
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
;
848 case RAW_FINDCLOSE_FINDCLOSE
:
849 handle
= io
->findclose
.in
.handle
;
853 p
= idr_find(pvfs
->search
.idtree
, handle
);
855 /* we didn't find the search handle */
856 return NT_STATUS_INVALID_HANDLE
;
859 search
= talloc_get_type(p
, struct pvfs_search_state
);