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