Line data Source code
1 : /*
2 : Unix SMB/Netbios implementation.
3 : Version 3.0
4 : async_io read handling using POSIX async io.
5 : Copyright (C) Jeremy Allison 2005.
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 "../lib/util/tevent_ntstatus.h"
25 : #include "../lib/util/tevent_unix.h"
26 :
27 : /****************************************************************************
28 : Accessor function to return write_through state.
29 : *****************************************************************************/
30 :
31 0 : bool aio_write_through_requested(struct aio_extra *aio_ex)
32 : {
33 0 : return aio_ex->write_through;
34 : }
35 :
36 : /****************************************************************************
37 : Create the extended aio struct we must keep around for the lifetime
38 : of the aio call.
39 : *****************************************************************************/
40 :
41 772 : struct aio_extra *create_aio_extra(TALLOC_CTX *mem_ctx,
42 : files_struct *fsp,
43 : size_t buflen)
44 : {
45 772 : struct aio_extra *aio_ex = talloc_zero(mem_ctx, struct aio_extra);
46 :
47 772 : if (!aio_ex) {
48 0 : return NULL;
49 : }
50 :
51 : /* The output buffer stored in the aio_ex is the start of
52 : the smb return buffer. The buffer used in the acb
53 : is the start of the reply data portion of that buffer. */
54 :
55 772 : if (buflen) {
56 0 : aio_ex->outbuf = data_blob_talloc(aio_ex, NULL, buflen);
57 0 : if (!aio_ex->outbuf.data) {
58 0 : TALLOC_FREE(aio_ex);
59 0 : return NULL;
60 : }
61 : }
62 772 : aio_ex->fsp = fsp;
63 772 : return aio_ex;
64 : }
65 :
66 : struct aio_req_fsp_link {
67 : files_struct *fsp;
68 : struct tevent_req *req;
69 : };
70 :
71 78343 : static int aio_del_req_from_fsp(struct aio_req_fsp_link *lnk)
72 : {
73 : unsigned i;
74 78343 : files_struct *fsp = lnk->fsp;
75 78343 : struct tevent_req *req = lnk->req;
76 :
77 78343 : for (i=0; i<fsp->num_aio_requests; i++) {
78 78343 : if (fsp->aio_requests[i] == req) {
79 78343 : break;
80 : }
81 : }
82 78343 : if (i == fsp->num_aio_requests) {
83 0 : DEBUG(1, ("req %p not found in fsp %p\n", req, fsp));
84 0 : return 0;
85 : }
86 78343 : fsp->num_aio_requests -= 1;
87 78343 : fsp->aio_requests[i] = fsp->aio_requests[fsp->num_aio_requests];
88 :
89 78343 : if (fsp->num_aio_requests == 0) {
90 78338 : TALLOC_FREE(fsp->aio_requests);
91 : }
92 78343 : return 0;
93 : }
94 :
95 78343 : bool aio_add_req_to_fsp(files_struct *fsp, struct tevent_req *req)
96 : {
97 : size_t array_len;
98 : struct aio_req_fsp_link *lnk;
99 :
100 78343 : lnk = talloc(req, struct aio_req_fsp_link);
101 78343 : if (lnk == NULL) {
102 0 : return false;
103 : }
104 :
105 78343 : array_len = talloc_array_length(fsp->aio_requests);
106 78343 : if (array_len <= fsp->num_aio_requests) {
107 : struct tevent_req **tmp;
108 :
109 78338 : if (fsp->num_aio_requests + 10 < 10) {
110 : /* Integer wrap. */
111 0 : TALLOC_FREE(lnk);
112 0 : return false;
113 : }
114 :
115 : /*
116 : * Allocate in blocks of 10 so we don't allocate
117 : * on every aio request.
118 : */
119 78338 : tmp = talloc_realloc(
120 : fsp, fsp->aio_requests, struct tevent_req *,
121 : fsp->num_aio_requests+10);
122 78338 : if (tmp == NULL) {
123 0 : TALLOC_FREE(lnk);
124 0 : return false;
125 : }
126 78338 : fsp->aio_requests = tmp;
127 : }
128 78343 : fsp->aio_requests[fsp->num_aio_requests] = req;
129 78343 : fsp->num_aio_requests += 1;
130 :
131 78343 : lnk->fsp = fsp;
132 78343 : lnk->req = req;
133 78343 : talloc_set_destructor(lnk, aio_del_req_from_fsp);
134 :
135 78343 : return true;
136 : }
137 :
138 : struct pwrite_fsync_state {
139 : struct tevent_context *ev;
140 : files_struct *fsp;
141 : bool write_through;
142 : ssize_t nwritten;
143 : };
144 :
145 : static void pwrite_fsync_write_done(struct tevent_req *subreq);
146 : static void pwrite_fsync_sync_done(struct tevent_req *subreq);
147 :
148 423 : struct tevent_req *pwrite_fsync_send(TALLOC_CTX *mem_ctx,
149 : struct tevent_context *ev,
150 : struct files_struct *fsp,
151 : const void *data,
152 : size_t n, off_t offset,
153 : bool write_through)
154 : {
155 : struct tevent_req *req, *subreq;
156 : struct pwrite_fsync_state *state;
157 : bool ok;
158 :
159 423 : req = tevent_req_create(mem_ctx, &state, struct pwrite_fsync_state);
160 423 : if (req == NULL) {
161 0 : return NULL;
162 : }
163 423 : state->ev = ev;
164 423 : state->fsp = fsp;
165 423 : state->write_through = write_through;
166 :
167 423 : ok = vfs_valid_pwrite_range(offset, n);
168 423 : if (!ok) {
169 0 : tevent_req_error(req, EINVAL);
170 0 : return tevent_req_post(req, ev);
171 : }
172 :
173 423 : if (n == 0) {
174 0 : tevent_req_done(req);
175 0 : return tevent_req_post(req, ev);
176 : }
177 :
178 423 : subreq = SMB_VFS_PWRITE_SEND(state, ev, fsp, data, n, offset);
179 423 : if (tevent_req_nomem(subreq, req)) {
180 0 : return tevent_req_post(req, ev);
181 : }
182 423 : tevent_req_set_callback(subreq, pwrite_fsync_write_done, req);
183 423 : return req;
184 : }
185 :
186 423 : static void pwrite_fsync_write_done(struct tevent_req *subreq)
187 : {
188 423 : struct tevent_req *req = tevent_req_callback_data(
189 : subreq, struct tevent_req);
190 423 : struct pwrite_fsync_state *state = tevent_req_data(
191 : req, struct pwrite_fsync_state);
192 423 : connection_struct *conn = state->fsp->conn;
193 : bool do_sync;
194 : struct vfs_aio_state vfs_aio_state;
195 :
196 423 : state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &vfs_aio_state);
197 423 : TALLOC_FREE(subreq);
198 423 : if (state->nwritten == -1) {
199 0 : tevent_req_error(req, vfs_aio_state.error);
200 22 : return;
201 : }
202 :
203 868 : do_sync = (lp_strict_sync(SNUM(conn)) &&
204 846 : (lp_sync_always(SNUM(conn)) || state->write_through));
205 423 : if (!do_sync) {
206 423 : tevent_req_done(req);
207 423 : return;
208 : }
209 :
210 0 : subreq = SMB_VFS_FSYNC_SEND(state, state->ev, state->fsp);
211 0 : if (tevent_req_nomem(subreq, req)) {
212 0 : return;
213 : }
214 0 : tevent_req_set_callback(subreq, pwrite_fsync_sync_done, req);
215 : }
216 :
217 0 : static void pwrite_fsync_sync_done(struct tevent_req *subreq)
218 : {
219 0 : struct tevent_req *req = tevent_req_callback_data(
220 : subreq, struct tevent_req);
221 : int ret;
222 : struct vfs_aio_state vfs_aio_state;
223 :
224 0 : ret = SMB_VFS_FSYNC_RECV(subreq, &vfs_aio_state);
225 0 : TALLOC_FREE(subreq);
226 0 : if (ret == -1) {
227 0 : tevent_req_error(req, vfs_aio_state.error);
228 0 : return;
229 : }
230 0 : tevent_req_done(req);
231 : }
232 :
233 423 : ssize_t pwrite_fsync_recv(struct tevent_req *req, int *perr)
234 : {
235 423 : struct pwrite_fsync_state *state = tevent_req_data(
236 : req, struct pwrite_fsync_state);
237 :
238 423 : if (tevent_req_is_unix_error(req, perr)) {
239 0 : return -1;
240 : }
241 423 : return state->nwritten;
242 : }
243 :
244 0 : bool cancel_smb2_aio(struct smb_request *smbreq)
245 : {
246 0 : struct smbd_smb2_request *smb2req = smbreq->smb2req;
247 0 : struct aio_extra *aio_ex = NULL;
248 :
249 0 : if (smb2req) {
250 0 : aio_ex = talloc_get_type(smbreq->async_priv,
251 : struct aio_extra);
252 : }
253 :
254 0 : if (aio_ex == NULL) {
255 0 : return false;
256 : }
257 :
258 0 : if (aio_ex->fsp == NULL) {
259 0 : return false;
260 : }
261 :
262 : /*
263 : * We let the aio request run and don't try to cancel it which means
264 : * processing of the SMB2 request must continue as normal, cf MS-SMB2
265 : * 3.3.5.16:
266 : *
267 : * If the target request is not successfully canceled, processing of
268 : * the target request MUST continue and no response is sent to the
269 : * cancel request.
270 : */
271 :
272 0 : return false;
273 : }
274 :
275 : static void aio_pread_smb2_done(struct tevent_req *req);
276 :
277 : /****************************************************************************
278 : Set up an aio request from a SMB2 read call.
279 : *****************************************************************************/
280 :
281 349 : NTSTATUS schedule_smb2_aio_read(connection_struct *conn,
282 : struct smb_request *smbreq,
283 : files_struct *fsp,
284 : TALLOC_CTX *ctx,
285 : DATA_BLOB *preadbuf,
286 : off_t startpos,
287 : size_t smb_maxcnt)
288 : {
289 : struct aio_extra *aio_ex;
290 349 : size_t min_aio_read_size = lp_aio_read_size(SNUM(conn));
291 : struct tevent_req *req;
292 : bool ok;
293 :
294 349 : ok = vfs_valid_pread_range(startpos, smb_maxcnt);
295 349 : if (!ok) {
296 0 : return NT_STATUS_INVALID_PARAMETER;
297 : }
298 :
299 349 : if (fsp_is_alternate_stream(fsp)) {
300 0 : DEBUG(10, ("AIO on streams not yet supported\n"));
301 0 : return NT_STATUS_RETRY;
302 : }
303 :
304 349 : if (fsp->op == NULL) {
305 : /* No AIO on internal opens. */
306 0 : return NT_STATUS_RETRY;
307 : }
308 :
309 349 : if ((!min_aio_read_size || (smb_maxcnt < min_aio_read_size))
310 0 : && !SMB_VFS_AIO_FORCE(fsp)) {
311 : /* Too small a read for aio request. */
312 0 : DEBUG(10,("smb2: read size (%u) too small "
313 : "for minimum aio_read of %u\n",
314 : (unsigned int)smb_maxcnt,
315 : (unsigned int)min_aio_read_size ));
316 0 : return NT_STATUS_RETRY;
317 : }
318 :
319 349 : if (smbd_smb2_is_compound(smbreq->smb2req)) {
320 0 : return NT_STATUS_RETRY;
321 : }
322 :
323 : /* Create the out buffer. */
324 349 : *preadbuf = data_blob_talloc(ctx, NULL, smb_maxcnt);
325 349 : if (preadbuf->data == NULL) {
326 0 : return NT_STATUS_NO_MEMORY;
327 : }
328 :
329 349 : if (!(aio_ex = create_aio_extra(smbreq->smb2req, fsp, 0))) {
330 0 : return NT_STATUS_NO_MEMORY;
331 : }
332 :
333 690 : init_strict_lock_struct(fsp,
334 349 : fsp->op->global->open_persistent_id,
335 : (uint64_t)startpos,
336 : (uint64_t)smb_maxcnt,
337 : READ_LOCK,
338 : lp_posix_cifsu_locktype(fsp),
339 : &aio_ex->lock);
340 :
341 : /* Take the lock until the AIO completes. */
342 349 : if (!SMB_VFS_STRICT_LOCK_CHECK(conn, fsp, &aio_ex->lock)) {
343 0 : TALLOC_FREE(aio_ex);
344 0 : return NT_STATUS_FILE_LOCK_CONFLICT;
345 : }
346 :
347 349 : aio_ex->nbyte = smb_maxcnt;
348 349 : aio_ex->offset = startpos;
349 :
350 349 : req = SMB_VFS_PREAD_SEND(aio_ex, fsp->conn->sconn->ev_ctx, fsp,
351 : preadbuf->data, smb_maxcnt, startpos);
352 349 : if (req == NULL) {
353 0 : DEBUG(0, ("smb2: SMB_VFS_PREAD_SEND failed. "
354 : "Error %s\n", strerror(errno)));
355 0 : TALLOC_FREE(aio_ex);
356 0 : return NT_STATUS_RETRY;
357 : }
358 349 : tevent_req_set_callback(req, aio_pread_smb2_done, aio_ex);
359 :
360 349 : if (!aio_add_req_to_fsp(fsp, req)) {
361 0 : DEBUG(1, ("Could not add req to fsp\n"));
362 0 : TALLOC_FREE(aio_ex);
363 0 : return NT_STATUS_RETRY;
364 : }
365 :
366 : /* We don't need talloc_move here as both aio_ex and
367 : * smbreq are children of smbreq->smb2req. */
368 349 : aio_ex->smbreq = smbreq;
369 349 : smbreq->async_priv = aio_ex;
370 :
371 349 : DEBUG(10,("smb2: scheduled aio_read for file %s, "
372 : "offset %.0f, len = %u (mid = %u)\n",
373 : fsp_str_dbg(fsp), (double)startpos, (unsigned int)smb_maxcnt,
374 : (unsigned int)aio_ex->smbreq->mid ));
375 :
376 349 : return NT_STATUS_OK;
377 : }
378 :
379 349 : static void aio_pread_smb2_done(struct tevent_req *req)
380 : {
381 349 : struct aio_extra *aio_ex = tevent_req_callback_data(
382 : req, struct aio_extra);
383 349 : struct tevent_req *subreq = aio_ex->smbreq->smb2req->subreq;
384 349 : files_struct *fsp = aio_ex->fsp;
385 : NTSTATUS status;
386 : ssize_t nread;
387 349 : struct vfs_aio_state vfs_aio_state = { 0 };
388 :
389 349 : nread = SMB_VFS_PREAD_RECV(req, &vfs_aio_state);
390 349 : TALLOC_FREE(req);
391 :
392 349 : DEBUG(10, ("pread_recv returned %d, err = %s\n", (int)nread,
393 : (nread == -1) ? strerror(vfs_aio_state.error) : "no error"));
394 :
395 : /* Common error or success code processing for async or sync
396 : read returns. */
397 :
398 349 : status = smb2_read_complete(subreq, nread, vfs_aio_state.error);
399 :
400 349 : if (nread > 0) {
401 349 : fh_set_pos(fsp->fh, aio_ex->offset + nread);
402 349 : fh_set_position_information(fsp->fh,
403 349 : fh_get_pos(fsp->fh));
404 : }
405 :
406 349 : DEBUG(10, ("smb2: scheduled aio_read completed "
407 : "for file %s, offset %.0f, len = %u "
408 : "(errcode = %d, NTSTATUS = %s)\n",
409 : fsp_str_dbg(aio_ex->fsp),
410 : (double)aio_ex->offset,
411 : (unsigned int)nread,
412 : vfs_aio_state.error, nt_errstr(status)));
413 :
414 349 : if (!NT_STATUS_IS_OK(status)) {
415 0 : tevent_req_nterror(subreq, status);
416 0 : return;
417 : }
418 349 : tevent_req_done(subreq);
419 : }
420 :
421 : static void aio_pwrite_smb2_done(struct tevent_req *req);
422 :
423 : /****************************************************************************
424 : Set up an aio request from a SMB2write call.
425 : *****************************************************************************/
426 :
427 447 : NTSTATUS schedule_aio_smb2_write(connection_struct *conn,
428 : struct smb_request *smbreq,
429 : files_struct *fsp,
430 : uint64_t in_offset,
431 : DATA_BLOB in_data,
432 : bool write_through)
433 : {
434 447 : struct aio_extra *aio_ex = NULL;
435 447 : size_t min_aio_write_size = lp_aio_write_size(SNUM(conn));
436 : struct tevent_req *req;
437 :
438 447 : if (fsp_is_alternate_stream(fsp)) {
439 : /* No AIO on streams yet */
440 24 : DEBUG(10, ("AIO on streams not yet supported\n"));
441 24 : return NT_STATUS_RETRY;
442 : }
443 :
444 423 : if (fsp->op == NULL) {
445 : /* No AIO on internal opens. */
446 0 : return NT_STATUS_RETRY;
447 : }
448 :
449 423 : if ((!min_aio_write_size || (in_data.length < min_aio_write_size))
450 0 : && !SMB_VFS_AIO_FORCE(fsp)) {
451 : /* Too small a write for aio request. */
452 0 : DEBUG(10,("smb2: write size (%u) too "
453 : "small for minimum aio_write of %u\n",
454 : (unsigned int)in_data.length,
455 : (unsigned int)min_aio_write_size ));
456 0 : return NT_STATUS_RETRY;
457 : }
458 :
459 423 : if (smbd_smb2_is_compound(smbreq->smb2req)) {
460 0 : return NT_STATUS_RETRY;
461 : }
462 :
463 423 : if (smbreq->unread_bytes) {
464 : /* Can't do async with recvfile. */
465 0 : return NT_STATUS_RETRY;
466 : }
467 :
468 423 : if (!(aio_ex = create_aio_extra(smbreq->smb2req, fsp, 0))) {
469 0 : return NT_STATUS_NO_MEMORY;
470 : }
471 :
472 423 : aio_ex->write_through = write_through;
473 :
474 824 : init_strict_lock_struct(fsp,
475 423 : fsp->op->global->open_persistent_id,
476 : in_offset,
477 423 : (uint64_t)in_data.length,
478 : WRITE_LOCK,
479 : lp_posix_cifsu_locktype(fsp),
480 : &aio_ex->lock);
481 :
482 : /* Take the lock until the AIO completes. */
483 423 : if (!SMB_VFS_STRICT_LOCK_CHECK(conn, fsp, &aio_ex->lock)) {
484 0 : TALLOC_FREE(aio_ex);
485 0 : return NT_STATUS_FILE_LOCK_CONFLICT;
486 : }
487 :
488 423 : aio_ex->nbyte = in_data.length;
489 423 : aio_ex->offset = in_offset;
490 :
491 824 : req = pwrite_fsync_send(aio_ex, fsp->conn->sconn->ev_ctx, fsp,
492 423 : in_data.data, in_data.length, in_offset,
493 : write_through);
494 423 : if (req == NULL) {
495 0 : DEBUG(3, ("smb2: SMB_VFS_PWRITE_SEND failed. "
496 : "Error %s\n", strerror(errno)));
497 0 : TALLOC_FREE(aio_ex);
498 0 : return NT_STATUS_RETRY;
499 : }
500 423 : tevent_req_set_callback(req, aio_pwrite_smb2_done, aio_ex);
501 :
502 423 : if (!aio_add_req_to_fsp(fsp, req)) {
503 0 : DEBUG(1, ("Could not add req to fsp\n"));
504 0 : TALLOC_FREE(aio_ex);
505 0 : return NT_STATUS_RETRY;
506 : }
507 :
508 : /* We don't need talloc_move here as both aio_ex and
509 : * smbreq are children of smbreq->smb2req. */
510 423 : aio_ex->smbreq = smbreq;
511 423 : smbreq->async_priv = aio_ex;
512 :
513 : /* This should actually be improved to span the write. */
514 423 : contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_WRITE);
515 423 : contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_WRITE);
516 :
517 : /*
518 : * We don't want to do write behind due to ownership
519 : * issues of the request structs. Maybe add it if I
520 : * figure those out. JRA.
521 : */
522 :
523 423 : DEBUG(10,("smb2: scheduled aio_write for file "
524 : "%s, offset %.0f, len = %u (mid = %u)\n",
525 : fsp_str_dbg(fsp),
526 : (double)in_offset,
527 : (unsigned int)in_data.length,
528 : (unsigned int)aio_ex->smbreq->mid));
529 :
530 423 : return NT_STATUS_OK;
531 : }
532 :
533 423 : static void aio_pwrite_smb2_done(struct tevent_req *req)
534 : {
535 423 : struct aio_extra *aio_ex = tevent_req_callback_data(
536 : req, struct aio_extra);
537 423 : ssize_t numtowrite = aio_ex->nbyte;
538 423 : struct tevent_req *subreq = aio_ex->smbreq->smb2req->subreq;
539 423 : files_struct *fsp = aio_ex->fsp;
540 : NTSTATUS status;
541 : ssize_t nwritten;
542 423 : int err = 0;
543 :
544 423 : nwritten = pwrite_fsync_recv(req, &err);
545 423 : TALLOC_FREE(req);
546 :
547 423 : DEBUG(10, ("pwrite_recv returned %d, err = %s\n", (int)nwritten,
548 : (nwritten == -1) ? strerror(err) : "no error"));
549 :
550 423 : mark_file_modified(fsp);
551 :
552 423 : status = smb2_write_complete_nosync(subreq, nwritten, err);
553 :
554 423 : DEBUG(10, ("smb2: scheduled aio_write completed "
555 : "for file %s, offset %.0f, requested %u, "
556 : "written = %u (errcode = %d, NTSTATUS = %s)\n",
557 : fsp_str_dbg(fsp),
558 : (double)aio_ex->offset,
559 : (unsigned int)numtowrite,
560 : (unsigned int)nwritten,
561 : err, nt_errstr(status)));
562 :
563 423 : if (!NT_STATUS_IS_OK(status)) {
564 0 : tevent_req_nterror(subreq, status);
565 0 : return;
566 : }
567 423 : tevent_req_done(subreq);
568 : }
|