r2520: - finished implementing the server side of the old style search requests
[gd/samba-autobuild/.git] / source4 / smb_server / search.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMBsearch handling
4    Copyright (C) Andrew Tridgell 2003
5    Copyright (C) James J Myers 2003 <myersjj@samba.org>
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21 /*
22    This file handles the parsing of transact2 requests
23 */
24
25 #include "includes.h"
26
27 /* check req->async.status and if not OK then send an error reply */
28 #define CHECK_ASYNC_STATUS do { \
29         if (!NT_STATUS_IS_OK(req->async.status)) { \
30                 req_reply_error(req, req->async.status); \
31                 return; \
32         }} while (0)
33         
34 /* 
35    check if the backend wants to handle the request asynchronously.
36    if it wants it handled synchronously then call the send function
37    immediately
38 */
39 #define REQ_ASYNC_TAIL do { \
40         if (!(req->control_flags & REQ_CONTROL_ASYNC)) { \
41                 req->async.send_fn(req); \
42         }} while (0)
43
44 /* useful wrapper for talloc with NO_MEMORY reply */
45 #define REQ_TALLOC(ptr) do { \
46         ptr = talloc(req, sizeof(*(ptr))); \
47         if (!ptr) { \
48                 req_reply_error(req, NT_STATUS_NO_MEMORY); \
49                 return; \
50         }} while (0)
51                 
52 #define CHECK_MIN_BLOB_SIZE(blob, size) do { \
53         if ((blob)->length < (size)) { \
54                 return NT_STATUS_INFO_LENGTH_MISMATCH; \
55         }} while (0)
56
57 /* a structure to encapsulate the state information about 
58  * an in-progress search first/next operation */
59 struct search_state {
60         struct smbsrv_request *req;
61         union smb_search_data *file;
62         uint16_t last_entry_offset;
63 };
64
65 /*
66   fill a single entry in a search find reply 
67 */
68 static void find_fill_info(struct smbsrv_request *req,
69                            union smb_search_data *file)
70 {
71         char *p;
72         
73         req_grow_data(req, req->out.data_size + 43);
74         p = req->out.data + req->out.data_size - 43;
75
76         SCVAL(p,  0, file->search.id.reserved);
77         memcpy(p+1, file->search.id.name, 11);
78         SCVAL(p, 12, file->search.id.handle);
79         SIVAL(p, 13, file->search.id.server_cookie);
80         SIVAL(p, 17, file->search.id.client_cookie);
81         SCVAL(p, 21, file->search.attrib);
82         srv_push_dos_date(req->smb_conn, p, 22, file->search.write_time);
83         SIVAL(p, 26, file->search.size);
84         memset(p+30, ' ', 12);
85         memcpy(p+30, file->search.name, MIN(strlen(file->search.name)+1, 12));
86         SCVAL(p,42,0);
87 }
88
89 /* callback function for search first/next */
90 static BOOL find_callback(void *private, union smb_search_data *file)
91 {
92         struct search_state *state = (struct search_state *)private;
93
94         find_fill_info(state->req, file);
95
96         return True;
97 }
98
99 /****************************************************************************
100  Reply to a search.
101 ****************************************************************************/
102 void reply_search(struct smbsrv_request *req)
103 {
104         union smb_search_first *sf;
105         union smb_search_next *sn;
106         uint16_t resume_key_length;
107         struct search_state state;
108         char *p;
109         NTSTATUS status;
110         enum smb_search_level level = RAW_SEARCH_SEARCH;
111         uint8_t op = CVAL(req->in.hdr,HDR_COM);
112
113         if (op == SMBffirst) {
114                 level = RAW_SEARCH_FFIRST;
115         } else if (op == SMBfunique) {
116                 level = RAW_SEARCH_FUNIQUE;
117         }
118
119         REQ_TALLOC(sf);
120         
121         /* parse request */
122         if (req->in.wct != 2) {
123                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
124                 return;
125         }
126         
127         p = req->in.data;
128         p += req_pull_ascii4(req, &sf->search_first.in.pattern, 
129                              p, STR_TERMINATE);
130         if (!sf->search_first.in.pattern) {
131                 req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
132                 return;
133         }
134
135         if (req_data_oob(req, p, 3)) {
136                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
137                 return;
138         }
139         if (*p != 5) {
140                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
141                 return;
142         }
143         resume_key_length = SVAL(p, 1);
144         p += 3;
145         
146         /* setup state for callback */
147         state.req = req;
148         state.file = NULL;
149         state.last_entry_offset = 0;
150         
151         /* construct reply */
152         req_setup_reply(req, 1, 0);
153         req_append_var_block(req, NULL, 0);
154
155         if (resume_key_length != 0) {
156                 if (resume_key_length != 21 || 
157                     req_data_oob(req, p, 21) ||
158                     level == RAW_SEARCH_FUNIQUE) {
159                         req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
160                         return;
161                 }
162
163                 /* do a search next operation */
164                 REQ_TALLOC(sn);
165
166                 sn->search_next.in.id.reserved      = CVAL(p, 0);
167                 memcpy(sn->search_next.in.id.name,    p+1, 11);
168                 sn->search_next.in.id.handle        = CVAL(p, 12);
169                 sn->search_next.in.id.server_cookie = IVAL(p, 13);
170                 sn->search_next.in.id.client_cookie = IVAL(p, 17);
171
172                 sn->search_next.level = level;
173                 sn->search_next.in.max_count     = SVAL(req->in.vwv, VWV(0));
174                 sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
175                 
176                 /* call backend */
177                 status = req->tcon->ntvfs_ops->search_next(req, sn, &state, find_callback);
178                 SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
179         } else {
180                 /* do a search first operation */
181                 sf->search_first.level = level;
182                 sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
183                 sf->search_first.in.max_count     = SVAL(req->in.vwv, VWV(0));
184                 
185                 /* call backend */
186                 status = req->tcon->ntvfs_ops->search_first(req, sf, &state, find_callback);
187                 SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
188         }
189
190         if (!NT_STATUS_IS_OK(status)) {
191                 req_reply_error(req, status);
192                 return;
193         }
194
195         req_send_reply(req);
196 }
197
198
199 /****************************************************************************
200  Reply to a fclose (async reply)
201 ****************************************************************************/
202 static void reply_fclose_send(struct smbsrv_request *req)
203 {
204         CHECK_ASYNC_STATUS;
205         
206         /* construct reply */
207         req_setup_reply(req, 1, 0);
208
209         SSVAL(req->out.vwv, VWV(0), 0);
210
211         req_send_reply(req);
212 }
213
214
215 /****************************************************************************
216  Reply to fclose (stop directory search).
217 ****************************************************************************/
218 void reply_fclose(struct smbsrv_request *req)
219 {
220         union smb_search_close *sc;
221         uint16_t resume_key_length;
222         char *p;
223         const char *pattern;
224
225         REQ_TALLOC(sc);
226
227         /* parse request */
228         if (req->in.wct != 2) {
229                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
230                 return;
231         }
232         
233         p = req->in.data;
234         p += req_pull_ascii4(req, &pattern, p, STR_TERMINATE);
235         if (pattern && *pattern) {
236                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
237                 return;
238         }
239         
240         if (req_data_oob(req, p, 3)) {
241                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
242                 return;
243         }
244         if (*p != 5) {
245                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
246                 return;
247         }
248         resume_key_length = SVAL(p, 1);
249         p += 3;
250
251         if (resume_key_length != 21) {
252                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
253                 return;
254         }
255
256         if (req_data_oob(req, p, 21)) {
257                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
258                 return;
259         }
260
261         sc->fclose.level               = RAW_FINDCLOSE_FCLOSE;
262         sc->fclose.in.max_count        = SVAL(req->in.vwv, VWV(0));
263         sc->fclose.in.search_attrib    = SVAL(req->in.vwv, VWV(1));
264         sc->fclose.in.id.reserved      = CVAL(p, 0);
265         memcpy(sc->fclose.in.id.name,    p+1, 11);
266         sc->fclose.in.id.handle        = CVAL(p, 12);
267         sc->fclose.in.id.server_cookie = IVAL(p, 13);
268         sc->fclose.in.id.client_cookie = IVAL(p, 17);
269
270         /* do a search close operation */
271         req->async.send_fn = reply_fclose_send;
272         req->async.private = sc;
273
274         /* call backend */
275         req->async.status = req->tcon->ntvfs_ops->search_close(req, sc);
276
277         REQ_ASYNC_TAIL;
278 }