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"
27 fill in a single search result for a given info level
29 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
30 enum smb_search_level level,
31 const char *unix_path,
33 uint16_t search_attrib,
35 union smb_search_data *file)
37 struct pvfs_filename *name;
40 status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
41 if (!NT_STATUS_IS_OK(status)) {
47 case RAW_SEARCH_BOTH_DIRECTORY_INFO:
48 file->both_directory_info.file_index = dir_index;
49 file->both_directory_info.create_time = name->dos.create_time;
50 file->both_directory_info.access_time = name->dos.access_time;
51 file->both_directory_info.write_time = name->dos.write_time;
52 file->both_directory_info.change_time = name->dos.change_time;
53 file->both_directory_info.size = name->st.st_size;
54 file->both_directory_info.alloc_size = name->dos.alloc_size;
55 file->both_directory_info.attrib = name->dos.attrib;
56 file->both_directory_info.ea_size = name->dos.ea_size;
57 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, name);
58 file->both_directory_info.name.s = fname;
66 return the next available search handle
68 static NTSTATUS pvfs_next_search_handle(struct pvfs_state *pvfs, uint16_t *handle)
70 struct pvfs_search_state *search;
72 if (pvfs->search.num_active_searches >= 0x10000) {
73 return NT_STATUS_INSUFFICIENT_RESOURCES;
76 (*handle) = pvfs->search.next_search_handle;
77 for (search=pvfs->search.open_searches;search;search=search->next) {
78 if (*handle == search->handle) {
79 *handle = ((*handle)+1) & 0xFFFF;
83 pvfs->search.next_search_handle = ((*handle)+1) & 0xFFFF;
89 list files in a directory matching a wildcard pattern
91 NTSTATUS pvfs_search_first(struct smbsrv_request *req, union smb_search_first *io,
93 BOOL (*callback)(void *, union smb_search_data *))
96 struct pvfs_state *pvfs = req->tcon->ntvfs_private;
97 struct pvfs_search_state *search;
98 uint16_t max_count, reply_count;
99 uint16_t search_attrib;
103 struct pvfs_filename *name;
105 if (io->generic.level == RAW_SEARCH_SEARCH) {
106 max_count = io->search_first.in.max_count;
107 search_attrib = io->search_first.in.search_attrib;
108 pattern = io->search_first.in.pattern;
110 max_count = io->t2ffirst.in.max_count;
111 search_attrib = io->t2ffirst.in.search_attrib;
112 pattern = io->t2ffirst.in.pattern;
115 /* resolve the cifs name to a posix name */
116 status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
117 if (!NT_STATUS_IS_OK(status)) {
121 if (!name->has_wildcard && !name->exists) {
122 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
125 /* we initially make search a child of the request, then if we
126 need to keep it long term we steal it for the private
128 search = talloc_p(req, struct pvfs_search_state);
130 return NT_STATUS_NO_MEMORY;
133 dir = talloc_p(search, struct pvfs_dir);
135 return NT_STATUS_NO_MEMORY;
138 /* do the actual directory listing */
139 status = pvfs_list(pvfs, name, dir);
140 if (!NT_STATUS_IS_OK(status)) {
144 /* we need to give a handle back to the client so it
145 can continue a search */
146 status = pvfs_next_search_handle(pvfs, &search->handle);
147 if (!NT_STATUS_IS_OK(status)) {
152 search->current_index = 0;
153 search->search_attrib = search_attrib;
155 if (dir->count < max_count) {
156 max_count = dir->count;
159 /* note that fill_search_info() can fail, if for example a
160 file disappears during a search or we don't have sufficient
161 permissions to stat() it, or the search_attrib does not
162 match the files attribute. In that case the name is ignored
163 and the search continues. */
164 for (i=reply_count=0; i < dir->count && reply_count < max_count;i++) {
165 union smb_search_data *file;
167 file = talloc_p(req, union smb_search_data);
169 return NT_STATUS_NO_MEMORY;
172 status = fill_search_info(pvfs, io->generic.level, dir->unix_path, dir->names[i],
173 search_attrib, i, file);
174 if (NT_STATUS_IS_OK(status)) {
175 if (!callback(search_private, file)) {
183 /* not matching any entries is an error */
184 if (reply_count == 0) {
185 return NT_STATUS_NO_MORE_ENTRIES;
188 search->current_index = i;
190 if (io->generic.level == RAW_SEARCH_SEARCH) {
191 io->search_first.out.count = reply_count;
192 DEBUG(0,("TODO: handle RAW_SEARCH_SEARCH continue\n"));
194 io->t2ffirst.out.count = reply_count;
195 io->t2ffirst.out.handle = search->handle;
196 io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
197 /* work out if we are going to keep the search state
198 and allow for a search continue */
199 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
200 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
203 pvfs->search.num_active_searches++;
204 pvfs->search.next_search_handle++;
205 talloc_steal(pvfs, search);
206 DLIST_ADD(pvfs->search.open_searches, search);
213 /* continue a search */
214 NTSTATUS pvfs_search_next(struct smbsrv_request *req, union smb_search_next *io,
215 void *search_private,
216 BOOL (*callback)(void *, union smb_search_data *))
219 struct pvfs_state *pvfs = req->tcon->ntvfs_private;
220 struct search_state *search;
221 union smb_search_data file;
226 if (io->generic.level == RAW_SEARCH_SEARCH) {
227 max_count = io->search_next.in.max_count;
228 search_attrib = io->search_next.in.search_attrib;
230 handle = io->t2fnext.in.handle;
233 if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) {
234 return NT_STATUS_NOT_SUPPORTED;
237 for (search=private->search; search; search = search->next) {
238 if (search->handle == io->t2fnext.in.handle) break;
242 /* we didn't find the search handle */
243 return NT_STATUS_FOOBAR;
248 /* the client might be asking for something other than just continuing
250 if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
251 (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
252 io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
253 /* look backwards first */
254 for (i=search->current_index; i > 0; i--) {
255 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
256 search->current_index = i;
261 /* then look forwards */
262 for (i=search->current_index+1; i <= dir->count; i++) {
263 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
264 search->current_index = i;
271 max_count = search->current_index + io->t2fnext.in.max_count;
273 if (max_count > dir->count) {
274 max_count = dir->count;
277 for (i = search->current_index; i < max_count;i++) {
279 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
280 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
281 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
282 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
283 file.both_directory_info.name.s = dir->files[i].name;
284 file.both_directory_info.short_name.s = dir->files[i].name;
285 file.both_directory_info.size = dir->files[i].st.st_size;
286 file.both_directory_info.attrib = pvfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
288 if (!callback(search_private, &file)) {
293 io->t2fnext.out.count = i - search->current_index;
294 io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
296 search->current_index = i;
298 /* work out if we are going to keep the search state */
299 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
300 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
301 DLIST_REMOVE(private->search, search);
309 NTSTATUS pvfs_search_close(struct smbsrv_request *req, union smb_search_close *io)
311 return NT_STATUS_NOT_IMPLEMENTED;