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