s3: include smbd/smbd.h where needed.
[mdw/samba.git] / source3 / smbd / smb2_write.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "../libcli/smb/smb_common.h"
25
26 static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
27                                                struct tevent_context *ev,
28                                                struct smbd_smb2_request *smb2req,
29                                                uint32_t in_smbpid,
30                                                uint64_t in_file_id_volatile,
31                                                DATA_BLOB in_data,
32                                                uint64_t in_offset,
33                                                uint32_t in_flags);
34 static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
35                                      uint32_t *out_count);
36
37 static void smbd_smb2_request_write_done(struct tevent_req *subreq);
38 NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req)
39 {
40         const uint8_t *inhdr;
41         const uint8_t *inbody;
42         int i = req->current_idx;
43         size_t expected_body_size = 0x31;
44         size_t body_size;
45         uint32_t in_smbpid;
46         uint16_t in_data_offset;
47         uint32_t in_data_length;
48         DATA_BLOB in_data_buffer;
49         uint64_t in_offset;
50         uint64_t in_file_id_persistent;
51         uint64_t in_file_id_volatile;
52         uint32_t in_flags;
53         struct tevent_req *subreq;
54
55         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
56         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
57                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
58         }
59
60         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
61
62         body_size = SVAL(inbody, 0x00);
63         if (body_size != expected_body_size) {
64                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
65         }
66
67         in_smbpid = IVAL(inhdr, SMB2_HDR_PID);
68
69         in_data_offset          = SVAL(inbody, 0x02);
70         in_data_length          = IVAL(inbody, 0x04);
71         in_offset               = BVAL(inbody, 0x08);
72         in_file_id_persistent   = BVAL(inbody, 0x10);
73         in_file_id_volatile     = BVAL(inbody, 0x18);
74         in_flags                = IVAL(inbody, 0x2C);
75
76         if (in_data_offset != (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) {
77                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
78         }
79
80         if (in_data_length > req->in.vector[i+2].iov_len) {
81                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
82         }
83
84         /* check the max write size */
85         if (in_data_length > lp_smb2_max_write()) {
86                 /* This is a warning. */
87                 DEBUG(2,("smbd_smb2_request_process_write : "
88                         "client ignored max write :%s: 0x%08X: 0x%08X\n",
89                         __location__, in_data_length, lp_smb2_max_write()));
90 #if 0
91                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
92 #endif
93         }
94
95         in_data_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
96         in_data_buffer.length = in_data_length;
97
98         if (req->compat_chain_fsp) {
99                 /* skip check */
100         } else if (in_file_id_persistent != in_file_id_volatile) {
101                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
102         }
103
104         subreq = smbd_smb2_write_send(req,
105                                       req->sconn->smb2.event_ctx,
106                                       req,
107                                       in_smbpid,
108                                       in_file_id_volatile,
109                                       in_data_buffer,
110                                       in_offset,
111                                       in_flags);
112         if (subreq == NULL) {
113                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
114         }
115         tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req);
116
117         return smbd_smb2_request_pending_queue(req, subreq);
118 }
119
120 static void smbd_smb2_request_write_done(struct tevent_req *subreq)
121 {
122         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
123                                         struct smbd_smb2_request);
124         int i = req->current_idx;
125         uint8_t *outhdr;
126         DATA_BLOB outbody;
127         DATA_BLOB outdyn;
128         uint32_t out_count = 0;
129         NTSTATUS status;
130         NTSTATUS error; /* transport error */
131
132         status = smbd_smb2_write_recv(subreq, &out_count);
133         TALLOC_FREE(subreq);
134         if (!NT_STATUS_IS_OK(status)) {
135                 error = smbd_smb2_request_error(req, status);
136                 if (!NT_STATUS_IS_OK(error)) {
137                         smbd_server_connection_terminate(req->sconn,
138                                                          nt_errstr(error));
139                         return;
140                 }
141                 return;
142         }
143
144         outhdr = (uint8_t *)req->out.vector[i].iov_base;
145
146         outbody = data_blob_talloc(req->out.vector, NULL, 0x10);
147         if (outbody.data == NULL) {
148                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
149                 if (!NT_STATUS_IS_OK(error)) {
150                         smbd_server_connection_terminate(req->sconn,
151                                                          nt_errstr(error));
152                         return;
153                 }
154                 return;
155         }
156
157         SSVAL(outbody.data, 0x00, 0x10 + 1);    /* struct size */
158         SSVAL(outbody.data, 0x02, 0);           /* reserved */
159         SIVAL(outbody.data, 0x04, out_count);   /* count */
160         SIVAL(outbody.data, 0x08, 0);           /* remaining */
161         SSVAL(outbody.data, 0x0C, 0);           /* write channel info offset */
162         SSVAL(outbody.data, 0x0E, 0);           /* write channel info length */
163
164         outdyn = data_blob_const(NULL, 0);
165
166         error = smbd_smb2_request_done(req, outbody, &outdyn);
167         if (!NT_STATUS_IS_OK(error)) {
168                 smbd_server_connection_terminate(req->sconn, nt_errstr(error));
169                 return;
170         }
171 }
172
173 struct smbd_smb2_write_state {
174         struct smbd_smb2_request *smb2req;
175         files_struct *fsp;
176         bool write_through;
177         uint32_t in_length;
178         uint64_t in_offset;
179         uint32_t out_count;
180 };
181
182 static void smbd_smb2_write_pipe_done(struct tevent_req *subreq);
183
184 NTSTATUS smb2_write_complete(struct tevent_req *req, ssize_t nwritten, int err)
185 {
186         NTSTATUS status;
187         struct smbd_smb2_write_state *state = tevent_req_data(req,
188                                         struct smbd_smb2_write_state);
189         files_struct *fsp = state->fsp;
190
191         DEBUG(3,("smb2: fnum=[%d/%s] "
192                 "length=%lu offset=%lu wrote=%lu\n",
193                 fsp->fnum,
194                 fsp_str_dbg(fsp),
195                 (unsigned long)state->in_length,
196                 (unsigned long)state->in_offset,
197                 (unsigned long)nwritten));
198
199         if (nwritten == -1) {
200                 return map_nt_error_from_unix(err);
201         }
202
203         if ((nwritten == 0) && (state->in_length != 0)) {
204                 DEBUG(5,("smb2: write [%s] disk full\n",
205                         fsp_str_dbg(fsp)));
206                 return NT_STATUS_DISK_FULL;
207         }
208
209         status = sync_file(fsp->conn, fsp, state->write_through);
210         if (!NT_STATUS_IS_OK(status)) {
211                 DEBUG(5,("smb2: sync_file for %s returned %s\n",
212                         fsp_str_dbg(fsp),
213                         nt_errstr(status)));
214                 return status;
215         }
216
217         state->out_count = nwritten;
218
219         return NT_STATUS_OK;
220 }
221
222 static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
223                                                struct tevent_context *ev,
224                                                struct smbd_smb2_request *smb2req,
225                                                uint32_t in_smbpid,
226                                                uint64_t in_file_id_volatile,
227                                                DATA_BLOB in_data,
228                                                uint64_t in_offset,
229                                                uint32_t in_flags)
230 {
231         NTSTATUS status;
232         struct tevent_req *req = NULL;
233         struct smbd_smb2_write_state *state = NULL;
234         struct smb_request *smbreq = NULL;
235         connection_struct *conn = smb2req->tcon->compat_conn;
236         files_struct *fsp = NULL;
237         ssize_t nwritten;
238         struct lock_struct lock;
239
240         req = tevent_req_create(mem_ctx, &state,
241                                 struct smbd_smb2_write_state);
242         if (req == NULL) {
243                 return NULL;
244         }
245         state->smb2req = smb2req;
246         if (in_flags & 0x00000001) {
247                 state->write_through = true;
248         }
249         state->in_length = in_data.length;
250         state->out_count = 0;
251
252         DEBUG(10,("smbd_smb2_write: file_id[0x%016llX]\n",
253                   (unsigned long long)in_file_id_volatile));
254
255         smbreq = smbd_smb2_fake_smb_request(smb2req);
256         if (tevent_req_nomem(smbreq, req)) {
257                 return tevent_req_post(req, ev);
258         }
259
260         fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
261         if (fsp == NULL) {
262                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
263                 return tevent_req_post(req, ev);
264         }
265         if (conn != fsp->conn) {
266                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
267                 return tevent_req_post(req, ev);
268         }
269         if (smb2req->session->vuid != fsp->vuid) {
270                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
271                 return tevent_req_post(req, ev);
272         }
273
274         state->fsp = fsp;
275
276         if (IS_IPC(smbreq->conn)) {
277                 struct tevent_req *subreq = NULL;
278
279                 if (!fsp_is_np(fsp)) {
280                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
281                         return tevent_req_post(req, ev);
282                 }
283
284                 subreq = np_write_send(state, smbd_event_context(),
285                                        fsp->fake_file_handle,
286                                        in_data.data,
287                                        in_data.length);
288                 if (tevent_req_nomem(subreq, req)) {
289                         return tevent_req_post(req, ev);
290                 }
291                 tevent_req_set_callback(subreq,
292                                         smbd_smb2_write_pipe_done,
293                                         req);
294                 return req;
295         }
296
297         if (!CHECK_WRITE(fsp)) {
298                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
299                 return tevent_req_post(req, ev);
300         }
301
302         /* Try and do an asynchronous write. */
303         status = schedule_aio_smb2_write(conn,
304                                         smbreq,
305                                         fsp,
306                                         in_offset,
307                                         in_data,
308                                         state->write_through);
309
310         if (NT_STATUS_IS_OK(status)) {
311                 /*
312                  * Doing an async write. Don't
313                  * send a "gone async" message
314                  * as we expect this to be less
315                  * than the client timeout period.
316                  * JRA. FIXME for offline files..
317                  * FIXME - add cancel code..
318                  */
319                 smb2req->async = true;
320                 return req;
321         }
322
323         if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
324                 /* Real error in setting up aio. Fail. */
325                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
326                 return tevent_req_post(req, ev);
327         }
328
329         /* Fallback to synchronous. */
330         init_strict_lock_struct(fsp,
331                                 in_file_id_volatile,
332                                 in_offset,
333                                 in_data.length,
334                                 WRITE_LOCK,
335                                 &lock);
336
337         if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) {
338                 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
339                 return tevent_req_post(req, ev);
340         }
341
342         nwritten = write_file(smbreq, fsp,
343                               (const char *)in_data.data,
344                               in_offset,
345                               in_data.length);
346
347         status = smb2_write_complete(req, nwritten, errno);
348
349         SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock);
350
351         DEBUG(10,("smb2: write on "
352                 "file %s, offset %.0f, requested %u, written = %u\n",
353                 fsp_str_dbg(fsp),
354                 (double)in_offset,
355                 (unsigned int)in_data.length,
356                 (unsigned int)nwritten ));
357
358         if (!NT_STATUS_IS_OK(status)) {
359                 tevent_req_nterror(req, status);
360         } else {
361                 /* Success. */
362                 tevent_req_done(req);
363         }
364
365         return tevent_req_post(req, ev);
366 }
367
368 static void smbd_smb2_write_pipe_done(struct tevent_req *subreq)
369 {
370         struct tevent_req *req = tevent_req_callback_data(subreq,
371                                  struct tevent_req);
372         struct smbd_smb2_write_state *state = tevent_req_data(req,
373                                               struct smbd_smb2_write_state);
374         NTSTATUS status;
375         ssize_t nwritten = -1;
376
377         status = np_write_recv(subreq, &nwritten);
378         TALLOC_FREE(subreq);
379         if (!NT_STATUS_IS_OK(status)) {
380                 tevent_req_nterror(req, status);
381                 return;
382         }
383
384         if ((nwritten == 0 && state->in_length != 0) || (nwritten < 0)) {
385                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
386                 return;
387         }
388
389         state->out_count = nwritten;
390
391         tevent_req_done(req);
392 }
393
394 static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
395                                      uint32_t *out_count)
396 {
397         NTSTATUS status;
398         struct smbd_smb2_write_state *state = tevent_req_data(req,
399                                               struct smbd_smb2_write_state);
400
401         if (tevent_req_is_nterror(req, &status)) {
402                 tevent_req_received(req);
403                 return status;
404         }
405
406         *out_count = state->out_count;
407
408         tevent_req_received(req);
409         return NT_STATUS_OK;
410 }