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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "include/includes.h"
24 #include "vfs_posix.h"
28 destroy an open search
30 static int pvfs_search_destructor(void *ptr)
32 struct pvfs_search_state *search = ptr;
33 idr_remove(search->pvfs->idtree_search, search->handle);
38 fill in a single search result for a given info level
40 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
41 enum smb_search_level level,
42 const char *unix_path,
44 struct pvfs_search_state *search,
46 union smb_search_data *file)
48 struct pvfs_filename *name;
50 const char *shortname;
52 status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
53 if (!NT_STATUS_IS_OK(status)) {
57 status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
58 if (!NT_STATUS_IS_OK(status)) {
63 case RAW_SEARCH_SEARCH:
64 case RAW_SEARCH_FFIRST:
65 case RAW_SEARCH_FUNIQUE:
66 shortname = pvfs_short_name(pvfs, name, name);
67 file->search.attrib = name->dos.attrib;
68 file->search.write_time = nt_time_to_unix(name->dos.write_time);
69 file->search.size = name->st.st_size;
70 file->search.name = shortname;
71 file->search.id.reserved = 8;
72 memset(file->search.id.name, ' ', sizeof(file->search.id.name));
73 memcpy(file->search.id.name, shortname,
74 MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
75 file->search.id.handle = search->handle;
76 file->search.id.server_cookie = dir_index;
77 file->search.id.client_cookie = 0;
80 case RAW_SEARCH_STANDARD:
81 file->standard.resume_key = dir_index;
82 file->standard.create_time = nt_time_to_unix(name->dos.create_time);
83 file->standard.access_time = nt_time_to_unix(name->dos.access_time);
84 file->standard.write_time = nt_time_to_unix(name->dos.write_time);
85 file->standard.size = name->st.st_size;
86 file->standard.alloc_size = name->dos.alloc_size;
87 file->standard.attrib = name->dos.attrib;
88 file->standard.name.s = fname;
91 case RAW_SEARCH_EA_SIZE:
92 file->ea_size.resume_key = dir_index;
93 file->ea_size.create_time = nt_time_to_unix(name->dos.create_time);
94 file->ea_size.access_time = nt_time_to_unix(name->dos.access_time);
95 file->ea_size.write_time = nt_time_to_unix(name->dos.write_time);
96 file->ea_size.size = name->st.st_size;
97 file->ea_size.alloc_size = name->dos.alloc_size;
98 file->ea_size.attrib = name->dos.attrib;
99 file->ea_size.ea_size = name->dos.ea_size;
100 file->ea_size.name.s = fname;
103 case RAW_SEARCH_DIRECTORY_INFO:
104 file->directory_info.file_index = dir_index;
105 file->directory_info.create_time = name->dos.create_time;
106 file->directory_info.access_time = name->dos.access_time;
107 file->directory_info.write_time = name->dos.write_time;
108 file->directory_info.change_time = name->dos.change_time;
109 file->directory_info.size = name->st.st_size;
110 file->directory_info.alloc_size = name->dos.alloc_size;
111 file->directory_info.attrib = name->dos.attrib;
112 file->directory_info.name.s = fname;
115 case RAW_SEARCH_FULL_DIRECTORY_INFO:
116 file->full_directory_info.file_index = dir_index;
117 file->full_directory_info.create_time = name->dos.create_time;
118 file->full_directory_info.access_time = name->dos.access_time;
119 file->full_directory_info.write_time = name->dos.write_time;
120 file->full_directory_info.change_time = name->dos.change_time;
121 file->full_directory_info.size = name->st.st_size;
122 file->full_directory_info.alloc_size = name->dos.alloc_size;
123 file->full_directory_info.attrib = name->dos.attrib;
124 file->full_directory_info.ea_size = name->dos.ea_size;
125 file->full_directory_info.name.s = fname;
128 case RAW_SEARCH_NAME_INFO:
129 file->name_info.file_index = dir_index;
130 file->name_info.name.s = fname;
133 case RAW_SEARCH_BOTH_DIRECTORY_INFO:
134 file->both_directory_info.file_index = dir_index;
135 file->both_directory_info.create_time = name->dos.create_time;
136 file->both_directory_info.access_time = name->dos.access_time;
137 file->both_directory_info.write_time = name->dos.write_time;
138 file->both_directory_info.change_time = name->dos.change_time;
139 file->both_directory_info.size = name->st.st_size;
140 file->both_directory_info.alloc_size = name->dos.alloc_size;
141 file->both_directory_info.attrib = name->dos.attrib;
142 file->both_directory_info.ea_size = name->dos.ea_size;
143 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
144 file->both_directory_info.name.s = fname;
147 case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
148 file->id_full_directory_info.file_index = dir_index;
149 file->id_full_directory_info.create_time = name->dos.create_time;
150 file->id_full_directory_info.access_time = name->dos.access_time;
151 file->id_full_directory_info.write_time = name->dos.write_time;
152 file->id_full_directory_info.change_time = name->dos.change_time;
153 file->id_full_directory_info.size = name->st.st_size;
154 file->id_full_directory_info.alloc_size = name->dos.alloc_size;
155 file->id_full_directory_info.attrib = name->dos.attrib;
156 file->id_full_directory_info.ea_size = name->dos.ea_size;
157 file->id_full_directory_info.file_id = name->dos.file_id;
158 file->id_full_directory_info.name.s = fname;
161 case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
162 file->id_both_directory_info.file_index = dir_index;
163 file->id_both_directory_info.create_time = name->dos.create_time;
164 file->id_both_directory_info.access_time = name->dos.access_time;
165 file->id_both_directory_info.write_time = name->dos.write_time;
166 file->id_both_directory_info.change_time = name->dos.change_time;
167 file->id_both_directory_info.size = name->st.st_size;
168 file->id_both_directory_info.alloc_size = name->dos.alloc_size;
169 file->id_both_directory_info.attrib = name->dos.attrib;
170 file->id_both_directory_info.ea_size = name->dos.ea_size;
171 file->id_both_directory_info.file_id = name->dos.file_id;
172 file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
173 file->id_both_directory_info.name.s = fname;
176 case RAW_SEARCH_GENERIC:
180 return NT_STATUS_INVALID_LEVEL;
187 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
189 struct pvfs_search_state *search,
190 enum smb_search_level level,
192 void *search_private,
193 BOOL (*callback)(void *, union smb_search_data *))
195 struct pvfs_dir *dir = search->dir;
200 if (max_count == 0) {
204 while ((*reply_count) < max_count) {
205 union smb_search_data *file;
207 uint_t ofs = search->current_index;
209 name = pvfs_list_next(dir, &search->current_index);
210 if (name == NULL) break;
212 file = talloc_p(mem_ctx, union smb_search_data);
214 return NT_STATUS_NO_MEMORY;
217 status = fill_search_info(pvfs, level,
218 pvfs_list_unix_path(dir), name,
219 search, search->current_index, file);
220 if (!NT_STATUS_IS_OK(status)) {
225 if (!callback(search_private, file)) {
227 search->current_index = ofs;
235 pvfs_list_hibernate(dir);
241 list files in a directory matching a wildcard pattern - old SMBsearch interface
243 static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
244 struct smbsrv_request *req, union smb_search_first *io,
245 void *search_private,
246 BOOL (*callback)(void *, union smb_search_data *))
248 struct pvfs_dir *dir;
249 struct pvfs_state *pvfs = ntvfs->private_data;
250 struct pvfs_search_state *search;
252 uint16_t search_attrib;
255 struct pvfs_filename *name;
258 search_attrib = io->search_first.in.search_attrib;
259 pattern = io->search_first.in.pattern;
261 /* resolve the cifs name to a posix name */
262 status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
263 if (!NT_STATUS_IS_OK(status)) {
267 if (!name->has_wildcard && !name->exists) {
268 return STATUS_NO_MORE_FILES;
271 /* we initially make search a child of the request, then if we
272 need to keep it long term we steal it for the private
274 search = talloc_p(req, struct pvfs_search_state);
276 return NT_STATUS_NO_MEMORY;
279 /* do the actual directory listing */
280 status = pvfs_list_start(pvfs, name, search, &dir);
281 if (!NT_STATUS_IS_OK(status)) {
285 /* we need to give a handle back to the client so it
286 can continue a search */
287 id = idr_get_new(pvfs->idtree_search, search, UINT8_MAX);
289 return NT_STATUS_INSUFFICIENT_RESOURCES;
295 search->current_index = 0;
296 search->search_attrib = search_attrib & 0xFF;
297 search->must_attrib = (search_attrib>>8) & 0xFF;
299 talloc_set_destructor(search, pvfs_search_destructor);
301 status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.level,
302 &reply_count, search_private, callback);
303 if (!NT_STATUS_IS_OK(status)) {
307 io->search_first.out.count = reply_count;
309 /* not matching any entries is an error */
310 if (reply_count == 0) {
311 return STATUS_NO_MORE_FILES;
314 talloc_steal(pvfs, search);
319 /* continue a old style search */
320 static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
321 struct smbsrv_request *req, union smb_search_next *io,
322 void *search_private,
323 BOOL (*callback)(void *, union smb_search_data *))
325 struct pvfs_state *pvfs = ntvfs->private_data;
326 struct pvfs_search_state *search;
327 struct pvfs_dir *dir;
328 uint_t reply_count, max_count;
332 handle = io->search_next.in.id.handle;
333 max_count = io->search_next.in.max_count;
335 search = idr_find(pvfs->idtree_search, handle);
336 if (search == NULL) {
337 /* we didn't find the search handle */
338 return NT_STATUS_INVALID_HANDLE;
341 search->current_index = io->search_next.in.id.server_cookie;
344 status = pvfs_list_wakeup(dir, &search->current_index);
345 if (!NT_STATUS_IS_OK(status)) {
349 status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
350 &reply_count, search_private, callback);
351 if (!NT_STATUS_IS_OK(status)) {
355 io->search_next.out.count = reply_count;
357 /* not matching any entries means end of search */
358 if (reply_count == 0) {
366 list files in a directory matching a wildcard pattern
368 NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
369 struct smbsrv_request *req, union smb_search_first *io,
370 void *search_private,
371 BOOL (*callback)(void *, union smb_search_data *))
373 struct pvfs_dir *dir;
374 struct pvfs_state *pvfs = ntvfs->private_data;
375 struct pvfs_search_state *search;
377 uint16_t search_attrib, max_count;
380 struct pvfs_filename *name;
383 if (io->generic.level >= RAW_SEARCH_SEARCH) {
384 return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
387 search_attrib = io->t2ffirst.in.search_attrib;
388 pattern = io->t2ffirst.in.pattern;
389 max_count = io->t2ffirst.in.max_count;
391 /* resolve the cifs name to a posix name */
392 status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
393 if (!NT_STATUS_IS_OK(status)) {
397 if (!name->has_wildcard && !name->exists) {
398 return NT_STATUS_NO_SUCH_FILE;
401 /* we initially make search a child of the request, then if we
402 need to keep it long term we steal it for the private
404 search = talloc_p(req, struct pvfs_search_state);
406 return NT_STATUS_NO_MEMORY;
409 /* do the actual directory listing */
410 status = pvfs_list_start(pvfs, name, search, &dir);
411 if (!NT_STATUS_IS_OK(status)) {
415 id = idr_get_new(pvfs->idtree_search, search, UINT16_MAX);
417 return NT_STATUS_INSUFFICIENT_RESOURCES;
423 search->current_index = 0;
424 search->search_attrib = search_attrib;
425 search->must_attrib = 0;
427 talloc_set_destructor(search, pvfs_search_destructor);
429 status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
430 &reply_count, search_private, callback);
431 if (!NT_STATUS_IS_OK(status)) {
435 /* not matching any entries is an error */
436 if (reply_count == 0) {
437 return NT_STATUS_NO_SUCH_FILE;
440 io->t2ffirst.out.count = reply_count;
441 io->t2ffirst.out.handle = search->handle;
442 io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
444 /* work out if we are going to keep the search state
445 and allow for a search continue */
446 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
447 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
448 io->t2ffirst.out.end_of_search)) {
451 talloc_steal(pvfs, search);
457 /* continue a search */
458 NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
459 struct smbsrv_request *req, union smb_search_next *io,
460 void *search_private,
461 BOOL (*callback)(void *, union smb_search_data *))
463 struct pvfs_state *pvfs = ntvfs->private_data;
464 struct pvfs_search_state *search;
465 struct pvfs_dir *dir;
470 if (io->generic.level >= RAW_SEARCH_SEARCH) {
471 return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
474 handle = io->t2fnext.in.handle;
476 search = idr_find(pvfs->idtree_search, handle);
477 if (search == NULL) {
478 /* we didn't find the search handle */
479 return NT_STATUS_INVALID_HANDLE;
484 /* work out what type of continuation is being used */
485 if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
486 status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
487 if (!NT_STATUS_IS_OK(status)) {
490 } else if (io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) {
491 /* plain continue - nothing to do */
493 search->current_index = io->t2fnext.in.resume_key;
496 status = pvfs_list_wakeup(dir, &search->current_index);
497 if (!NT_STATUS_IS_OK(status)) {
501 status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.level,
502 &reply_count, search_private, callback);
503 if (!NT_STATUS_IS_OK(status)) {
507 /* not matching any entries is an error */
508 if (reply_count == 0) {
509 return NT_STATUS_NO_MORE_ENTRIES;
512 io->t2fnext.out.count = reply_count;
513 io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
515 /* work out if we are going to keep the search state */
516 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
517 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
518 io->t2fnext.out.end_of_search)) {
526 NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
527 struct smbsrv_request *req, union smb_search_close *io)
529 struct pvfs_state *pvfs = ntvfs->private_data;
530 struct pvfs_search_state *search;
533 if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
534 handle = io->fclose.in.id.handle;
536 handle = io->findclose.in.handle;
539 search = idr_find(pvfs->idtree_search, handle);
540 if (search == NULL) {
541 /* we didn't find the search handle */
542 return NT_STATUS_INVALID_HANDLE;