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