r2520: - finished implementing the server side of the old style search requests
[gd/samba-autobuild/.git] / source4 / 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                                  struct pvfs_search_state *search,
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         if (!pvfs_match_attrib(pvfs, name, search->search_attrib)) {
46                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
47         }
48
49         switch (level) {
50         case RAW_SEARCH_SEARCH:
51         case RAW_SEARCH_FFIRST:
52         case RAW_SEARCH_FUNIQUE:
53                 file->search.attrib           = name->dos.attrib;
54                 file->search.write_time       = nt_time_to_unix(name->dos.write_time);
55                 file->search.size             = name->st.st_size;
56                 file->search.name             = fname;
57                 file->search.id.reserved      = 8;
58                 memset(file->search.id.name, ' ', sizeof(file->search.id.name));
59                 memcpy(file->search.id.name, fname, MIN(strlen(fname)+1, sizeof(file->search.id.name)));
60                 file->search.id.handle        = search->handle;
61                 file->search.id.server_cookie = dir_index+1;
62                 file->search.id.client_cookie = 0;
63                 return NT_STATUS_OK;
64
65         case RAW_SEARCH_STANDARD:
66                 file->standard.resume_key   = dir_index+1;
67                 file->standard.create_time  = nt_time_to_unix(name->dos.create_time);
68                 file->standard.access_time  = nt_time_to_unix(name->dos.access_time);
69                 file->standard.write_time   = nt_time_to_unix(name->dos.write_time);
70                 file->standard.size         = name->st.st_size;
71                 file->standard.alloc_size   = name->dos.alloc_size;
72                 file->standard.attrib       = name->dos.attrib;
73                 file->standard.name.s       = fname;
74                 return NT_STATUS_OK;
75
76         case RAW_SEARCH_EA_SIZE:
77                 file->ea_size.resume_key   = dir_index+1;
78                 file->ea_size.create_time  = nt_time_to_unix(name->dos.create_time);
79                 file->ea_size.access_time  = nt_time_to_unix(name->dos.access_time);
80                 file->ea_size.write_time   = nt_time_to_unix(name->dos.write_time);
81                 file->ea_size.size         = name->st.st_size;
82                 file->ea_size.alloc_size   = name->dos.alloc_size;
83                 file->ea_size.attrib       = name->dos.attrib;
84                 file->ea_size.ea_size      = name->dos.ea_size;
85                 file->ea_size.name.s       = fname;
86                 return NT_STATUS_OK;
87
88         case RAW_SEARCH_DIRECTORY_INFO:
89                 file->directory_info.file_index   = dir_index+1;
90                 file->directory_info.create_time  = name->dos.create_time;
91                 file->directory_info.access_time  = name->dos.access_time;
92                 file->directory_info.write_time   = name->dos.write_time;
93                 file->directory_info.change_time  = name->dos.change_time;
94                 file->directory_info.size         = name->st.st_size;
95                 file->directory_info.alloc_size   = name->dos.alloc_size;
96                 file->directory_info.attrib       = name->dos.attrib;
97                 file->directory_info.name.s       = fname;
98                 return NT_STATUS_OK;
99
100         case RAW_SEARCH_FULL_DIRECTORY_INFO:
101                 file->full_directory_info.file_index   = dir_index+1;
102                 file->full_directory_info.create_time  = name->dos.create_time;
103                 file->full_directory_info.access_time  = name->dos.access_time;
104                 file->full_directory_info.write_time   = name->dos.write_time;
105                 file->full_directory_info.change_time  = name->dos.change_time;
106                 file->full_directory_info.size         = name->st.st_size;
107                 file->full_directory_info.alloc_size   = name->dos.alloc_size;
108                 file->full_directory_info.attrib       = name->dos.attrib;
109                 file->full_directory_info.ea_size      = name->dos.ea_size;
110                 file->full_directory_info.name.s       = fname;
111                 return NT_STATUS_OK;
112
113         case RAW_SEARCH_NAME_INFO:
114                 file->name_info.file_index   = dir_index;
115                 file->name_info.name.s       = fname;
116                 return NT_STATUS_OK;
117
118         case RAW_SEARCH_BOTH_DIRECTORY_INFO:
119                 file->both_directory_info.file_index   = dir_index+1;
120                 file->both_directory_info.create_time  = name->dos.create_time;
121                 file->both_directory_info.access_time  = name->dos.access_time;
122                 file->both_directory_info.write_time   = name->dos.write_time;
123                 file->both_directory_info.change_time  = name->dos.change_time;
124                 file->both_directory_info.size         = name->st.st_size;
125                 file->both_directory_info.alloc_size   = name->dos.alloc_size;
126                 file->both_directory_info.attrib       = name->dos.attrib;
127                 file->both_directory_info.ea_size      = name->dos.ea_size;
128                 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, name);
129                 file->both_directory_info.name.s       = fname;
130                 return NT_STATUS_OK;
131
132         case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
133                 file->id_full_directory_info.file_index   = dir_index+1;
134                 file->id_full_directory_info.create_time  = name->dos.create_time;
135                 file->id_full_directory_info.access_time  = name->dos.access_time;
136                 file->id_full_directory_info.write_time   = name->dos.write_time;
137                 file->id_full_directory_info.change_time  = name->dos.change_time;
138                 file->id_full_directory_info.size         = name->st.st_size;
139                 file->id_full_directory_info.alloc_size   = name->dos.alloc_size;
140                 file->id_full_directory_info.attrib       = name->dos.attrib;
141                 file->id_full_directory_info.ea_size      = name->dos.ea_size;
142                 file->id_full_directory_info.file_id      = name->dos.file_id;
143                 file->id_full_directory_info.name.s       = fname;
144                 return NT_STATUS_OK;
145
146         case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
147                 file->id_both_directory_info.file_index   = dir_index+1;
148                 file->id_both_directory_info.create_time  = name->dos.create_time;
149                 file->id_both_directory_info.access_time  = name->dos.access_time;
150                 file->id_both_directory_info.write_time   = name->dos.write_time;
151                 file->id_both_directory_info.change_time  = name->dos.change_time;
152                 file->id_both_directory_info.size         = name->st.st_size;
153                 file->id_both_directory_info.alloc_size   = name->dos.alloc_size;
154                 file->id_both_directory_info.attrib       = name->dos.attrib;
155                 file->id_both_directory_info.ea_size      = name->dos.ea_size;
156                 file->id_both_directory_info.file_id      = name->dos.file_id;
157                 file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, name);
158                 file->id_both_directory_info.name.s       = fname;
159                 return NT_STATUS_OK;
160
161         case RAW_SEARCH_GENERIC:
162                 break;
163         }
164
165         return NT_STATUS_INVALID_LEVEL;
166 }
167
168
169 /*
170   the search fill loop
171 */
172 static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, 
173                                  uint_t max_count, 
174                                  struct pvfs_search_state *search,
175                                  enum smb_search_level level,
176                                  uint_t *reply_count,
177                                  void *search_private, 
178                                  BOOL (*callback)(void *, union smb_search_data *))
179 {
180         int i;
181         struct pvfs_dir *dir = search->dir;
182         NTSTATUS status;
183
184         *reply_count = 0;
185
186         for (i = search->current_index; i < dir->count;i++) {
187                 union smb_search_data *file;
188
189                 file = talloc_p(mem_ctx, union smb_search_data);
190                 if (!file) {
191                         return NT_STATUS_NO_MEMORY;
192                 }
193
194                 status = fill_search_info(pvfs, level, dir->unix_path, dir->names[i], 
195                                           search, i, file);
196                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
197                         talloc_free(file);
198                         continue;
199                 }
200
201                 if (!NT_STATUS_IS_OK(status)) {
202                         talloc_free(file);
203                         search->current_index = i;
204                         return status;
205                 }
206
207                 if (!callback(search_private, file)) {
208                         talloc_free(file);
209                         break;
210                 }
211                 (*reply_count)++;
212                 talloc_free(file);
213
214                 /* note that this deliberately allows a reply_count of
215                    1 for a max_count of 0. w2k3 allows this too. */
216                 if (*reply_count >= max_count) break;
217         }
218
219         search->current_index = i;
220
221         return NT_STATUS_OK;
222 }
223
224 /*
225   return the next available search handle
226 */
227 static NTSTATUS pvfs_next_search_handle(struct pvfs_state *pvfs, uint16_t *handle, 
228                                         uint_t max_handles)
229 {
230         struct pvfs_search_state *search;
231
232         if (pvfs->search.num_active_searches >= max_handles) {
233                 return NT_STATUS_INSUFFICIENT_RESOURCES;
234         }
235
236         (*handle) = (pvfs->search.next_search_handle) & (max_handles-1);
237 again:
238         for (search=pvfs->search.open_searches;search;search=search->next) {
239                 if (*handle == search->handle) {
240                         *handle = ((*handle)+1) & (max_handles-1);
241                         goto again;
242                 } 
243         }
244         pvfs->search.next_search_handle = ((*handle)+1) & (max_handles-1);
245
246         return NT_STATUS_OK;
247 }
248
249
250 /* 
251    list files in a directory matching a wildcard pattern - old SMBsearch interface
252 */
253 static NTSTATUS pvfs_search_first_old(struct smbsrv_request *req, union smb_search_first *io, 
254                                       void *search_private, 
255                                       BOOL (*callback)(void *, union smb_search_data *))
256 {
257         struct pvfs_dir *dir;
258         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
259         struct pvfs_search_state *search;
260         uint_t reply_count;
261         uint16_t search_attrib;
262         const char *pattern;
263         NTSTATUS status;
264         struct pvfs_filename *name;
265
266         search_attrib = io->search_first.in.search_attrib;
267         pattern       = io->search_first.in.pattern;
268
269         /* resolve the cifs name to a posix name */
270         status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
271         if (!NT_STATUS_IS_OK(status)) {
272                 return status;
273         }
274
275         if (!name->has_wildcard && !name->exists) {
276                 return STATUS_NO_MORE_FILES;
277         }
278
279         /* we initially make search a child of the request, then if we
280            need to keep it long term we steal it for the private
281            structure */
282         search = talloc_p(req, struct pvfs_search_state);
283         if (!search) {
284                 return NT_STATUS_NO_MEMORY;
285         }
286
287         dir = talloc_p(search, struct pvfs_dir);
288         if (!dir) {
289                 return NT_STATUS_NO_MEMORY;
290         }
291
292         /* do the actual directory listing */
293         status = pvfs_list(pvfs, name, dir);
294         if (!NT_STATUS_IS_OK(status)) {
295                 return status;
296         }
297
298         /* we need to give a handle back to the client so it
299            can continue a search */
300         status = pvfs_next_search_handle(pvfs, &search->handle, 0x100);
301         if (!NT_STATUS_IS_OK(status)) {
302                 return status;
303         }
304         
305         search->dir = dir;
306         search->current_index = 0;
307         search->search_attrib = search_attrib;
308
309         status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.level,
310                                   &reply_count, search_private, callback);
311         if (!NT_STATUS_IS_OK(status)) {
312                 return status;
313         }
314
315         io->search_first.out.count = reply_count;
316
317         /* not matching any entries is an error */
318         if (reply_count == 0) {
319                 return STATUS_NO_MORE_FILES;
320         }
321
322         pvfs->search.num_active_searches++;
323         talloc_steal(pvfs, search);
324         DLIST_ADD(pvfs->search.open_searches, search);
325
326         return NT_STATUS_OK;
327 }
328
329 /* continue a old style search */
330 static NTSTATUS pvfs_search_next_old(struct smbsrv_request *req, union smb_search_next *io, 
331                                      void *search_private, 
332                                      BOOL (*callback)(void *, union smb_search_data *))
333 {
334         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
335         struct pvfs_search_state *search;
336         struct pvfs_dir *dir;
337         uint_t reply_count, max_count;
338         uint16_t handle;
339         NTSTATUS status;
340
341         handle    = io->search_next.in.id.handle;
342         max_count = io->search_next.in.max_count;
343
344         for (search=pvfs->search.open_searches; search; search = search->next) {
345                 if (search->handle == handle) break;
346         }
347         
348         if (!search) {
349                 /* we didn't find the search handle */
350                 return NT_STATUS_INVALID_HANDLE;
351         }
352
353         search->current_index = io->search_next.in.id.server_cookie;
354
355         dir = search->dir;
356
357         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
358                                   &reply_count, search_private, callback);
359         if (!NT_STATUS_IS_OK(status)) {
360                 return status;
361         }
362
363         io->search_next.out.count = reply_count;
364
365         /* not matching any entries means end of search */
366         if (reply_count == 0) {
367                 DLIST_REMOVE(pvfs->search.open_searches, search);
368                 talloc_free(search);
369         }
370
371         return NT_STATUS_OK;
372 }
373
374 /* 
375    list files in a directory matching a wildcard pattern
376 */
377 NTSTATUS pvfs_search_first(struct smbsrv_request *req, union smb_search_first *io, 
378                            void *search_private, 
379                            BOOL (*callback)(void *, union smb_search_data *))
380 {
381         struct pvfs_dir *dir;
382         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
383         struct pvfs_search_state *search;
384         uint_t reply_count;
385         uint16_t search_attrib, max_count;
386         const char *pattern;
387         NTSTATUS status;
388         struct pvfs_filename *name;
389
390         if (io->generic.level >= RAW_SEARCH_SEARCH) {
391                 return pvfs_search_first_old(req, io, search_private, callback);
392         }
393
394         search_attrib = io->t2ffirst.in.search_attrib;
395         pattern       = io->t2ffirst.in.pattern;
396         max_count     = io->t2ffirst.in.max_count;
397
398         /* resolve the cifs name to a posix name */
399         status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
400         if (!NT_STATUS_IS_OK(status)) {
401                 return status;
402         }
403
404         if (!name->has_wildcard && !name->exists) {
405                 return NT_STATUS_NO_SUCH_FILE;
406         }
407
408         /* we initially make search a child of the request, then if we
409            need to keep it long term we steal it for the private
410            structure */
411         search = talloc_p(req, struct pvfs_search_state);
412         if (!search) {
413                 return NT_STATUS_NO_MEMORY;
414         }
415
416         dir = talloc_p(search, struct pvfs_dir);
417         if (!dir) {
418                 return NT_STATUS_NO_MEMORY;
419         }
420
421         /* do the actual directory listing */
422         status = pvfs_list(pvfs, name, dir);
423         if (!NT_STATUS_IS_OK(status)) {
424                 return status;
425         }
426
427         /* we need to give a handle back to the client so it
428            can continue a search */
429         status = pvfs_next_search_handle(pvfs, &search->handle, 0x10000);
430         if (!NT_STATUS_IS_OK(status)) {
431                 return status;
432         }
433         
434         search->dir = dir;
435         search->current_index = 0;
436         search->search_attrib = search_attrib;
437
438         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
439                                   &reply_count, search_private, callback);
440         if (!NT_STATUS_IS_OK(status)) {
441                 return status;
442         }
443
444         /* not matching any entries is an error */
445         if (reply_count == 0) {
446                 return NT_STATUS_NO_SUCH_FILE;
447         }
448
449         io->t2ffirst.out.count = reply_count;
450         io->t2ffirst.out.handle = search->handle;
451         io->t2ffirst.out.end_of_search = (search->current_index == dir->count) ? 1 : 0;
452
453         /* work out if we are going to keep the search state
454            and allow for a search continue */
455         if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
456             ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
457              io->t2ffirst.out.end_of_search)) {
458                 talloc_free(search);
459         } else {
460                 pvfs->search.num_active_searches++;
461                 talloc_steal(pvfs, search);
462                 DLIST_ADD(pvfs->search.open_searches, search);
463         }
464
465         return NT_STATUS_OK;
466 }
467
468 /* continue a search */
469 NTSTATUS pvfs_search_next(struct smbsrv_request *req, union smb_search_next *io, 
470                           void *search_private, 
471                           BOOL (*callback)(void *, union smb_search_data *))
472 {
473         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
474         struct pvfs_search_state *search;
475         struct pvfs_dir *dir;
476         uint_t reply_count;
477         uint16_t handle;
478         NTSTATUS status;
479         int i;
480
481         if (io->generic.level >= RAW_SEARCH_SEARCH) {
482                 return pvfs_search_next_old(req, io, search_private, callback);
483         }
484
485         handle = io->t2fnext.in.handle;
486
487         for (search=pvfs->search.open_searches; search; search = search->next) {
488                 if (search->handle == handle) break;
489         }
490         
491         if (!search) {
492                 /* we didn't find the search handle */
493                 return NT_STATUS_INVALID_HANDLE;
494         }
495
496         dir = search->dir;
497
498         /* work out what type of continuation is being used */
499         if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
500                 /* look backwards first */
501                 for (i=search->current_index; i > 0; i--) {
502                         if (strcmp(io->t2fnext.in.last_name, dir->names[i-1]) == 0) {
503                                 search->current_index = i;
504                                 goto found;
505                         }
506                 }
507
508                 /* then look forwards */
509                 for (i=search->current_index+1; i <= dir->count; i++) {
510                         if (strcmp(io->t2fnext.in.last_name, dir->names[i-1]) == 0) {
511                                 search->current_index = i;
512                                 goto found;
513                         }
514                 }
515         } else if (io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) {
516                 /* plain continue - nothing to do */
517         } else {
518                 search->current_index = io->t2fnext.in.resume_key;
519         }
520
521 found:  
522         status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.level,
523                                   &reply_count, search_private, callback);
524         if (!NT_STATUS_IS_OK(status)) {
525                 return status;
526         }
527
528         /* not matching any entries is an error */
529         if (reply_count == 0) {
530                 return NT_STATUS_NO_MORE_ENTRIES;
531         }
532
533         io->t2fnext.out.count = reply_count;
534         io->t2fnext.out.end_of_search = (search->current_index == dir->count) ? 1 : 0;
535
536         /* work out if we are going to keep the search state */
537         if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
538             ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
539              io->t2fnext.out.end_of_search)) {
540                 DLIST_REMOVE(pvfs->search.open_searches, search);
541                 talloc_free(search);
542         }
543
544         return NT_STATUS_OK;
545 }
546
547 /* close a search */
548 NTSTATUS pvfs_search_close(struct smbsrv_request *req, union smb_search_close *io)
549 {
550         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
551         struct pvfs_search_state *search;
552         uint16_t handle;
553
554         if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
555                 handle = io->fclose.in.id.handle;
556         } else {
557                 handle = io->findclose.in.handle;
558         }
559
560         for (search=pvfs->search.open_searches; search; search = search->next) {
561                 if (search->handle == handle) break;
562         }
563         
564         if (!search) {
565                 /* we didn't find the search handle */
566                 return NT_STATUS_INVALID_HANDLE;
567         }
568
569         DLIST_REMOVE(pvfs->search.open_searches, search);
570         talloc_free(search);
571
572         return NT_STATUS_OK;
573 }
574