a99958d6c6c7782f12a6cfd8cb9e86272ac43619
[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, ' ', 13);
85         memcpy(p+30, file->search.name, MIN(strlen(file->search.name)+1, 13));
86 }
87
88 /* callback function for search first/next */
89 static BOOL find_callback(void *private, union smb_search_data *file)
90 {
91         struct search_state *state = (struct search_state *)private;
92
93         find_fill_info(state->req, file);
94
95         return True;
96 }
97
98 /****************************************************************************
99  Reply to a search.
100 ****************************************************************************/
101 void reply_search(struct smbsrv_request *req)
102 {
103         union smb_search_first *sf;
104         union smb_search_next *sn;
105         uint16_t resume_key_length;
106         struct search_state state;
107         char *p;
108         NTSTATUS status;
109         enum smb_search_level level = RAW_SEARCH_SEARCH;
110         uint8_t op = CVAL(req->in.hdr,HDR_COM);
111
112         if (op == SMBffirst) {
113                 level = RAW_SEARCH_FFIRST;
114         } else if (op == SMBfunique) {
115                 level = RAW_SEARCH_FUNIQUE;
116         }
117
118         REQ_TALLOC(sf);
119         
120         /* parse request */
121         if (req->in.wct != 2) {
122                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
123                 return;
124         }
125         
126         p = req->in.data;
127         p += req_pull_ascii4(req, &sf->search_first.in.pattern, 
128                              p, STR_TERMINATE);
129         if (!sf->search_first.in.pattern) {
130                 req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
131                 return;
132         }
133
134         if (req_data_oob(req, p, 3)) {
135                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
136                 return;
137         }
138         if (*p != 5) {
139                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
140                 return;
141         }
142         resume_key_length = SVAL(p, 1);
143         p += 3;
144         
145         /* setup state for callback */
146         state.req = req;
147         state.file = NULL;
148         state.last_entry_offset = 0;
149         
150         /* construct reply */
151         req_setup_reply(req, 1, 0);
152         req_append_var_block(req, NULL, 0);
153
154         if (resume_key_length != 0) {
155                 if (resume_key_length != 21 || 
156                     req_data_oob(req, p, 21) ||
157                     level == RAW_SEARCH_FUNIQUE) {
158                         req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
159                         return;
160                 }
161
162                 /* do a search next operation */
163                 REQ_TALLOC(sn);
164
165                 sn->search_next.in.id.reserved      = CVAL(p, 0);
166                 memcpy(sn->search_next.in.id.name,    p+1, 11);
167                 sn->search_next.in.id.handle        = CVAL(p, 12);
168                 sn->search_next.in.id.server_cookie = IVAL(p, 13);
169                 sn->search_next.in.id.client_cookie = IVAL(p, 17);
170
171                 sn->search_next.level = level;
172                 sn->search_next.in.max_count     = SVAL(req->in.vwv, VWV(0));
173                 sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
174                 
175                 /* call backend */
176                 status = req->tcon->ntvfs_ops->search_next(req, sn, &state, find_callback);
177                 SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
178         } else {
179                 /* do a search first operation */
180                 sf->search_first.level = level;
181                 sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
182                 sf->search_first.in.max_count     = SVAL(req->in.vwv, VWV(0));
183                 
184                 /* call backend */
185                 status = req->tcon->ntvfs_ops->search_first(req, sf, &state, find_callback);
186                 SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
187         }
188
189         if (!NT_STATUS_IS_OK(status)) {
190                 req_reply_error(req, status);
191                 return;
192         }
193
194         req_send_reply(req);
195 }
196
197
198 /****************************************************************************
199  Reply to a fclose (async reply)
200 ****************************************************************************/
201 static void reply_fclose_send(struct smbsrv_request *req)
202 {
203         CHECK_ASYNC_STATUS;
204         
205         /* construct reply */
206         req_setup_reply(req, 1, 0);
207
208         SSVAL(req->out.vwv, VWV(0), 0);
209
210         req_send_reply(req);
211 }
212
213
214 /****************************************************************************
215  Reply to fclose (stop directory search).
216 ****************************************************************************/
217 void reply_fclose(struct smbsrv_request *req)
218 {
219         union smb_search_close *sc;
220         uint16_t resume_key_length;
221         char *p;
222         const char *pattern;
223
224         REQ_TALLOC(sc);
225
226         /* parse request */
227         if (req->in.wct != 2) {
228                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
229                 return;
230         }
231         
232         p = req->in.data;
233         p += req_pull_ascii4(req, &pattern, p, STR_TERMINATE);
234         if (pattern && *pattern) {
235                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
236                 return;
237         }
238         
239         if (req_data_oob(req, p, 3)) {
240                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
241                 return;
242         }
243         if (*p != 5) {
244                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
245                 return;
246         }
247         resume_key_length = SVAL(p, 1);
248         p += 3;
249
250         if (resume_key_length != 21) {
251                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
252                 return;
253         }
254
255         if (req_data_oob(req, p, 21)) {
256                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
257                 return;
258         }
259
260         sc->fclose.level               = RAW_FINDCLOSE_FCLOSE;
261         sc->fclose.in.max_count        = SVAL(req->in.vwv, VWV(0));
262         sc->fclose.in.search_attrib    = SVAL(req->in.vwv, VWV(1));
263         sc->fclose.in.id.reserved      = CVAL(p, 0);
264         memcpy(sc->fclose.in.id.name,    p+1, 11);
265         sc->fclose.in.id.handle        = CVAL(p, 12);
266         sc->fclose.in.id.server_cookie = IVAL(p, 13);
267         sc->fclose.in.id.client_cookie = IVAL(p, 17);
268
269         /* do a search close operation */
270         req->async.send_fn = reply_fclose_send;
271         req->async.private = sc;
272
273         /* call backend */
274         req->async.status = req->tcon->ntvfs_ops->search_close(req, sc);
275
276         REQ_ASYNC_TAIL;
277 }