r2469: complete overhaul of the old-style RAW_SEARCH_ calls (the OS/2 and
[samba.git] / source / ntvfs / posix / pvfs_search.c
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "include/includes.h"
24 #include "vfs_posix.h"
25
26 /*
27   fill in a single search result for a given info level
28 */
29 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
30                                  enum smb_search_level level,
31                                  const char *unix_path,
32                                  const char *fname, 
33                                  uint16_t search_attrib,
34                                  uint32_t dir_index,
35                                  union smb_search_data *file)
36 {
37         struct pvfs_filename *name;
38         NTSTATUS status;
39
40         status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
41         if (!NT_STATUS_IS_OK(status)) {
42                 return status;
43         }
44
45         switch (level) {
46
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;
59                 break;
60         }
61
62         return NT_STATUS_OK;
63 }
64
65 /*
66   return the next available search handle
67 */
68 static NTSTATUS pvfs_next_search_handle(struct pvfs_state *pvfs, uint16_t *handle)
69 {
70         struct pvfs_search_state *search;
71
72         if (pvfs->search.num_active_searches >= 0x10000) {
73                 return NT_STATUS_INSUFFICIENT_RESOURCES;
74         }
75
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;
80                         continue;
81                 } 
82         }
83         pvfs->search.next_search_handle = ((*handle)+1) & 0xFFFF;
84
85         return NT_STATUS_OK;
86 }
87
88 /* 
89    list files in a directory matching a wildcard pattern
90 */
91 NTSTATUS pvfs_search_first(struct smbsrv_request *req, union smb_search_first *io, 
92                            void *search_private, 
93                            BOOL (*callback)(void *, union smb_search_data *))
94 {
95         struct pvfs_dir *dir;
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;
100         const char *pattern;
101         int i;
102         NTSTATUS status;
103         struct pvfs_filename *name;
104
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;
109         } else {
110                 max_count     = io->t2ffirst.in.max_count;
111                 search_attrib = io->t2ffirst.in.search_attrib;
112                 pattern       = io->t2ffirst.in.pattern;
113         }
114
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)) {
118                 return status;
119         }
120
121         if (!name->has_wildcard && !name->exists) {
122                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
123         }
124
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
127            structure */
128         search = talloc_p(req, struct pvfs_search_state);
129         if (!search) {
130                 return NT_STATUS_NO_MEMORY;
131         }
132
133         dir = talloc_p(search, struct pvfs_dir);
134         if (!dir) {
135                 return NT_STATUS_NO_MEMORY;
136         }
137
138         /* do the actual directory listing */
139         status = pvfs_list(pvfs, name, dir);
140         if (!NT_STATUS_IS_OK(status)) {
141                 return status;
142         }
143
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)) {
148                 return status;
149         }
150         
151         search->dir = dir;
152         search->current_index = 0;
153         search->search_attrib = search_attrib;
154
155         if (dir->count < max_count) {
156                 max_count = dir->count;
157         }
158
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;
166
167                 file = talloc_p(req, union smb_search_data);
168                 if (!file) {
169                         return NT_STATUS_NO_MEMORY;
170                 }
171
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)) {
176                                 break;
177                         }
178                         reply_count++;
179                 }
180                 talloc_free(file);
181         }
182
183         /* not matching any entries is an error */
184         if (reply_count == 0) {
185                 return NT_STATUS_NO_MORE_ENTRIES;
186         }
187
188         search->current_index = i;
189
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"));
193         } else {
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))) {
201                         talloc_free(search);
202                 } else {
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);
207                 }
208         }
209
210         return NT_STATUS_OK;
211 }
212
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 *))
217 {
218 #if 0
219         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
220         struct search_state *search;
221         union smb_search_data file;
222         uint_t max_count;
223         uint16_t handle;
224         int i;
225
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;
229         } else {
230                 handle = io->t2fnext.in.handle;
231         }
232
233         if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) {
234                 return NT_STATUS_NOT_SUPPORTED;
235         }
236
237         for (search=private->search; search; search = search->next) {
238                 if (search->handle == io->t2fnext.in.handle) break;
239         }
240         
241         if (!search) {
242                 /* we didn't find the search handle */
243                 return NT_STATUS_FOOBAR;
244         }
245
246         dir = search->dir;
247
248         /* the client might be asking for something other than just continuing
249            with the search */
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;
257                                 goto found;
258                         }
259                 }
260
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;
265                                 goto found;
266                         }
267                 }
268         }
269
270 found:  
271         max_count = search->current_index + io->t2fnext.in.max_count;
272
273         if (max_count > dir->count) {
274                 max_count = dir->count;
275         }
276
277         for (i = search->current_index; i < max_count;i++) {
278                 ZERO_STRUCT(file);
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);
287
288                 if (!callback(search_private, &file)) {
289                         break;
290                 }
291         }
292
293         io->t2fnext.out.count = i - search->current_index;
294         io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
295
296         search->current_index = i;
297
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);
302                 talloc_free(search);
303         }
304 #endif
305         return NT_STATUS_OK;
306 }
307
308 /* close a search */
309 NTSTATUS pvfs_search_close(struct smbsrv_request *req, union smb_search_close *io)
310 {
311         return NT_STATUS_NOT_IMPLEMENTED;
312 }
313