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