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