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