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