r1277: rename struct server_context to smbsrv_ontext
[samba.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, size) do { \
46         ptr = talloc(req->mem_ctx, size); \
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 request_context *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 request_context *req,
69                            union smb_search_data *file)
70 {
71         char *p = req->out.data + req->out.data_size;
72         uint32_t dos_date;
73         char search_name[13];
74         
75         DEBUG(9,("find_fill_info: input file data: attr=0x%x size=%u time=0x%x name=%13s\n",
76                 file->search.attrib, file->search.size,
77                 (uint32_t)file->search.write_time, file->search.name));
78
79         p += req_append_bytes(req, file->search.search_id.data, 21);
80         p += req_append_bytes(req, (char*)&file->search.attrib, 1);
81         srv_push_dos_date3(req->smb_ctx, (uint8_t *)&dos_date, 0, file->search.write_time);
82         p += req_append_bytes(req, (char*)&dos_date, 4);
83         p += req_append_bytes(req, (char*)&file->search.size, 4);
84         memset(&search_name[0], ' ', 13);
85         memcpy(&search_name[0], file->search.name, 
86                 MAX(13, strlen(file->search.name)));
87         p += req_append_bytes(req, &search_name[0], 13);
88 }
89
90 /* callback function for search first/next */
91 static BOOL find_callback(void *private, union smb_search_data *file)
92 {
93         struct search_state *state = (struct search_state *)private;
94
95         find_fill_info(state->req, file);
96
97         return True;
98 }
99
100 /****************************************************************************
101  Reply to a search.
102 ****************************************************************************/
103 void reply_search(struct request_context *req)
104 {
105         union smb_search_first *sf;
106         union smb_search_next *sn;
107         DATA_BLOB resume_key;
108         uint16_t resume_key_length;
109         struct search_state state;
110         char *p;
111
112         REQ_TALLOC(sf, sizeof(*sf));
113         
114         /* parse request */
115         if (req->in.wct != 2) {
116                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
117                 return;
118         }
119         
120         p = req->in.data;
121         p += req_pull_ascii4(req, &sf->search_first.in.pattern, 
122                 p, STR_TERMINATE);
123         if (!sf->search_first.in.pattern) {
124                 req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
125                 return;
126         }
127         /* pull in type 5 byte and length */
128         if (!req_pull_blob(req, p, 3, &resume_key))
129                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
130         resume_key_length = SVAL(resume_key.data, 1);
131         p += 3;
132         DEBUG(19,("reply_search: pattern=%s, key_length=%d\n",
133                 sf->search_first.in.pattern, resume_key_length));
134         
135         /* setup state for callback */
136         state.req = req;
137         state.file = NULL;
138         state.last_entry_offset = 0;
139         
140         /* construct reply */
141         req_setup_reply(req, 1, 0);
142         req_append_var_block(req, NULL, 0);
143
144         if (resume_key_length > 0) {
145                 /* do a search next operation */
146                 REQ_TALLOC(sn, sizeof(*sn));
147                 sn->search_next.level = RAW_SEARCH_SEARCH;
148                 req->async.private = sn;
149                 if (!req_pull_blob(req, req->in.data, resume_key_length, 
150                                 &(sn->search_next.in.search_id)))
151                         req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
152                 sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
153                 sn->search_next.in.max_count     = SVAL(req->in.vwv, VWV(0));
154                 
155                 /* call backend */
156                 req->async.status = req->conn->ntvfs_ops->search_next(req, 
157                         sn, &state, find_callback);
158                 SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
159         } else {
160                 /* do a search first operation */
161                 req->async.private = sf;
162                 sf->search_first.level = RAW_SEARCH_SEARCH;
163                 sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
164                 sf->search_first.in.max_count     = SVAL(req->in.vwv, VWV(0));
165                 
166                 /* call backend */
167                 req->async.status = req->conn->ntvfs_ops->search_first(req, 
168                         sf, &state, find_callback);
169                 SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
170         }
171
172         req_send_reply(req);
173 }
174
175
176 /****************************************************************************
177  Reply to a fclose (async reply)
178 ****************************************************************************/
179 static void reply_fclose_send(struct request_context *req)
180 {
181         CHECK_ASYNC_STATUS;
182         
183         /* construct reply */
184         req_setup_reply(req, 1, 0);
185
186         req_send_reply(req);
187 }
188
189
190 /****************************************************************************
191  Reply to fclose (stop directory search).
192 ****************************************************************************/
193 void reply_fclose(struct request_context *req)
194 {
195         union smb_search_next *sn;
196         DATA_BLOB resume_key;
197         uint16_t resume_key_length;
198
199         REQ_TALLOC(sn, sizeof(*sn));
200
201         /* parse request */
202         if (req->in.wct != 2) {
203                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
204                 return;
205         }
206         
207         sn->search_next.level = RAW_SEARCH_FCLOSE;
208         
209         /* pull in type 5 byte and length */
210         if (!req_pull_blob(req, req->in.data, 3, &resume_key))
211                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
212         resume_key_length = SVAL(resume_key.data, 1);
213         if (resume_key_length > 0) {
214                 /* do a search close operation */
215                 if (!req_pull_blob(req, req->in.data, resume_key_length, 
216                                 &(sn->search_next.in.search_id)))
217                         req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
218         } else
219                 req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
220
221         req->async.send_fn = reply_fclose_send;
222         req->async.private = sn;
223
224         /* call backend */
225         req->async.status = req->conn->ntvfs_ops->search_next(req, sn,
226                 NULL, NULL);
227
228         REQ_ASYNC_TAIL;
229 }