s3:blocking: do the timeout calculation before calling dbwrap_watched_watch_send()
[metze/samba/wip.git] / source3 / smbd / blocking.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Blocking Locking functions
4    Copyright (C) Jeremy Allison 1998-2003
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "smbd/smbd.h"
22 #include "smbd/globals.h"
23 #include "messages.h"
24 #include "lib/util/tevent_ntstatus.h"
25 #include "lib/dbwrap/dbwrap_watch.h"
26 #include "librpc/gen_ndr/ndr_open_files.h"
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_LOCKING
30
31 NTSTATUS smbd_do_locks_try(
32         struct files_struct *fsp,
33         enum brl_flavour lock_flav,
34         uint16_t num_locks,
35         struct smbd_lock_element *locks,
36         uint16_t *blocker_idx,
37         struct server_id *blocking_pid,
38         uint64_t *blocking_smblctx)
39 {
40         NTSTATUS status = NT_STATUS_OK;
41         uint16_t i;
42
43         for (i=0; i<num_locks; i++) {
44                 struct smbd_lock_element *e = &locks[i];
45
46                 status = do_lock(
47                         fsp,
48                         e->smblctx,
49                         e->count,
50                         e->offset,
51                         e->brltype,
52                         lock_flav,
53                         blocking_pid,
54                         blocking_smblctx);
55                 if (!NT_STATUS_IS_OK(status)) {
56                         break;
57                 }
58         }
59
60         if (NT_STATUS_IS_OK(status)) {
61                 return NT_STATUS_OK;
62         }
63
64         *blocker_idx = i;
65
66         /*
67          * Undo the locks we successfully got
68          */
69         for (i = i-1; i != UINT16_MAX; i--) {
70                 struct smbd_lock_element *e = &locks[i];
71                 do_unlock(fsp,
72                           e->smblctx,
73                           e->count,
74                           e->offset,
75                           lock_flav);
76         }
77
78         return status;
79 }
80
81 static bool smbd_smb1_fsp_add_blocked_lock_req(
82         struct files_struct *fsp, struct tevent_req *req)
83 {
84         size_t num_reqs = talloc_array_length(fsp->blocked_smb1_lock_reqs);
85         struct tevent_req **tmp = NULL;
86
87         tmp = talloc_realloc(
88                 fsp,
89                 fsp->blocked_smb1_lock_reqs,
90                 struct tevent_req *,
91                 num_reqs+1);
92         if (tmp == NULL) {
93                 return false;
94         }
95         fsp->blocked_smb1_lock_reqs = tmp;
96         fsp->blocked_smb1_lock_reqs[num_reqs] = req;
97         return true;
98 }
99
100 struct smbd_smb1_do_locks_state {
101         struct tevent_context *ev;
102         struct smb_request *smbreq;
103         struct files_struct *fsp;
104         uint32_t timeout;
105         uint32_t polling_msecs;
106         struct timeval endtime;
107         bool large_offset;      /* required for correct cancel */
108         enum brl_flavour lock_flav;
109         uint16_t num_locks;
110         struct smbd_lock_element *locks;
111         uint16_t blocker;
112         NTSTATUS deny_status;
113 };
114
115 static void smbd_smb1_do_locks_try(struct tevent_req *req);
116 static void smbd_smb1_do_locks_retry(struct tevent_req *subreq);
117 static void smbd_smb1_blocked_locks_cleanup(
118         struct tevent_req *req, enum tevent_req_state req_state);
119
120 static void smbd_smb1_do_locks_setup_timeout(
121         struct smbd_smb1_do_locks_state *state,
122         const struct smbd_lock_element *blocker)
123 {
124         struct files_struct *fsp = state->fsp;
125
126         if (!timeval_is_zero(&state->endtime)) {
127                 /*
128                  * already done
129                  */
130                 return;
131         }
132
133         if ((state->timeout != 0) && (state->timeout != UINT32_MAX)) {
134                 /*
135                  * Windows internal resolution for blocking locks
136                  * seems to be about 200ms... Don't wait for less than
137                  * that. JRA.
138                  */
139                 state->timeout = MAX(state->timeout, lp_lock_spin_time());
140         }
141
142         if (state->timeout != 0) {
143                 goto set_endtime;
144         }
145
146         if (blocker == NULL) {
147                 goto set_endtime;
148         }
149
150         if ((blocker->offset >= 0xEF000000) &&
151             ((blocker->offset >> 63) == 0)) {
152                 /*
153                  * This must be an optimization of an ancient
154                  * application bug...
155                  */
156                 state->timeout = lp_lock_spin_time();
157         }
158
159         if ((fsp->lock_failure_seen) &&
160             (blocker->offset == fsp->lock_failure_offset)) {
161                 /*
162                  * Delay repeated lock attempts on the same
163                  * lock. Maybe a more advanced version of the
164                  * above check?
165                  */
166                 DBG_DEBUG("Delaying lock request due to previous "
167                           "failure\n");
168                 state->timeout = lp_lock_spin_time();
169         }
170
171 set_endtime:
172         /*
173          * Note state->timeout might still 0,
174          * but that's ok, as we don't want to retry
175          * in that case.
176          */
177         state->endtime = timeval_add(&state->smbreq->request_time,
178                                      state->timeout / 1000,
179                                      (state->timeout % 1000) * 1000);
180 }
181
182 static void smbd_smb1_do_locks_update_polling_msecs(
183         struct smbd_smb1_do_locks_state *state)
184 {
185         /*
186          * The default lp_lock_spin_time() is 200ms.
187          *
188          * v_min is in the range of 0.002 to 20 secs
189          * (0.2 secs by default)
190          *
191          * v_max is in the range of 0.02 to 200 secs
192          * (2.0 secs by default)
193          *
194          * The typical steps are:
195          * 0.2, 0.4, 0.6, 0.8, ... 2.0
196          */
197         uint32_t v_min = MAX(2, MIN(20000, lp_lock_spin_time()));
198         uint32_t v_max = 10 * v_min;
199
200         if (state->polling_msecs >= v_max) {
201                 state->polling_msecs = v_max;
202                 return;
203         }
204
205         state->polling_msecs += v_min;
206 }
207
208 struct tevent_req *smbd_smb1_do_locks_send(
209         TALLOC_CTX *mem_ctx,
210         struct tevent_context *ev,
211         struct smb_request **smbreq, /* talloc_move()d into our state */
212         struct files_struct *fsp,
213         uint32_t lock_timeout,
214         bool large_offset,
215         enum brl_flavour lock_flav,
216         uint16_t num_locks,
217         struct smbd_lock_element *locks)
218 {
219         struct tevent_req *req = NULL, *subreq = NULL;
220         struct smbd_smb1_do_locks_state *state = NULL;
221         struct share_mode_lock *lck = NULL;
222         struct server_id blocking_pid = { 0 };
223         uint64_t blocking_smblctx = 0;
224         struct timeval endtime = { 0 };
225         NTSTATUS status = NT_STATUS_OK;
226         bool ok;
227         bool expired;
228
229         req = tevent_req_create(
230                 mem_ctx, &state, struct smbd_smb1_do_locks_state);
231         if (req == NULL) {
232                 return NULL;
233         }
234         state->ev = ev;
235         state->smbreq = talloc_move(state, smbreq);
236         state->fsp = fsp;
237         state->timeout = lock_timeout;
238         state->large_offset = large_offset;
239         state->lock_flav = lock_flav;
240         state->num_locks = num_locks;
241         state->locks = locks;
242
243         if (lock_flav == POSIX_LOCK) {
244                 /*
245                  * SMB1 posix locks always use
246                  * NT_STATUS_FILE_LOCK_CONFLICT.
247                  */
248                 state->deny_status = NT_STATUS_FILE_LOCK_CONFLICT;
249         } else {
250                 state->deny_status = NT_STATUS_LOCK_NOT_GRANTED;
251         }
252
253         DBG_DEBUG("state=%p, state->smbreq=%p\n", state, state->smbreq);
254
255         if (num_locks == 0) {
256                 DBG_DEBUG("no locks\n");
257                 tevent_req_done(req);
258                 return tevent_req_post(req, ev);
259         }
260
261         lck = get_existing_share_mode_lock(state, state->fsp->file_id);
262         if (tevent_req_nomem(lck, req)) {
263                 DBG_DEBUG("Could not get share mode lock\n");
264                 return tevent_req_post(req, ev);
265         }
266
267         status = smbd_do_locks_try(
268                 state->fsp,
269                 state->lock_flav,
270                 state->num_locks,
271                 state->locks,
272                 &state->blocker,
273                 &blocking_pid,
274                 &blocking_smblctx);
275         if (NT_STATUS_IS_OK(status)) {
276                 tevent_req_done(req);
277                 goto done;
278         }
279         if (!ERROR_WAS_LOCK_DENIED(status)) {
280                 tevent_req_nterror(req, status);
281                 goto done;
282         }
283
284         smbd_smb1_do_locks_setup_timeout(state, &locks[state->blocker]);
285         DBG_DEBUG("timeout=%"PRIu32", blocking_smblctx=%"PRIu64"\n",
286                   state->timeout,
287                   blocking_smblctx);
288
289         /*
290          * The client specified timeout expired
291          * avoid further retries.
292          *
293          * Otherwise keep waiting either waiting
294          * for changes in locking.tdb or the polling
295          * mode timers waiting for posix locks.
296          *
297          * If the endtime is not elapsed yet,
298          * it means we'll retry after a timeout.
299          * In that case we'll have to return
300          * NT_STATUS_FILE_LOCK_CONFLICT
301          * instead of NT_STATUS_LOCK_NOT_GRANTED.
302          */
303         expired = timeval_expired(&state->endtime);
304         if (expired) {
305                 status = state->deny_status;
306                 tevent_req_nterror(req, status);
307                 goto done;
308         }
309         state->deny_status = NT_STATUS_FILE_LOCK_CONFLICT;
310
311         endtime = state->endtime;
312
313         if (blocking_smblctx == UINT64_MAX) {
314                 struct timeval tmp;
315
316                 smbd_smb1_do_locks_update_polling_msecs(state);
317
318                 DBG_DEBUG("Blocked on a posix lock. Retry in %"PRIu32" msecs\n",
319                           state->polling_msecs);
320
321                 tmp = timeval_current_ofs_msec(state->polling_msecs);
322                 endtime = timeval_min(&endtime, &tmp);
323         }
324
325         subreq = dbwrap_watched_watch_send(
326                 state, state->ev, lck->data->record, blocking_pid);
327         if (tevent_req_nomem(subreq, req)) {
328                 goto done;
329         }
330         TALLOC_FREE(lck);
331         tevent_req_set_callback(subreq, smbd_smb1_do_locks_retry, req);
332
333         ok = tevent_req_set_endtime(subreq, state->ev, endtime);
334         if (!ok) {
335                 tevent_req_oom(req);
336                 goto done;
337         }
338
339         ok = smbd_smb1_fsp_add_blocked_lock_req(fsp, req);
340         if (!ok) {
341                 tevent_req_oom(req);
342                 goto done;
343         }
344         tevent_req_set_cleanup_fn(req, smbd_smb1_blocked_locks_cleanup);
345         return req;
346 done:
347         TALLOC_FREE(lck);
348         return tevent_req_post(req, ev);
349 }
350
351 static void smbd_smb1_blocked_locks_cleanup(
352         struct tevent_req *req, enum tevent_req_state req_state)
353 {
354         struct smbd_smb1_do_locks_state *state = tevent_req_data(
355                 req, struct smbd_smb1_do_locks_state);
356         struct files_struct *fsp = state->fsp;
357         struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
358         size_t num_blocked = talloc_array_length(blocked);
359         size_t i, num_after;
360
361         DBG_DEBUG("req=%p, state=%p, req_state=%d\n",
362                   req,
363                   state,
364                   (int)req_state);
365
366         if (req_state == TEVENT_REQ_RECEIVED) {
367                 DBG_DEBUG("already received\n");
368                 return;
369         }
370
371         for (i=0; i<num_blocked; i++) {
372                 if (blocked[i] == req) {
373                         break;
374                 }
375         }
376         SMB_ASSERT(i<num_blocked);
377
378         num_after = num_blocked - (i+1);
379
380         if (num_after > 0) {
381                 /*
382                  * The locks need to be kept in order, see
383                  * raw.lock.multilock2
384                  */
385                 memmove(&blocked[i],
386                         &blocked[i+1],
387                         sizeof(*blocked) * num_after);
388         }
389         fsp->blocked_smb1_lock_reqs = talloc_realloc(
390                 fsp, blocked, struct tevent_req *, num_blocked-1);
391 }
392
393 static void smbd_smb1_do_locks_try(struct tevent_req *req)
394 {
395         struct smbd_smb1_do_locks_state *state = tevent_req_data(
396                 req, struct smbd_smb1_do_locks_state);
397         struct files_struct *fsp = state->fsp;
398         struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
399         struct tevent_req *retry_req = blocked[0];
400         struct smbd_smb1_do_locks_state *retry_state = tevent_req_data(
401                 retry_req, struct smbd_smb1_do_locks_state);
402         struct share_mode_lock *lck;
403         struct timeval endtime = { 0 };
404         struct server_id blocking_pid = { 0 };
405         uint64_t blocking_smblctx = 0;
406         struct tevent_req *subreq = NULL;
407         NTSTATUS status;
408         bool ok;
409         bool expired;
410
411         lck = get_existing_share_mode_lock(state, fsp->file_id);
412         if (tevent_req_nomem(lck, req)) {
413                 DBG_DEBUG("Could not get share mode lock\n");
414                 return;
415         }
416
417         status = smbd_do_locks_try(
418                 fsp,
419                 retry_state->lock_flav,
420                 retry_state->num_locks,
421                 retry_state->locks,
422                 &state->blocker,
423                 &blocking_pid,
424                 &blocking_smblctx);
425         if (NT_STATUS_IS_OK(status)) {
426                 goto done;
427         }
428         if (!ERROR_WAS_LOCK_DENIED(status)) {
429                 goto done;
430         }
431
432         /*
433          * The client specified timeout expired
434          * avoid further retries.
435          *
436          * Otherwise keep waiting either waiting
437          * for changes in locking.tdb or the polling
438          * mode timers waiting for posix locks.
439          *
440          * If the endtime is not expired yet,
441          * it means we'll retry after a timeout.
442          * In that case we'll have to return
443          * NT_STATUS_FILE_LOCK_CONFLICT
444          * instead of NT_STATUS_LOCK_NOT_GRANTED.
445          */
446         expired = timeval_expired(&state->endtime);
447         if (expired) {
448                 status = state->deny_status;
449                 goto done;
450         }
451         state->deny_status = NT_STATUS_FILE_LOCK_CONFLICT;
452
453         endtime = state->endtime;
454
455         if (blocking_smblctx == UINT64_MAX) {
456                 struct timeval tmp;
457
458                 smbd_smb1_do_locks_update_polling_msecs(state);
459
460                 DBG_DEBUG("Blocked on a posix lock. Retry in %"PRIu32" msecs\n",
461                           state->polling_msecs);
462
463                 tmp = timeval_current_ofs_msec(state->polling_msecs);
464                 endtime = timeval_min(&endtime, &tmp);
465         }
466
467         subreq = dbwrap_watched_watch_send(
468                 state, state->ev, lck->data->record, blocking_pid);
469         if (tevent_req_nomem(subreq, req)) {
470                 goto done;
471         }
472         TALLOC_FREE(lck);
473         tevent_req_set_callback(subreq, smbd_smb1_do_locks_retry, req);
474
475         ok = tevent_req_set_endtime(subreq, state->ev, endtime);
476         if (!ok) {
477                 status = NT_STATUS_NO_MEMORY;
478                 goto done;
479         }
480         return;
481 done:
482         TALLOC_FREE(lck);
483         smbd_smb1_brl_finish_by_req(req, status);
484 }
485
486 static void smbd_smb1_do_locks_retry(struct tevent_req *subreq)
487 {
488         struct tevent_req *req = tevent_req_callback_data(
489                 subreq, struct tevent_req);
490         struct smbd_smb1_do_locks_state *state = tevent_req_data(
491                 req, struct smbd_smb1_do_locks_state);
492         NTSTATUS status;
493         bool ok;
494
495         /*
496          * Make sure we run as the user again
497          */
498         ok = change_to_user_by_fsp(state->fsp);
499         if (!ok) {
500                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
501                 return;
502         }
503
504         status = dbwrap_watched_watch_recv(subreq, NULL, NULL);
505         TALLOC_FREE(subreq);
506
507         DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
508                   nt_errstr(status));
509
510         /*
511          * We ignore any errors here, it's most likely
512          * we just get NT_STATUS_OK or NT_STATUS_IO_TIMEOUT.
513          *
514          * In any case we can just give it a retry.
515          */
516
517         smbd_smb1_do_locks_try(req);
518 }
519
520 NTSTATUS smbd_smb1_do_locks_recv(struct tevent_req *req)
521 {
522         struct smbd_smb1_do_locks_state *state = tevent_req_data(
523                 req, struct smbd_smb1_do_locks_state);
524         NTSTATUS status = NT_STATUS_OK;
525         bool err;
526
527         err = tevent_req_is_nterror(req, &status);
528
529         DBG_DEBUG("err=%d, status=%s\n", (int)err, nt_errstr(status));
530
531         if (tevent_req_is_nterror(req, &status)) {
532                 struct files_struct *fsp = state->fsp;
533                 struct smbd_lock_element *blocker =
534                         &state->locks[state->blocker];
535
536                 DBG_DEBUG("Setting lock_failure_offset=%"PRIu64"\n",
537                           blocker->offset);
538
539                 fsp->lock_failure_seen = true;
540                 fsp->lock_failure_offset = blocker->offset;
541                 return status;
542         }
543
544         tevent_req_received(req);
545
546         return NT_STATUS_OK;
547 }
548
549 bool smbd_smb1_do_locks_extract_smbreq(
550         struct tevent_req *req,
551         TALLOC_CTX *mem_ctx,
552         struct smb_request **psmbreq)
553 {
554         struct smbd_smb1_do_locks_state *state = tevent_req_data(
555                 req, struct smbd_smb1_do_locks_state);
556
557         DBG_DEBUG("req=%p, state=%p, state->smbreq=%p\n",
558                   req,
559                   state,
560                   state->smbreq);
561
562         if (state->smbreq == NULL) {
563                 return false;
564         }
565         *psmbreq = talloc_move(mem_ctx, &state->smbreq);
566         return true;
567 }
568
569 void smbd_smb1_brl_finish_by_req(struct tevent_req *req, NTSTATUS status)
570 {
571         DBG_DEBUG("req=%p, status=%s\n", req, nt_errstr(status));
572
573         if (NT_STATUS_IS_OK(status)) {
574                 tevent_req_done(req);
575         } else {
576                 tevent_req_nterror(req, status);
577         }
578 }
579
580 bool smbd_smb1_brl_finish_by_lock(
581         struct files_struct *fsp,
582         bool large_offset,
583         enum brl_flavour lock_flav,
584         struct smbd_lock_element lock,
585         NTSTATUS finish_status)
586 {
587         struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
588         size_t num_blocked = talloc_array_length(blocked);
589         size_t i;
590
591         DBG_DEBUG("num_blocked=%zu\n", num_blocked);
592
593         for (i=0; i<num_blocked; i++) {
594                 struct tevent_req *req = blocked[i];
595                 struct smbd_smb1_do_locks_state *state = tevent_req_data(
596                         req, struct smbd_smb1_do_locks_state);
597                 uint16_t j;
598
599                 DBG_DEBUG("i=%zu, req=%p\n", i, req);
600
601                 if ((state->large_offset != large_offset) ||
602                     (state->lock_flav != lock_flav)) {
603                         continue;
604                 }
605
606                 for (j=0; j<state->num_locks; j++) {
607                         struct smbd_lock_element *l = &state->locks[j];
608
609                         if ((lock.smblctx == l->smblctx) &&
610                             (lock.offset == l->offset) &&
611                             (lock.count == l->count)) {
612                                 smbd_smb1_brl_finish_by_req(
613                                         req, finish_status);
614                                 return true;
615                         }
616                 }
617         }
618         return false;
619 }
620
621 static struct files_struct *smbd_smb1_brl_finish_by_mid_fn(
622         struct files_struct *fsp, void *private_data)
623 {
624         struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
625         size_t num_blocked = talloc_array_length(blocked);
626         uint64_t mid = *((uint64_t *)private_data);
627         size_t i;
628
629         DBG_DEBUG("fsp=%p, num_blocked=%zu\n", fsp, num_blocked);
630
631         for (i=0; i<num_blocked; i++) {
632                 struct tevent_req *req = blocked[i];
633                 struct smbd_smb1_do_locks_state *state = tevent_req_data(
634                         req, struct smbd_smb1_do_locks_state);
635                 struct smb_request *smbreq = state->smbreq;
636
637                 if (smbreq->mid == mid) {
638                         tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
639                         return fsp;
640                 }
641         }
642
643         return NULL;
644 }
645
646 /*
647  * This walks the list of fsps, we store the blocked reqs attached to
648  * them. It can be expensive, but this is legacy SMB1 and trying to
649  * remember looking at traces I don't reall many of those calls.
650  */
651
652 bool smbd_smb1_brl_finish_by_mid(
653         struct smbd_server_connection *sconn, uint64_t mid)
654 {
655         struct files_struct *found = files_forall(
656                 sconn, smbd_smb1_brl_finish_by_mid_fn, &mid);
657         return (found != NULL);
658 }