Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : POSIX NTVFS backend - directory search functions
5 :
6 : Copyright (C) Andrew Tridgell 2004
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
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 :
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
35 :
36 : /*
37 : destroy an open search
38 : */
39 7103 : static int pvfs_search_destructor(struct pvfs_search_state *search)
40 : {
41 7103 : DLIST_REMOVE(search->pvfs->search.list, search);
42 7103 : idr_remove(search->pvfs->search.idtree, search->handle);
43 7103 : return 0;
44 : }
45 :
46 : /*
47 : called when a search timer goes off
48 : */
49 0 : static void pvfs_search_timer(struct tevent_context *ev, struct tevent_timer *te,
50 : struct timeval t, void *ptr)
51 : {
52 0 : struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
53 0 : talloc_free(search);
54 0 : }
55 :
56 : /*
57 : setup a timer to destroy a open search after a inactivity period
58 : */
59 8423 : static void pvfs_search_setup_timer(struct pvfs_search_state *search)
60 : {
61 8423 : struct tevent_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
62 8423 : if (search->handle == INVALID_SEARCH_HANDLE) return;
63 7509 : talloc_free(search->te);
64 7509 : search->te = tevent_add_timer(ev, search,
65 : timeval_current_ofs(search->pvfs->search.inactivity_time, 0),
66 : pvfs_search_timer, search);
67 : }
68 :
69 : /*
70 : fill in a single search result for a given info level
71 : */
72 130545 : static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
73 : enum smb_search_data_level level,
74 : const char *unix_path,
75 : const char *fname,
76 : struct pvfs_search_state *search,
77 : off_t dir_offset,
78 : union smb_search_data *file)
79 : {
80 : struct pvfs_filename *name;
81 : NTSTATUS status;
82 : const char *shortname;
83 130545 : 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 */
86 :
87 130545 : status = pvfs_resolve_partial(pvfs, file, unix_path, fname, 0, &name);
88 130545 : if (!NT_STATUS_IS_OK(status)) {
89 0 : return status;
90 : }
91 :
92 130545 : status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
93 130545 : if (!NT_STATUS_IS_OK(status)) {
94 6501 : return status;
95 : }
96 :
97 124044 : switch (level) {
98 8771 : case RAW_SEARCH_DATA_SEARCH:
99 8771 : shortname = pvfs_short_name(pvfs, name, name);
100 8771 : file->search.attrib = name->dos.attrib;
101 8771 : file->search.write_time = nt_time_to_unix(name->dos.write_time);
102 8771 : file->search.size = name->st.st_size;
103 8771 : file->search.name = shortname;
104 8771 : file->search.id.reserved = search->handle >> 8;
105 8771 : memset(file->search.id.name, ' ', sizeof(file->search.id.name));
106 16839 : memcpy(file->search.id.name, shortname,
107 16839 : MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
108 8771 : file->search.id.handle = search->handle & 0xFF;
109 8771 : file->search.id.server_cookie = dir_index;
110 8771 : file->search.id.client_cookie = 0;
111 8771 : return NT_STATUS_OK;
112 :
113 2103 : case RAW_SEARCH_DATA_STANDARD:
114 2103 : file->standard.resume_key = dir_index;
115 2103 : file->standard.create_time = nt_time_to_unix(name->dos.create_time);
116 2103 : file->standard.access_time = nt_time_to_unix(name->dos.access_time);
117 2103 : file->standard.write_time = nt_time_to_unix(name->dos.write_time);
118 2103 : file->standard.size = name->st.st_size;
119 2103 : file->standard.alloc_size = name->dos.alloc_size;
120 2103 : file->standard.attrib = name->dos.attrib;
121 2103 : file->standard.name.s = fname;
122 2103 : return NT_STATUS_OK;
123 :
124 18403 : case RAW_SEARCH_DATA_EA_SIZE:
125 18403 : file->ea_size.resume_key = dir_index;
126 18403 : file->ea_size.create_time = nt_time_to_unix(name->dos.create_time);
127 18403 : file->ea_size.access_time = nt_time_to_unix(name->dos.access_time);
128 18403 : file->ea_size.write_time = nt_time_to_unix(name->dos.write_time);
129 18403 : file->ea_size.size = name->st.st_size;
130 18403 : file->ea_size.alloc_size = name->dos.alloc_size;
131 18403 : file->ea_size.attrib = name->dos.attrib;
132 18403 : file->ea_size.ea_size = name->dos.ea_size;
133 18403 : file->ea_size.name.s = fname;
134 18403 : return NT_STATUS_OK;
135 :
136 3 : case RAW_SEARCH_DATA_EA_LIST:
137 3 : file->ea_list.resume_key = dir_index;
138 3 : file->ea_list.create_time = nt_time_to_unix(name->dos.create_time);
139 3 : file->ea_list.access_time = nt_time_to_unix(name->dos.access_time);
140 3 : file->ea_list.write_time = nt_time_to_unix(name->dos.write_time);
141 3 : file->ea_list.size = name->st.st_size;
142 3 : file->ea_list.alloc_size = name->dos.alloc_size;
143 3 : file->ea_list.attrib = name->dos.attrib;
144 3 : file->ea_list.name.s = fname;
145 3 : return pvfs_query_ea_list(pvfs, file, name, -1,
146 : search->num_ea_names,
147 : search->ea_names,
148 : &file->ea_list.eas);
149 :
150 2124 : case RAW_SEARCH_DATA_DIRECTORY_INFO:
151 2124 : file->directory_info.file_index = dir_index;
152 2124 : file->directory_info.create_time = name->dos.create_time;
153 2124 : file->directory_info.access_time = name->dos.access_time;
154 2124 : file->directory_info.write_time = name->dos.write_time;
155 2124 : file->directory_info.change_time = name->dos.change_time;
156 2124 : file->directory_info.size = name->st.st_size;
157 2124 : file->directory_info.alloc_size = name->dos.alloc_size;
158 2124 : file->directory_info.attrib = name->dos.attrib;
159 2124 : file->directory_info.name.s = fname;
160 2124 : return NT_STATUS_OK;
161 :
162 2277 : case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
163 2277 : file->full_directory_info.file_index = dir_index;
164 2277 : file->full_directory_info.create_time = name->dos.create_time;
165 2277 : file->full_directory_info.access_time = name->dos.access_time;
166 2277 : file->full_directory_info.write_time = name->dos.write_time;
167 2277 : file->full_directory_info.change_time = name->dos.change_time;
168 2277 : file->full_directory_info.size = name->st.st_size;
169 2277 : file->full_directory_info.alloc_size = name->dos.alloc_size;
170 2277 : file->full_directory_info.attrib = name->dos.attrib;
171 2277 : file->full_directory_info.ea_size = name->dos.ea_size;
172 2277 : file->full_directory_info.name.s = fname;
173 2277 : return NT_STATUS_OK;
174 :
175 65953 : case RAW_SEARCH_DATA_NAME_INFO:
176 65953 : file->name_info.file_index = dir_index;
177 65953 : file->name_info.name.s = fname;
178 65953 : return NT_STATUS_OK;
179 :
180 14694 : case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
181 14694 : file->both_directory_info.file_index = dir_index;
182 14694 : file->both_directory_info.create_time = name->dos.create_time;
183 14694 : file->both_directory_info.access_time = name->dos.access_time;
184 14694 : file->both_directory_info.write_time = name->dos.write_time;
185 14694 : file->both_directory_info.change_time = name->dos.change_time;
186 14694 : file->both_directory_info.size = name->st.st_size;
187 14694 : file->both_directory_info.alloc_size = name->dos.alloc_size;
188 14694 : file->both_directory_info.attrib = name->dos.attrib;
189 14694 : file->both_directory_info.ea_size = name->dos.ea_size;
190 14694 : file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
191 14694 : file->both_directory_info.name.s = fname;
192 14694 : return NT_STATUS_OK;
193 :
194 2103 : case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
195 2103 : file->id_full_directory_info.file_index = dir_index;
196 2103 : file->id_full_directory_info.create_time = name->dos.create_time;
197 2103 : file->id_full_directory_info.access_time = name->dos.access_time;
198 2103 : file->id_full_directory_info.write_time = name->dos.write_time;
199 2103 : file->id_full_directory_info.change_time = name->dos.change_time;
200 2103 : file->id_full_directory_info.size = name->st.st_size;
201 2103 : file->id_full_directory_info.alloc_size = name->dos.alloc_size;
202 2103 : file->id_full_directory_info.attrib = name->dos.attrib;
203 2103 : file->id_full_directory_info.ea_size = name->dos.ea_size;
204 2103 : file->id_full_directory_info.file_id = name->dos.file_id;
205 2103 : file->id_full_directory_info.name.s = fname;
206 2103 : return NT_STATUS_OK;
207 :
208 3630 : case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
209 3630 : file->id_both_directory_info.file_index = dir_index;
210 3630 : file->id_both_directory_info.create_time = name->dos.create_time;
211 3630 : file->id_both_directory_info.access_time = name->dos.access_time;
212 3630 : file->id_both_directory_info.write_time = name->dos.write_time;
213 3630 : file->id_both_directory_info.change_time = name->dos.change_time;
214 3630 : file->id_both_directory_info.size = name->st.st_size;
215 3630 : file->id_both_directory_info.alloc_size = name->dos.alloc_size;
216 3630 : file->id_both_directory_info.attrib = name->dos.attrib;
217 3630 : file->id_both_directory_info.ea_size = name->dos.ea_size;
218 3630 : file->id_both_directory_info.file_id = name->dos.file_id;
219 3630 : file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
220 3630 : file->id_both_directory_info.name.s = fname;
221 3630 : return NT_STATUS_OK;
222 :
223 5 : case RAW_SEARCH_DATA_GENERIC:
224 : case RAW_SEARCH_DATA_UNIX_INFO:
225 : case RAW_SEARCH_DATA_UNIX_INFO2:
226 5 : return NT_STATUS_INVALID_LEVEL;
227 : }
228 :
229 3978 : return NT_STATUS_INVALID_LEVEL;
230 : }
231 :
232 :
233 : /*
234 : the search fill loop
235 : */
236 8423 : 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 *))
243 : {
244 8423 : struct pvfs_dir *dir = search->dir;
245 : NTSTATUS status;
246 :
247 8423 : *reply_count = 0;
248 :
249 8423 : if (max_count == 0) {
250 2 : max_count = 1;
251 : }
252 :
253 147383 : while ((*reply_count) < max_count) {
254 : union smb_search_data *file;
255 : const char *name;
256 138367 : off_t ofs = search->current_index;
257 :
258 138367 : name = pvfs_list_next(dir, &search->current_index);
259 138367 : if (name == NULL) break;
260 :
261 130545 : file = talloc(mem_ctx, union smb_search_data);
262 130545 : if (!file) {
263 0 : return NT_STATUS_NO_MEMORY;
264 : }
265 :
266 130545 : status = fill_search_info(pvfs, level,
267 : pvfs_list_unix_path(dir), name,
268 : search, search->current_index, file);
269 130545 : if (!NT_STATUS_IS_OK(status)) {
270 10484 : talloc_free(file);
271 10484 : continue;
272 : }
273 :
274 120061 : if (!callback(search_private, file)) {
275 1 : talloc_free(file);
276 1 : search->current_index = ofs;
277 1 : break;
278 : }
279 :
280 120060 : (*reply_count)++;
281 120060 : talloc_free(file);
282 : }
283 :
284 8423 : pvfs_search_setup_timer(search);
285 :
286 8423 : return NT_STATUS_OK;
287 : }
288 :
289 : /*
290 : we've run out of search handles - cleanup those that the client forgot
291 : to close
292 : */
293 0 : static void pvfs_search_cleanup(struct pvfs_state *pvfs)
294 : {
295 : int i;
296 0 : time_t t = time_mono(NULL);
297 :
298 0 : for (i=0;i<MAX_OLD_SEARCHES;i++) {
299 : struct pvfs_search_state *search;
300 0 : void *p = idr_find(pvfs->search.idtree, i);
301 :
302 0 : if (p == NULL) return;
303 :
304 0 : search = talloc_get_type(p, struct pvfs_search_state);
305 0 : if (pvfs_list_eos(search->dir, search->current_index) &&
306 0 : search->last_used != 0 &&
307 0 : t > search->last_used + 30) {
308 : /* its almost certainly been forgotten
309 : about */
310 0 : talloc_free(search);
311 : }
312 : }
313 : }
314 :
315 :
316 : /*
317 : list files in a directory matching a wildcard pattern - old SMBsearch interface
318 : */
319 33 : 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 *))
323 : {
324 : struct pvfs_dir *dir;
325 33 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
326 : struct pvfs_state);
327 : struct pvfs_search_state *search;
328 : unsigned int reply_count;
329 : uint16_t search_attrib;
330 : const char *pattern;
331 : NTSTATUS status;
332 : struct pvfs_filename *name;
333 : int id;
334 :
335 33 : search_attrib = io->search_first.in.search_attrib;
336 33 : pattern = io->search_first.in.pattern;
337 :
338 : /* resolve the cifs name to a posix name */
339 33 : status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
340 33 : if (!NT_STATUS_IS_OK(status)) {
341 0 : return status;
342 : }
343 :
344 33 : if (!name->has_wildcard && !name->exists) {
345 3 : return STATUS_NO_MORE_FILES;
346 : }
347 :
348 30 : status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
349 30 : if (!NT_STATUS_IS_OK(status)) {
350 0 : return status;
351 : }
352 :
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
355 : structure */
356 30 : search = talloc(req, struct pvfs_search_state);
357 30 : if (!search) {
358 0 : return NT_STATUS_NO_MEMORY;
359 : }
360 :
361 : /* do the actual directory listing */
362 30 : status = pvfs_list_start(pvfs, name, search, &dir);
363 30 : if (!NT_STATUS_IS_OK(status)) {
364 0 : return status;
365 : }
366 :
367 : /* we need to give a handle back to the client so it
368 : can continue a search */
369 30 : id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
370 30 : if (id == -1) {
371 0 : pvfs_search_cleanup(pvfs);
372 0 : id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
373 : }
374 30 : if (id == -1) {
375 0 : return NT_STATUS_INSUFFICIENT_RESOURCES;
376 : }
377 :
378 30 : search->pvfs = pvfs;
379 30 : search->handle = id;
380 30 : search->dir = dir;
381 30 : search->current_index = 0;
382 30 : search->search_attrib = search_attrib & 0xFF;
383 30 : search->must_attrib = (search_attrib>>8) & 0xFF;
384 30 : search->last_used = time_mono(NULL);
385 30 : search->te = NULL;
386 :
387 30 : DLIST_ADD(pvfs->search.list, search);
388 :
389 30 : talloc_set_destructor(search, pvfs_search_destructor);
390 :
391 30 : status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
392 : &reply_count, search_private, callback);
393 30 : if (!NT_STATUS_IS_OK(status)) {
394 0 : return status;
395 : }
396 :
397 30 : io->search_first.out.count = reply_count;
398 :
399 : /* not matching any entries is an error */
400 30 : if (reply_count == 0) {
401 0 : return STATUS_NO_MORE_FILES;
402 : }
403 :
404 30 : talloc_steal(pvfs, search);
405 :
406 30 : return NT_STATUS_OK;
407 : }
408 :
409 : /* continue a old style search */
410 67 : 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 *))
414 : {
415 67 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
416 : struct pvfs_state);
417 : void *p;
418 : struct pvfs_search_state *search;
419 : struct pvfs_dir *dir;
420 : unsigned int reply_count, max_count;
421 : uint16_t handle;
422 : NTSTATUS status;
423 :
424 67 : handle = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
425 67 : max_count = io->search_next.in.max_count;
426 :
427 67 : p = idr_find(pvfs->search.idtree, handle);
428 67 : if (p == NULL) {
429 : /* we didn't find the search handle */
430 0 : return NT_STATUS_INVALID_HANDLE;
431 : }
432 :
433 67 : search = talloc_get_type(p, struct pvfs_search_state);
434 :
435 67 : dir = search->dir;
436 :
437 67 : status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie,
438 : &search->current_index);
439 67 : if (!NT_STATUS_IS_OK(status)) {
440 0 : return status;
441 : }
442 67 : search->last_used = time_mono(NULL);
443 :
444 67 : status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
445 : &reply_count, search_private, callback);
446 67 : if (!NT_STATUS_IS_OK(status)) {
447 0 : return status;
448 : }
449 :
450 67 : io->search_next.out.count = reply_count;
451 :
452 : /* not matching any entries means end of search */
453 67 : if (reply_count == 0) {
454 7 : talloc_free(search);
455 : }
456 :
457 67 : return NT_STATUS_OK;
458 : }
459 :
460 : /*
461 : list files in a directory matching a wildcard pattern
462 : */
463 7099 : 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 *))
467 : {
468 : struct pvfs_dir *dir;
469 7099 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
470 : struct pvfs_state);
471 : struct pvfs_search_state *search;
472 : unsigned int reply_count;
473 : uint16_t search_attrib, max_count;
474 : const char *pattern;
475 : NTSTATUS status;
476 : struct pvfs_filename *name;
477 : int id;
478 :
479 7099 : search_attrib = io->t2ffirst.in.search_attrib;
480 7099 : pattern = io->t2ffirst.in.pattern;
481 7099 : max_count = io->t2ffirst.in.max_count;
482 :
483 : /* resolve the cifs name to a posix name */
484 7099 : status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
485 7099 : if (!NT_STATUS_IS_OK(status)) {
486 2 : return status;
487 : }
488 :
489 7097 : if (!name->has_wildcard && !name->exists) {
490 20 : return NT_STATUS_NO_SUCH_FILE;
491 : }
492 :
493 7077 : status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
494 7077 : if (!NT_STATUS_IS_OK(status)) {
495 4 : return status;
496 : }
497 :
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
500 : structure */
501 7073 : search = talloc(req, struct pvfs_search_state);
502 7073 : if (!search) {
503 0 : return NT_STATUS_NO_MEMORY;
504 : }
505 :
506 : /* do the actual directory listing */
507 7073 : status = pvfs_list_start(pvfs, name, search, &dir);
508 7073 : if (!NT_STATUS_IS_OK(status)) {
509 0 : return status;
510 : }
511 :
512 7073 : id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
513 7073 : if (id == -1) {
514 0 : return NT_STATUS_INSUFFICIENT_RESOURCES;
515 : }
516 :
517 7073 : search->pvfs = pvfs;
518 7073 : search->handle = id;
519 7073 : search->dir = dir;
520 7073 : search->current_index = 0;
521 7073 : search->search_attrib = search_attrib;
522 7073 : search->must_attrib = 0;
523 7073 : search->last_used = 0;
524 7073 : search->num_ea_names = io->t2ffirst.in.num_names;
525 7073 : search->ea_names = io->t2ffirst.in.ea_names;
526 7073 : search->te = NULL;
527 :
528 7073 : DLIST_ADD(pvfs->search.list, search);
529 7073 : talloc_set_destructor(search, pvfs_search_destructor);
530 :
531 7073 : status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
532 : &reply_count, search_private, callback);
533 7073 : if (!NT_STATUS_IS_OK(status)) {
534 0 : return status;
535 : }
536 :
537 : /* not matching any entries is an error */
538 7073 : if (reply_count == 0) {
539 4236 : return NT_STATUS_NO_SUCH_FILE;
540 : }
541 :
542 2837 : io->t2ffirst.out.count = reply_count;
543 2837 : io->t2ffirst.out.handle = search->handle;
544 2837 : io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
545 :
546 : /* work out if we are going to keep the search state
547 : and allow for a search continue */
548 5470 : if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
549 5268 : ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
550 2635 : io->t2ffirst.out.end_of_search)) {
551 2805 : talloc_free(search);
552 : } else {
553 32 : talloc_steal(pvfs, search);
554 : }
555 :
556 2837 : return NT_STATUS_OK;
557 : }
558 :
559 : /* continue a search */
560 339 : 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 *))
564 : {
565 339 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
566 : struct pvfs_state);
567 : void *p;
568 : struct pvfs_search_state *search;
569 : struct pvfs_dir *dir;
570 : unsigned int reply_count;
571 : uint16_t handle;
572 : NTSTATUS status;
573 :
574 339 : handle = io->t2fnext.in.handle;
575 :
576 339 : p = idr_find(pvfs->search.idtree, handle);
577 339 : if (p == NULL) {
578 : /* we didn't find the search handle */
579 0 : return NT_STATUS_INVALID_HANDLE;
580 : }
581 :
582 339 : search = talloc_get_type(p, struct pvfs_search_state);
583 :
584 339 : dir = search->dir;
585 :
586 339 : status = NT_STATUS_OK;
587 :
588 : /* work out what type of continuation is being used */
589 339 : if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
590 241 : status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
591 482 : if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
592 0 : status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
593 : &search->current_index);
594 : }
595 98 : } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
596 49 : status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
597 : &search->current_index);
598 : }
599 339 : if (!NT_STATUS_IS_OK(status)) {
600 0 : return status;
601 : }
602 :
603 339 : search->num_ea_names = io->t2fnext.in.num_names;
604 339 : search->ea_names = io->t2fnext.in.ea_names;
605 :
606 339 : status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
607 : &reply_count, search_private, callback);
608 339 : if (!NT_STATUS_IS_OK(status)) {
609 0 : return status;
610 : }
611 :
612 339 : io->t2fnext.out.count = reply_count;
613 339 : io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
614 :
615 : /* work out if we are going to keep the search state */
616 678 : if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
617 499 : ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
618 160 : io->t2fnext.out.end_of_search)) {
619 28 : talloc_free(search);
620 : }
621 :
622 339 : return NT_STATUS_OK;
623 : }
624 :
625 470 : 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 *))
629 : {
630 : struct pvfs_dir *dir;
631 470 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
632 : struct pvfs_state);
633 : struct pvfs_search_state *search;
634 : unsigned int reply_count;
635 : uint16_t max_count;
636 : const char *pattern;
637 : NTSTATUS status;
638 : struct pvfs_filename *name;
639 : struct pvfs_file *f;
640 :
641 470 : f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
642 470 : if (!f) {
643 0 : return NT_STATUS_FILE_CLOSED;
644 : }
645 :
646 : /* its only valid for directories */
647 470 : if (f->handle->fd != -1) {
648 0 : return NT_STATUS_INVALID_PARAMETER;
649 : }
650 :
651 470 : if (!(f->access_mask & SEC_DIR_LIST)) {
652 0 : return NT_STATUS_ACCESS_DENIED;
653 : }
654 :
655 470 : if (f->search) {
656 0 : talloc_free(f->search);
657 0 : f->search = NULL;
658 : }
659 :
660 470 : if (strequal(io->in.pattern, "")) {
661 0 : return NT_STATUS_OBJECT_NAME_INVALID;
662 : }
663 470 : if (strchr_m(io->in.pattern, '\\')) {
664 0 : return NT_STATUS_OBJECT_NAME_INVALID;
665 : }
666 470 : if (strchr_m(io->in.pattern, '/')) {
667 0 : return NT_STATUS_OBJECT_NAME_INVALID;
668 : }
669 :
670 470 : if (strequal("", f->handle->name->original_name)) {
671 13 : pattern = talloc_asprintf(req, "%s", io->in.pattern);
672 13 : NT_STATUS_HAVE_NO_MEMORY(pattern);
673 : } else {
674 914 : pattern = talloc_asprintf(req, "%s\\%s",
675 457 : f->handle->name->original_name,
676 0 : io->in.pattern);
677 457 : NT_STATUS_HAVE_NO_MEMORY(pattern);
678 : }
679 :
680 : /* resolve the cifs name to a posix name */
681 470 : status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
682 470 : NT_STATUS_NOT_OK_RETURN(status);
683 :
684 470 : if (!name->has_wildcard && !name->exists) {
685 0 : return NT_STATUS_NO_SUCH_FILE;
686 : }
687 :
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
690 : structure */
691 470 : search = talloc(req, struct pvfs_search_state);
692 470 : NT_STATUS_HAVE_NO_MEMORY(search);
693 :
694 : /* do the actual directory listing */
695 470 : status = pvfs_list_start(pvfs, name, search, &dir);
696 470 : NT_STATUS_NOT_OK_RETURN(status);
697 :
698 470 : search->pvfs = pvfs;
699 470 : search->handle = INVALID_SEARCH_HANDLE;
700 470 : search->dir = dir;
701 470 : search->current_index = 0;
702 470 : search->search_attrib = 0x0000FFFF;
703 470 : search->must_attrib = 0;
704 470 : search->last_used = 0;
705 470 : search->num_ea_names = 0;
706 470 : search->ea_names = NULL;
707 470 : search->te = NULL;
708 :
709 470 : if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
710 1 : max_count = 1;
711 : } else {
712 469 : max_count = UINT16_MAX;
713 : }
714 :
715 470 : status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
716 : &reply_count, search_private, callback);
717 470 : NT_STATUS_NOT_OK_RETURN(status);
718 :
719 : /* not matching any entries is an error */
720 470 : if (reply_count == 0) {
721 0 : return NT_STATUS_NO_SUCH_FILE;
722 : }
723 :
724 470 : f->search = talloc_steal(f, search);
725 :
726 470 : return NT_STATUS_OK;
727 : }
728 :
729 914 : 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 *))
733 : {
734 914 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
735 : struct pvfs_state);
736 : struct pvfs_search_state *search;
737 : unsigned int reply_count;
738 : uint16_t max_count;
739 : NTSTATUS status;
740 : struct pvfs_file *f;
741 :
742 914 : f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
743 914 : if (!f) {
744 0 : return NT_STATUS_FILE_CLOSED;
745 : }
746 :
747 : /* its only valid for directories */
748 914 : if (f->handle->fd != -1) {
749 0 : return NT_STATUS_INVALID_PARAMETER;
750 : }
751 :
752 : /* if there's no search started on the dir handle, it's like a search_first */
753 914 : search = f->search;
754 914 : if (!search) {
755 470 : return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
756 : }
757 :
758 444 : if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
759 0 : search->current_index = 0;
760 : }
761 :
762 444 : if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
763 0 : max_count = 1;
764 : } else {
765 444 : max_count = UINT16_MAX;
766 : }
767 :
768 444 : status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
769 : &reply_count, search_private, callback);
770 444 : NT_STATUS_NOT_OK_RETURN(status);
771 :
772 : /* not matching any entries is an error */
773 444 : if (reply_count == 0) {
774 444 : return STATUS_NO_MORE_FILES;
775 : }
776 :
777 0 : return NT_STATUS_OK;
778 : }
779 :
780 : /*
781 : list files in a directory matching a wildcard pattern
782 : */
783 7132 : 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 *))
787 : {
788 7132 : switch (io->generic.level) {
789 33 : case RAW_SEARCH_SEARCH:
790 : case RAW_SEARCH_FFIRST:
791 : case RAW_SEARCH_FUNIQUE:
792 33 : return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
793 :
794 7099 : case RAW_SEARCH_TRANS2:
795 7099 : return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
796 :
797 0 : case RAW_SEARCH_SMB2:
798 0 : return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
799 : }
800 :
801 0 : return NT_STATUS_INVALID_LEVEL;
802 : }
803 :
804 : /* continue a search */
805 1320 : 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 *))
809 : {
810 1320 : switch (io->generic.level) {
811 67 : case RAW_SEARCH_SEARCH:
812 : case RAW_SEARCH_FFIRST:
813 67 : return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
814 :
815 0 : case RAW_SEARCH_FUNIQUE:
816 0 : return NT_STATUS_INVALID_LEVEL;
817 :
818 339 : case RAW_SEARCH_TRANS2:
819 339 : return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
820 :
821 914 : case RAW_SEARCH_SMB2:
822 914 : return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
823 : }
824 :
825 0 : return NT_STATUS_INVALID_LEVEL;
826 : }
827 :
828 :
829 : /* close a search */
830 1 : NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
831 : struct ntvfs_request *req, union smb_search_close *io)
832 : {
833 1 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
834 : struct pvfs_state);
835 : void *p;
836 : struct pvfs_search_state *search;
837 1 : uint16_t handle = INVALID_SEARCH_HANDLE;
838 :
839 1 : switch (io->generic.level) {
840 0 : case RAW_FINDCLOSE_GENERIC:
841 0 : return NT_STATUS_INVALID_LEVEL;
842 :
843 1 : case RAW_FINDCLOSE_FCLOSE:
844 1 : handle = io->fclose.in.id.handle;
845 1 : break;
846 :
847 0 : case RAW_FINDCLOSE_FINDCLOSE:
848 0 : handle = io->findclose.in.handle;
849 0 : break;
850 : }
851 :
852 1 : p = idr_find(pvfs->search.idtree, handle);
853 1 : if (p == NULL) {
854 : /* we didn't find the search handle */
855 0 : return NT_STATUS_INVALID_HANDLE;
856 : }
857 :
858 1 : search = talloc_get_type(p, struct pvfs_search_state);
859 :
860 1 : talloc_free(search);
861 :
862 1 : return NT_STATUS_OK;
863 : }
864 :
|