r4411: when checking for create permissions, we need to check the parent, not the...
[samba.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 "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         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_p(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         /* we initially make search a child of the request, then if we
329            need to keep it long term we steal it for the private
330            structure */
331         search = talloc_p(req, struct pvfs_search_state);
332         if (!search) {
333                 return NT_STATUS_NO_MEMORY;
334         }
335
336         /* do the actual directory listing */
337         status = pvfs_list_start(pvfs, name, search, &dir);
338         if (!NT_STATUS_IS_OK(status)) {
339                 return status;
340         }
341
342         /* we need to give a handle back to the client so it
343            can continue a search */
344         id = idr_get_new(pvfs->idtree_search, search, MAX_OLD_SEARCHES);
345         if (id == -1) {
346                 pvfs_search_cleanup(pvfs);
347                 id = idr_get_new(pvfs->idtree_search, search, MAX_OLD_SEARCHES);
348         }
349         if (id == -1) {
350                 return NT_STATUS_INSUFFICIENT_RESOURCES;
351         }
352
353         search->pvfs = pvfs;
354         search->handle = id;
355         search->dir = dir;
356         search->current_index = 0;
357         search->search_attrib = search_attrib & 0xFF;
358         search->must_attrib = (search_attrib>>8) & 0xFF;
359         search->last_used = time(NULL);
360
361         talloc_set_destructor(search, pvfs_search_destructor);
362
363         status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.level,
364                                   &reply_count, search_private, callback);
365         if (!NT_STATUS_IS_OK(status)) {
366                 return status;
367         }
368
369         io->search_first.out.count = reply_count;
370
371         /* not matching any entries is an error */
372         if (reply_count == 0) {
373                 return STATUS_NO_MORE_FILES;
374         }
375
376         talloc_steal(pvfs, search);
377
378         return NT_STATUS_OK;
379 }
380
381 /* continue a old style search */
382 static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
383                                      struct smbsrv_request *req, union smb_search_next *io, 
384                                      void *search_private, 
385                                      BOOL (*callback)(void *, union smb_search_data *))
386 {
387         struct pvfs_state *pvfs = ntvfs->private_data;
388         struct pvfs_search_state *search;
389         struct pvfs_dir *dir;
390         uint_t reply_count, max_count;
391         uint16_t handle;
392         NTSTATUS status;
393
394         handle    = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
395         max_count = io->search_next.in.max_count;
396
397         search = idr_find(pvfs->idtree_search, handle);
398         if (search == NULL) {
399                 /* we didn't find the search handle */
400                 return NT_STATUS_INVALID_HANDLE;
401         }
402
403         search->current_index = io->search_next.in.id.server_cookie;
404         search->last_used = time(NULL);
405         dir = search->dir;
406
407         status = pvfs_list_wakeup(dir, &search->current_index);
408         if (!NT_STATUS_IS_OK(status)) {
409                 return status;
410         }
411
412         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
413                                   &reply_count, search_private, callback);
414         if (!NT_STATUS_IS_OK(status)) {
415                 return status;
416         }
417
418         io->search_next.out.count = reply_count;
419
420         /* not matching any entries means end of search */
421         if (reply_count == 0) {
422                 talloc_free(search);
423         }
424
425         return NT_STATUS_OK;
426 }
427
428 /* 
429    list files in a directory matching a wildcard pattern
430 */
431 NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
432                            struct smbsrv_request *req, union smb_search_first *io, 
433                            void *search_private, 
434                            BOOL (*callback)(void *, union smb_search_data *))
435 {
436         struct pvfs_dir *dir;
437         struct pvfs_state *pvfs = ntvfs->private_data;
438         struct pvfs_search_state *search;
439         uint_t reply_count;
440         uint16_t search_attrib, max_count;
441         const char *pattern;
442         NTSTATUS status;
443         struct pvfs_filename *name;
444         int id;
445
446         if (io->generic.level >= RAW_SEARCH_SEARCH) {
447                 return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
448         }
449
450         search_attrib = io->t2ffirst.in.search_attrib;
451         pattern       = io->t2ffirst.in.pattern;
452         max_count     = io->t2ffirst.in.max_count;
453
454         /* resolve the cifs name to a posix name */
455         status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
456         if (!NT_STATUS_IS_OK(status)) {
457                 return status;
458         }
459
460         if (!name->has_wildcard && !name->exists) {
461                 return NT_STATUS_NO_SUCH_FILE;
462         }
463
464         /* we initially make search a child of the request, then if we
465            need to keep it long term we steal it for the private
466            structure */
467         search = talloc_p(req, struct pvfs_search_state);
468         if (!search) {
469                 return NT_STATUS_NO_MEMORY;
470         }
471
472         /* do the actual directory listing */
473         status = pvfs_list_start(pvfs, name, search, &dir);
474         if (!NT_STATUS_IS_OK(status)) {
475                 return status;
476         }
477
478         id = idr_get_new(pvfs->idtree_search, search, UINT16_MAX);
479         if (id == -1) {
480                 return NT_STATUS_INSUFFICIENT_RESOURCES;
481         }
482
483         search->pvfs = pvfs;
484         search->handle = id;
485         search->dir = dir;
486         search->current_index = 0;
487         search->search_attrib = search_attrib;
488         search->must_attrib = 0;
489         search->last_used = 0;
490         search->num_ea_names = io->t2ffirst.in.num_names;
491         search->ea_names = io->t2ffirst.in.ea_names;
492
493         talloc_set_destructor(search, pvfs_search_destructor);
494
495         status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
496                                   &reply_count, search_private, callback);
497         if (!NT_STATUS_IS_OK(status)) {
498                 return status;
499         }
500
501         /* not matching any entries is an error */
502         if (reply_count == 0) {
503                 return NT_STATUS_NO_SUCH_FILE;
504         }
505
506         io->t2ffirst.out.count = reply_count;
507         io->t2ffirst.out.handle = search->handle;
508         io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
509
510         /* work out if we are going to keep the search state
511            and allow for a search continue */
512         if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
513             ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
514              io->t2ffirst.out.end_of_search)) {
515                 talloc_free(search);
516         } else {
517                 talloc_steal(pvfs, search);
518         }
519
520         return NT_STATUS_OK;
521 }
522
523 /* continue a search */
524 NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
525                           struct smbsrv_request *req, union smb_search_next *io, 
526                           void *search_private, 
527                           BOOL (*callback)(void *, union smb_search_data *))
528 {
529         struct pvfs_state *pvfs = ntvfs->private_data;
530         struct pvfs_search_state *search;
531         struct pvfs_dir *dir;
532         uint_t reply_count;
533         uint16_t handle;
534         NTSTATUS status;
535
536         if (io->generic.level >= RAW_SEARCH_SEARCH) {
537                 return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
538         }
539
540         handle = io->t2fnext.in.handle;
541
542         search = idr_find(pvfs->idtree_search, handle);
543         if (search == NULL) {
544                 /* we didn't find the search handle */
545                 return NT_STATUS_INVALID_HANDLE;
546         }
547
548         dir = search->dir;
549
550         /* work out what type of continuation is being used */
551         if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
552                 status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
553                 if (!NT_STATUS_IS_OK(status)) {
554                         if (io->t2fnext.in.resume_key) {
555                                 search->current_index = io->t2fnext.in.resume_key;
556                         } else {
557                                 return status;
558                         }
559                 }
560         } else if (io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) {
561                 /* plain continue - nothing to do */
562         } else {
563                 search->current_index = io->t2fnext.in.resume_key;
564         }
565
566         status = pvfs_list_wakeup(dir, &search->current_index);
567         if (!NT_STATUS_IS_OK(status)) {
568                 return status;
569         }
570
571         search->num_ea_names = io->t2fnext.in.num_names;
572         search->ea_names = io->t2fnext.in.ea_names;
573
574         status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.level,
575                                   &reply_count, search_private, callback);
576         if (!NT_STATUS_IS_OK(status)) {
577                 return status;
578         }
579
580         io->t2fnext.out.count = reply_count;
581         io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
582
583         /* work out if we are going to keep the search state */
584         if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
585             ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
586              io->t2fnext.out.end_of_search)) {
587                 talloc_free(search);
588         }
589
590         return NT_STATUS_OK;
591 }
592
593 /* close a search */
594 NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
595                            struct smbsrv_request *req, union smb_search_close *io)
596 {
597         struct pvfs_state *pvfs = ntvfs->private_data;
598         struct pvfs_search_state *search;
599         uint16_t handle;
600
601         if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
602                 handle = io->fclose.in.id.handle;
603         } else {
604                 handle = io->findclose.in.handle;
605         }
606
607         search = idr_find(pvfs->idtree_search, handle);
608         if (search == NULL) {
609                 /* we didn't find the search handle */
610                 return NT_STATUS_INVALID_HANDLE;
611         }
612
613         talloc_free(search);
614
615         return NT_STATUS_OK;
616 }
617