r2404: the first large lump of posix vfs stuff.
[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 "include/includes.h"
24 #include "vfs_posix.h"
25
26 /*
27   fill in a single search result for a given info level
28 */
29 static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
30                                  enum smb_search_level level,
31                                  const char *unix_path,
32                                  const char *name, 
33                                  uint16_t search_attrib,
34                                  uint32_t dir_index,
35                                  union smb_search_data *file)
36 {
37         struct pvfs_file_info *finfo;
38         NTSTATUS status;
39
40         finfo = talloc_p((TALLOC_CTX *)file, struct pvfs_file_info);
41         if (!finfo) {
42                 return NT_STATUS_NO_MEMORY;
43         }
44
45         status = pvfs_relative_file_info_cs(pvfs, unix_path, name, finfo);
46         if (!NT_STATUS_IS_OK(status)) {
47                 talloc_free(finfo);
48                 return status;
49         }
50         
51         switch (level) {
52
53         case RAW_SEARCH_BOTH_DIRECTORY_INFO:
54                 file->both_directory_info.file_index   = dir_index;
55                 file->both_directory_info.create_time  = finfo->create_time;
56                 file->both_directory_info.access_time  = finfo->access_time;
57                 file->both_directory_info.write_time   = finfo->write_time;
58                 file->both_directory_info.change_time  = finfo->change_time;
59                 file->both_directory_info.size         = finfo->size;
60                 file->both_directory_info.alloc_size   = finfo->alloc_size;
61                 file->both_directory_info.attrib       = finfo->attrib;
62                 file->both_directory_info.ea_size      = finfo->ea_size;
63                 file->both_directory_info.short_name.s = pvfs_short_name(pvfs, (TALLOC_CTX *)file, 
64                                                                          unix_path, name);
65                 file->both_directory_info.name.s       = name;
66                 break;
67         }
68
69         talloc_free(finfo);
70
71         return NT_STATUS_OK;
72 }
73
74 /*
75   return the next available search handle
76 */
77 static NTSTATUS pvfs_next_search_handle(struct pvfs_state *pvfs, uint16_t *handle)
78 {
79         struct pvfs_search_state *search;
80
81         if (pvfs->search.num_active_searches >= 0x10000) {
82                 return NT_STATUS_INSUFFICIENT_RESOURCES;
83         }
84
85         (*handle) = pvfs->search.next_search_handle;
86         for (search=pvfs->search.open_searches;search;search=search->next) {
87                 if (*handle == search->handle) {
88                         *handle = ((*handle)+1) & 0xFFFF;
89                         continue;
90                 } 
91         }
92         pvfs->search.next_search_handle = ((*handle)+1) & 0xFFFF;
93
94         return NT_STATUS_OK;
95 }
96
97 /* 
98    list files in a directory matching a wildcard pattern
99 */
100 NTSTATUS pvfs_search_first(struct smbsrv_request *req, union smb_search_first *io, 
101                            void *search_private, 
102                            BOOL (*callback)(void *, union smb_search_data *))
103 {
104         struct pvfs_dir *dir;
105         struct pvfs_state *pvfs = req->tcon->ntvfs_private;
106         struct pvfs_search_state *search;
107         union smb_search_data *file;
108         uint16_t max_count, reply_count;
109         uint16_t search_attrib;
110         const char *pattern;
111         int i;
112         NTSTATUS status;
113         struct pvfs_filename *name;
114
115         switch (io->generic.level) {
116         case RAW_SEARCH_SEARCH:
117                 max_count     = io->search_first.in.max_count;
118                 search_attrib = io->search_first.in.search_attrib;
119                 pattern       = io->search_first.in.pattern;
120                 break;
121
122         case RAW_SEARCH_STANDARD:
123         case RAW_SEARCH_EA_SIZE:
124         case RAW_SEARCH_DIRECTORY_INFO:
125         case RAW_SEARCH_FULL_DIRECTORY_INFO:
126         case RAW_SEARCH_NAME_INFO:
127         case RAW_SEARCH_BOTH_DIRECTORY_INFO:
128         case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
129         case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
130         case RAW_SEARCH_UNIX_INFO:
131                 max_count     = io->t2ffirst.in.max_count;
132                 search_attrib = io->t2ffirst.in.search_attrib;
133                 pattern       = io->t2ffirst.in.pattern;
134                 break;
135
136         case RAW_SEARCH_FCLOSE:
137         case RAW_SEARCH_GENERIC:
138                 DEBUG(0,("WARNING: Invalid search class %d in pvfs_search_first\n", io->generic.level));
139                 return NT_STATUS_INVALID_INFO_CLASS;
140         }
141
142         /* resolve the cifs name to a posix name */
143         status = pvfs_resolve_name(pvfs, req, pattern, 0, &name);
144         if (!NT_STATUS_IS_OK(status)) {
145                 return status;
146         }
147
148         if (!name->has_wildcard && !name->exists) {
149                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
150         }
151
152         /* we initially make search a child of the request, then if we
153            need to keep it long term we steal it for the private
154            structure */
155         search = talloc_p(req, struct pvfs_search_state);
156         if (!search) {
157                 return NT_STATUS_NO_MEMORY;
158         }
159
160         dir = talloc_p(search, struct pvfs_dir);
161         if (!dir) {
162                 return NT_STATUS_NO_MEMORY;
163         }
164
165         /* do the actual directory listing */
166         status = pvfs_list(pvfs, name, dir);
167         if (!NT_STATUS_IS_OK(status)) {
168                 return status;
169         }
170
171         /* we need to give a handle back to the client so it
172            can continue a search */
173         status = pvfs_next_search_handle(pvfs, &search->handle);
174         if (!NT_STATUS_IS_OK(status)) {
175                 return status;
176         }
177         
178         search->dir = dir;
179         search->current_index = 0;
180         search->search_attrib = search_attrib;
181
182         if (dir->count < max_count) {
183                 max_count = dir->count;
184         }
185
186         file = talloc_p(req, union smb_search_data);
187         if (!file) {
188                 return NT_STATUS_NO_MEMORY;
189         }
190
191         /* note that fill_search_info() can fail, if for example a
192            file disappears during a search or we don't have sufficient
193            permissions to stat() it, or the search_attrib does not
194            match the files attribute. In that case the name is ignored
195            and the search continues. */
196         for (i=reply_count=0; i < dir->count && reply_count < max_count;i++) {
197                 status = fill_search_info(pvfs, io->generic.level, dir->unix_path, dir->names[i], 
198                                           search_attrib, i, file);
199                 if (NT_STATUS_IS_OK(status)) {
200                         if (!callback(search_private, file)) {
201                                 break;
202                         }
203                         reply_count++;
204                 }
205         }
206
207         /* not matching any entries is an error */
208         if (reply_count == 0) {
209                 return NT_STATUS_NO_MORE_ENTRIES;
210         }
211
212         search->current_index = i;
213
214         if (io->generic.level == RAW_SEARCH_SEARCH) {
215                 io->search_first.out.count = reply_count;
216                 DEBUG(0,("TODO: handle RAW_SEARCH_SEARCH continue\n"));
217         } else {
218                 io->t2ffirst.out.count = reply_count;
219                 io->t2ffirst.out.handle = search->handle;
220                 io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
221                 /* work out if we are going to keep the search state
222                    and allow for a search continue */
223                 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
224                     ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
225                         talloc_free(search);
226                 } else {
227                         pvfs->search.num_active_searches++;
228                         pvfs->search.next_search_handle++;
229                         talloc_steal(pvfs, search);
230                         DLIST_ADD(pvfs->search.open_searches, search);
231                 }
232         }
233
234         return NT_STATUS_OK;
235 }
236
237 /* continue a search */
238 NTSTATUS pvfs_search_next(struct smbsrv_request *req, union smb_search_next *io, 
239                           void *search_private, 
240                           BOOL (*callback)(void *, union smb_search_data *))
241 {
242         return NT_STATUS_NOT_IMPLEMENTED;
243 }
244
245 /* close a search */
246 NTSTATUS pvfs_search_close(struct smbsrv_request *req, union smb_search_close *io)
247 {
248         return NT_STATUS_NOT_IMPLEMENTED;
249 }
250