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