smbd: do_lock does not need "blr" anymore
[amitay/samba.git] / source3 / smbd / smb2_lock.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6    Copyright (C) Jeremy Allison 2010
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
26 #include "../lib/util/tevent_ntstatus.h"
27 #include "messages.h"
28
29 struct smbd_smb2_lock_element {
30         uint64_t offset;
31         uint64_t length;
32         uint32_t flags;
33 };
34
35 struct smbd_smb2_lock_state {
36         struct smbd_smb2_request *smb2req;
37         struct smb_request *smb1req;
38         struct blocking_lock_record *blr;
39         uint16_t lock_count;
40         struct smbd_lock_element *locks;
41 };
42
43 static void remove_pending_lock(struct smbd_smb2_lock_state *state,
44                                 struct blocking_lock_record *blr);
45
46 static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
47                                                  struct tevent_context *ev,
48                                                  struct smbd_smb2_request *smb2req,
49                                                  struct files_struct *in_fsp,
50                                                  uint16_t in_lock_count,
51                                                  struct smbd_smb2_lock_element *in_locks);
52 static NTSTATUS smbd_smb2_lock_recv(struct tevent_req *req);
53
54 static void smbd_smb2_request_lock_done(struct tevent_req *subreq);
55 NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
56 {
57         const uint8_t *inbody;
58         uint16_t in_lock_count;
59         uint64_t in_file_id_persistent;
60         uint64_t in_file_id_volatile;
61         struct files_struct *in_fsp;
62         struct smbd_smb2_lock_element *in_locks;
63         struct tevent_req *subreq;
64         const uint8_t *lock_buffer;
65         uint16_t l;
66         NTSTATUS status;
67
68         status = smbd_smb2_request_verify_sizes(req, 0x30);
69         if (!NT_STATUS_IS_OK(status)) {
70                 return smbd_smb2_request_error(req, status);
71         }
72         inbody = SMBD_SMB2_IN_BODY_PTR(req);
73
74         in_lock_count                   = CVAL(inbody, 0x02);
75         /* 0x04 - 4 bytes reserved */
76         in_file_id_persistent           = BVAL(inbody, 0x08);
77         in_file_id_volatile             = BVAL(inbody, 0x10);
78
79         if (in_lock_count < 1) {
80                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
81         }
82
83         if (((in_lock_count - 1) * 0x18) > SMBD_SMB2_IN_DYN_LEN(req)) {
84                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
85         }
86
87         in_locks = talloc_array(req, struct smbd_smb2_lock_element,
88                                 in_lock_count);
89         if (in_locks == NULL) {
90                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
91         }
92
93         l = 0;
94         lock_buffer = inbody + 0x18;
95
96         in_locks[l].offset      = BVAL(lock_buffer, 0x00);
97         in_locks[l].length      = BVAL(lock_buffer, 0x08);
98         in_locks[l].flags       = IVAL(lock_buffer, 0x10);
99         /* 0x14 - 4 reserved bytes */
100
101         lock_buffer = SMBD_SMB2_IN_DYN_PTR(req);
102
103         for (l=1; l < in_lock_count; l++) {
104                 in_locks[l].offset      = BVAL(lock_buffer, 0x00);
105                 in_locks[l].length      = BVAL(lock_buffer, 0x08);
106                 in_locks[l].flags       = IVAL(lock_buffer, 0x10);
107                 /* 0x14 - 4 reserved bytes */
108
109                 lock_buffer += 0x18;
110         }
111
112         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
113         if (in_fsp == NULL) {
114                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
115         }
116
117         subreq = smbd_smb2_lock_send(req, req->sconn->ev_ctx,
118                                      req, in_fsp,
119                                      in_lock_count,
120                                      in_locks);
121         if (subreq == NULL) {
122                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
123         }
124         tevent_req_set_callback(subreq, smbd_smb2_request_lock_done, req);
125
126         return smbd_smb2_request_pending_queue(req, subreq, 500);
127 }
128
129 static void smbd_smb2_request_lock_done(struct tevent_req *subreq)
130 {
131         struct smbd_smb2_request *smb2req = tevent_req_callback_data(subreq,
132                                         struct smbd_smb2_request);
133         DATA_BLOB outbody;
134         NTSTATUS status;
135         NTSTATUS error; /* transport error */
136
137         status = smbd_smb2_lock_recv(subreq);
138         TALLOC_FREE(subreq);
139         if (!NT_STATUS_IS_OK(status)) {
140                 error = smbd_smb2_request_error(smb2req, status);
141                 if (!NT_STATUS_IS_OK(error)) {
142                         smbd_server_connection_terminate(smb2req->sconn,
143                                                          nt_errstr(error));
144                         return;
145                 }
146                 return;
147         }
148
149         outbody = smbd_smb2_generate_outbody(smb2req, 0x04);
150         if (outbody.data == NULL) {
151                 error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY);
152                 if (!NT_STATUS_IS_OK(error)) {
153                         smbd_server_connection_terminate(smb2req->sconn,
154                                                          nt_errstr(error));
155                         return;
156                 }
157                 return;
158         }
159
160         SSVAL(outbody.data, 0x00, 0x04);        /* struct size */
161         SSVAL(outbody.data, 0x02, 0);           /* reserved */
162
163         error = smbd_smb2_request_done(smb2req, outbody, NULL);
164         if (!NT_STATUS_IS_OK(error)) {
165                 smbd_server_connection_terminate(smb2req->sconn,
166                                                  nt_errstr(error));
167                 return;
168         }
169 }
170
171 static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
172                                                  struct tevent_context *ev,
173                                                  struct smbd_smb2_request *smb2req,
174                                                  struct files_struct *fsp,
175                                                  uint16_t in_lock_count,
176                                                  struct smbd_smb2_lock_element *in_locks)
177 {
178         struct tevent_req *req;
179         struct smbd_smb2_lock_state *state;
180         struct smb_request *smb1req;
181         int32_t timeout = -1;
182         bool isunlock = false;
183         uint16_t i;
184         struct smbd_lock_element *locks;
185         NTSTATUS status;
186         bool async = false;
187
188         req = tevent_req_create(mem_ctx, &state,
189                         struct smbd_smb2_lock_state);
190         if (req == NULL) {
191                 return NULL;
192         }
193         state->smb2req = smb2req;
194         smb2req->subreq = req; /* So we can find this when going async. */
195
196         smb1req = smbd_smb2_fake_smb_request(smb2req);
197         if (tevent_req_nomem(smb1req, req)) {
198                 return tevent_req_post(req, ev);
199         }
200         state->smb1req = smb1req;
201
202         DEBUG(10,("smbd_smb2_lock_send: %s - %s\n",
203                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
204
205         locks = talloc_array(state, struct smbd_lock_element, in_lock_count);
206         if (locks == NULL) {
207                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
208                 return tevent_req_post(req, ev);
209         }
210
211         switch (in_locks[0].flags) {
212         case SMB2_LOCK_FLAG_SHARED:
213         case SMB2_LOCK_FLAG_EXCLUSIVE:
214                 if (in_lock_count > 1) {
215                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
216                         return tevent_req_post(req, ev);
217                 }
218                 timeout = -1;
219                 break;
220
221         case SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
222         case SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
223                 timeout = 0;
224                 break;
225
226         case SMB2_LOCK_FLAG_UNLOCK:
227                 /* only the first lock gives the UNLOCK bit - see
228                    MS-SMB2 3.3.5.14 */
229                 isunlock = true;
230                 timeout = 0;
231                 break;
232
233         default:
234                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
235                 return tevent_req_post(req, ev);
236         }
237
238         if (!isunlock && (in_lock_count > 1)) {
239
240                 /*
241                  * 3.3.5.14.2 says we SHOULD fail with INVALID_PARAMETER if we
242                  * have more than one lock and one of those is blocking.
243                  */
244
245                 for (i=0; i<in_lock_count; i++) {
246                         uint32_t flags = in_locks[i].flags;
247
248                         if ((flags & SMB2_LOCK_FLAG_FAIL_IMMEDIATELY) == 0) {
249                                 tevent_req_nterror(
250                                         req, NT_STATUS_INVALID_PARAMETER);
251                                 return tevent_req_post(req, ev);
252                         }
253                 }
254         }
255
256         for (i=0; i<in_lock_count; i++) {
257                 bool invalid = false;
258
259                 switch (in_locks[i].flags) {
260                 case SMB2_LOCK_FLAG_SHARED:
261                 case SMB2_LOCK_FLAG_EXCLUSIVE:
262                         if (isunlock) {
263                                 invalid = true;
264                                 break;
265                         }
266                         break;
267
268                 case SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
269                 case SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
270                         if (isunlock) {
271                                 invalid = true;
272                         }
273                         break;
274
275                 case SMB2_LOCK_FLAG_UNLOCK:
276                         if (!isunlock) {
277                                 tevent_req_nterror(req,
278                                                    NT_STATUS_INVALID_PARAMETER);
279                                 return tevent_req_post(req, ev);
280                         }
281                         break;
282
283                 default:
284                         if (isunlock) {
285                                 /*
286                                  * If the first element was a UNLOCK
287                                  * we need to defer the error response
288                                  * to the backend, because we need to process
289                                  * all unlock elements before
290                                  */
291                                 invalid = true;
292                                 break;
293                         }
294                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
295                         return tevent_req_post(req, ev);
296                 }
297
298                 locks[i].smblctx = fsp->op->global->open_persistent_id;
299                 locks[i].offset = in_locks[i].offset;
300                 locks[i].count  = in_locks[i].length;
301
302                 if (in_locks[i].flags & SMB2_LOCK_FLAG_EXCLUSIVE) {
303                         locks[i].brltype = WRITE_LOCK;
304                 } else if (in_locks[i].flags & SMB2_LOCK_FLAG_SHARED) {
305                         locks[i].brltype = READ_LOCK;
306                 } else if (invalid) {
307                         /*
308                          * this is an invalid UNLOCK element
309                          * and the backend needs to test for
310                          * brltype != UNLOCK_LOCK and return
311                          * NT_STATUS_INVALID_PARAMER
312                          */
313                         locks[i].brltype = READ_LOCK;
314                 } else {
315                         locks[i].brltype = UNLOCK_LOCK;
316                 }
317
318                 DEBUG(10,("smbd_smb2_lock_send: index %d offset=%llu, count=%llu, "
319                         "smblctx = %llu type %d\n",
320                         i,
321                         (unsigned long long)locks[i].offset,
322                         (unsigned long long)locks[i].count,
323                         (unsigned long long)locks[i].smblctx,
324                         (int)locks[i].brltype ));
325         }
326
327         state->locks = locks;
328         state->lock_count = in_lock_count;
329
330         if (isunlock) {
331                 status = smbd_do_unlocking(smb1req, fsp,
332                                            in_lock_count, locks);
333                 async = false;
334         } else {
335                 status = smbd_do_locking(smb1req, fsp,
336                                          0,
337                                          timeout,
338                                          in_lock_count,
339                                          locks,
340                                          &async);
341         }
342         if (!NT_STATUS_IS_OK(status)) {
343                 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
344                        status = NT_STATUS_LOCK_NOT_GRANTED;
345                 }
346                 tevent_req_nterror(req, status);
347                 return tevent_req_post(req, ev);
348         }
349
350         if (async) {
351                 return req;
352         }
353
354         tevent_req_done(req);
355         return tevent_req_post(req, ev);
356 }
357
358 static NTSTATUS smbd_smb2_lock_recv(struct tevent_req *req)
359 {
360         NTSTATUS status;
361
362         if (tevent_req_is_nterror(req, &status)) {
363                 tevent_req_received(req);
364                 return status;
365         }
366
367         tevent_req_received(req);
368         return NT_STATUS_OK;
369 }
370
371 /****************************************************************
372  Cancel an outstanding blocking lock request.
373 *****************************************************************/
374
375 static bool smbd_smb2_lock_cancel(struct tevent_req *req)
376 {
377         struct smbd_smb2_request *smb2req = NULL;
378         struct smbd_smb2_lock_state *state = tevent_req_data(req,
379                                 struct smbd_smb2_lock_state);
380         if (!state) {
381                 return false;
382         }
383
384         if (!state->smb2req) {
385                 return false;
386         }
387
388         smb2req = state->smb2req;
389
390         remove_pending_lock(state, state->blr);
391         tevent_req_defer_callback(req, smb2req->sconn->ev_ctx);
392
393         /*
394          * If the request is canceled because of logoff, tdis or close
395          * the status is NT_STATUS_RANGE_NOT_LOCKED instead of
396          * NT_STATUS_CANCELLED.
397          *
398          * Note that the close case is handled in
399          * cancel_pending_lock_requests_by_fid_smb2(SHUTDOWN_CLOSE)
400          * for now.
401          */
402         if (!NT_STATUS_IS_OK(smb2req->session->status)) {
403                 tevent_req_nterror(req, NT_STATUS_RANGE_NOT_LOCKED);
404                 return true;
405         }
406
407         if (!NT_STATUS_IS_OK(smb2req->tcon->status)) {
408                 tevent_req_nterror(req, NT_STATUS_RANGE_NOT_LOCKED);
409                 return true;
410         }
411
412         tevent_req_nterror(req, NT_STATUS_CANCELLED);
413         return true;
414 }
415
416 /****************************************************************
417  Got a message saying someone unlocked a file. Re-schedule all
418  blocking lock requests as we don't know if anything overlapped.
419 *****************************************************************/
420
421 static void received_unlock_msg(struct messaging_context *msg,
422                                 void *private_data,
423                                 uint32_t msg_type,
424                                 struct server_id server_id,
425                                 DATA_BLOB *data)
426 {
427         struct smbd_server_connection *sconn =
428                 talloc_get_type_abort(private_data,
429                 struct smbd_server_connection);
430
431         DEBUG(10,("received_unlock_msg (SMB2)\n"));
432
433         process_blocking_lock_queue_smb2(sconn, timeval_current());
434 }
435
436 /****************************************************************
437  Function to get the blr on a pending record.
438 *****************************************************************/
439
440 struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req)
441 {
442         struct smbd_smb2_lock_state *state = NULL;
443         const uint8_t *inhdr;
444
445         if (!smb2req) {
446                 return NULL;
447         }
448         if (smb2req->subreq == NULL) {
449                 return NULL;
450         }
451         if (!tevent_req_is_in_progress(smb2req->subreq)) {
452                 return NULL;
453         }
454         inhdr = SMBD_SMB2_IN_HDR_PTR(smb2req);
455         if (SVAL(inhdr, SMB2_HDR_OPCODE) != SMB2_OP_LOCK) {
456                 return NULL;
457         }
458         state = tevent_req_data(smb2req->subreq,
459                         struct smbd_smb2_lock_state);
460         if (!state) {
461                 return NULL;
462         }
463         return state->blr;
464 }
465 /****************************************************************
466  Set up the next brl timeout.
467 *****************************************************************/
468
469 static bool recalc_smb2_brl_timeout(struct smbd_server_connection *sconn)
470 {
471         struct smbd_smb2_request *smb2req;
472         struct timeval next_timeout = timeval_zero();
473         int max_brl_timeout = lp_parm_int(-1, "brl", "recalctime", 5);
474
475         TALLOC_FREE(sconn->smb2.locks.brl_timeout);
476
477         for (smb2req = sconn->smb2.requests; smb2req; smb2req = smb2req->next) {
478                 struct blocking_lock_record *blr =
479                         get_pending_smb2req_blr(smb2req);
480                 if (!blr) {
481                         continue;
482                 }
483                 if (timeval_is_zero(&blr->expire_time)) {
484                         /*
485                          * If we're blocked on pid 0xFFFFFFFFFFFFFFFFLL this is
486                          * a POSIX lock, so calculate a timeout of
487                          * 10 seconds into the future.
488                          */
489                         if (blr->blocking_smblctx == 0xFFFFFFFFFFFFFFFFLL) {
490                                 struct timeval psx_to = timeval_current_ofs(10, 0);
491                                 next_timeout = timeval_brl_min(&next_timeout, &psx_to);
492                         }
493
494                         continue;
495                 }
496
497                 next_timeout = timeval_brl_min(&next_timeout, &blr->expire_time);
498         }
499
500         if (timeval_is_zero(&next_timeout)) {
501                 DEBUG(10, ("recalc_smb2_brl_timeout:Next "
502                         "timeout = Infinite.\n"));
503                 return true;
504         }
505
506         /*
507          * To account for unclean shutdowns by clients we need a
508          * maximum timeout that we use for checking pending locks. If
509          * we have any pending locks at all, then check if the pending
510          * lock can continue at least every brl:recalctime seconds
511          * (default 5 seconds).
512          *
513          * This saves us needing to do a message_send_all() in the
514          * SIGCHLD handler in the parent daemon. That
515          * message_send_all() caused O(n^2) work to be done when IP
516          * failovers happened in clustered Samba, which could make the
517          * entire system unusable for many minutes.
518          */
519
520         if (max_brl_timeout > 0) {
521                 struct timeval min_to = timeval_current_ofs(max_brl_timeout, 0);
522                 next_timeout = timeval_brl_min(&next_timeout, &min_to);
523         }
524
525         if (DEBUGLVL(10)) {
526                 struct timeval cur, from_now;
527
528                 cur = timeval_current();
529                 from_now = timeval_until(&cur, &next_timeout);
530                 DEBUG(10, ("recalc_smb2_brl_timeout: Next "
531                         "timeout = %d.%d seconds from now.\n",
532                         (int)from_now.tv_sec, (int)from_now.tv_usec));
533         }
534
535         sconn->smb2.locks.brl_timeout = tevent_add_timer(
536                                 sconn->ev_ctx,
537                                 NULL,
538                                 next_timeout,
539                                 brl_timeout_fn,
540                                 sconn);
541         if (!sconn->smb2.locks.brl_timeout) {
542                 return false;
543         }
544         return true;
545 }
546
547 /****************************************************************
548  Get an SMB2 lock request to go async. lock_timeout should
549  always be -1 here.
550 *****************************************************************/
551
552 bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
553                                 struct smb_request *smb1req,
554                                 files_struct *fsp,
555                                 int lock_timeout,
556                                 int lock_num,
557                                 uint64_t smblctx,
558                                 enum brl_type lock_type,
559                                 enum brl_flavour lock_flav,
560                                 uint64_t offset,
561                                 uint64_t count,
562                                 uint64_t blocking_smblctx)
563 {
564         struct smbd_server_connection *sconn = smb1req->sconn;
565         struct smbd_smb2_request *smb2req = smb1req->smb2req;
566         struct tevent_req *req = NULL;
567         struct smbd_smb2_lock_state *state = NULL;
568         struct blocking_lock_record *blr = NULL;
569         NTSTATUS status = NT_STATUS_OK;
570
571         if (!smb2req) {
572                 return false;
573         }
574         req = smb2req->subreq;
575         if (!req) {
576                 return false;
577         }
578         if (!tevent_req_is_in_progress(smb2req->subreq)) {
579                 return false;
580         }
581         state = tevent_req_data(req, struct smbd_smb2_lock_state);
582         if (!state) {
583                 return false;
584         }
585
586         blr = talloc_zero(state, struct blocking_lock_record);
587         if (!blr) {
588                 return false;
589         }
590         blr->fsp = fsp;
591
592         if (lock_timeout == -1) {
593                 blr->expire_time.tv_sec = 0;
594                 blr->expire_time.tv_usec = 0; /* Never expire. */
595         } else {
596                 blr->expire_time = timeval_current_ofs_msec(lock_timeout);
597         }
598
599         blr->lock_num = lock_num;
600         blr->smblctx = smblctx;
601         blr->blocking_smblctx = blocking_smblctx;
602         blr->lock_flav = lock_flav;
603         blr->lock_type = lock_type;
604         blr->offset = offset;
605         blr->count = count;
606
607         /* Specific brl_lock() implementations can fill this in. */
608         blr->blr_private = NULL;
609
610         /* Add a pending lock record for this. */
611         status = brl_lock(sconn->msg_ctx,
612                         br_lck,
613                         smblctx,
614                         messaging_server_id(sconn->msg_ctx),
615                         offset,
616                         count,
617                         lock_type == READ_LOCK ? PENDING_READ_LOCK : PENDING_WRITE_LOCK,
618                         blr->lock_flav,
619                         true,
620                         NULL);
621
622         if (!NT_STATUS_IS_OK(status)) {
623                 DEBUG(0,("push_blocking_lock_request_smb2: "
624                         "failed to add PENDING_LOCK record.\n"));
625                 TALLOC_FREE(blr);
626                 return false;
627         }
628         state->blr = blr;
629
630         DEBUG(10,("push_blocking_lock_request_smb2: file %s timeout %d\n",
631                 fsp_str_dbg(fsp),
632                 lock_timeout ));
633
634         recalc_smb2_brl_timeout(sconn);
635
636         /* Ensure we'll receive messages when this is unlocked. */
637         if (!sconn->smb2.locks.blocking_lock_unlock_state) {
638                 messaging_register(sconn->msg_ctx, sconn,
639                                 MSG_SMB_UNLOCK, received_unlock_msg);
640                 sconn->smb2.locks.blocking_lock_unlock_state = true;
641         }
642
643         /* allow this request to be canceled */
644         tevent_req_set_cancel_fn(req, smbd_smb2_lock_cancel);
645
646         return true;
647 }
648
649 /****************************************************************
650  Remove a pending lock record under lock.
651 *****************************************************************/
652
653 static void remove_pending_lock(struct smbd_smb2_lock_state *state,
654                         struct blocking_lock_record *blr)
655 {
656         struct byte_range_lock *br_lck = brl_get_locks(
657                                 state, blr->fsp);
658
659         DEBUG(10, ("remove_pending_lock: BLR = %p\n", blr));
660
661         if (br_lck) {
662                 brl_lock_cancel(br_lck,
663                                 blr->smblctx,
664                                 messaging_server_id(blr->fsp->conn->sconn->msg_ctx),
665                                 blr->offset,
666                                 blr->count,
667                                 blr->lock_flav,
668                                 blr);
669                 TALLOC_FREE(br_lck);
670         }
671 }
672
673 /****************************************************************
674  Re-proccess a blocking lock request.
675  This is equivalent to process_lockingX() inside smbd/blocking.c
676 *****************************************************************/
677
678 static void reprocess_blocked_smb2_lock(struct smbd_smb2_request *smb2req,
679                                 struct timeval tv_curr)
680 {
681         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
682         struct blocking_lock_record *blr = NULL;
683         struct smbd_smb2_lock_state *state = NULL;
684         struct byte_range_lock *br_lck = NULL;
685         struct smbd_lock_element *e = NULL;
686         files_struct *fsp = NULL;
687
688         if (!smb2req->subreq) {
689                 return;
690         }
691         state = tevent_req_data(smb2req->subreq, struct smbd_smb2_lock_state);
692         if (!state) {
693                 return;
694         }
695
696         blr = state->blr;
697         fsp = blr->fsp;
698
699         /* We can only have one blocked lock in SMB2. */
700         SMB_ASSERT(state->lock_count == 1);
701         SMB_ASSERT(blr->lock_num == 0);
702
703         /* Try and get the outstanding lock. */
704         e = &state->locks[blr->lock_num];
705
706         br_lck = do_lock(fsp->conn->sconn->msg_ctx,
707                         fsp,
708                         e->smblctx,
709                         e->count,
710                         e->offset,
711                         e->brltype,
712                         WINDOWS_LOCK,
713                         true,
714                         &status,
715                         &blr->blocking_smblctx);
716
717         TALLOC_FREE(br_lck);
718
719         if (NT_STATUS_IS_OK(status)) {
720                 /*
721                  * Success - we got the lock.
722                  */
723
724                 DEBUG(3,("reprocess_blocked_smb2_lock SUCCESS file = %s, "
725                         "%s, num_locks=%d\n",
726                         fsp_str_dbg(fsp),
727                         fsp_fnum_dbg(fsp),
728                         (int)state->lock_count));
729
730                 remove_pending_lock(state, blr);
731                 tevent_req_done(smb2req->subreq);
732                 return;
733         }
734
735         if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
736                         !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) {
737                 /*
738                  * We have other than a "can't get lock"
739                  * error. Return an error.
740                  */
741                 remove_pending_lock(state, blr);
742                 tevent_req_nterror(smb2req->subreq, status);
743                 return;
744         }
745
746         /*
747          * We couldn't get the lock for this record.
748          * If the time has expired, return a lock error.
749          */
750
751         if (!timeval_is_zero(&blr->expire_time) &&
752                         timeval_compare(&blr->expire_time, &tv_curr) <= 0) {
753                 remove_pending_lock(state, blr);
754                 tevent_req_nterror(smb2req->subreq, NT_STATUS_LOCK_NOT_GRANTED);
755                 return;
756         }
757
758         /*
759          * Still can't get the lock - keep waiting.
760          */
761
762         DEBUG(10,("reprocess_blocked_smb2_lock: failed to get lock "
763                 "for file %s, %s. Still waiting....\n",
764                 fsp_str_dbg(fsp),
765                 fsp_fnum_dbg(fsp)));
766
767         return;
768 }
769
770 /****************************************************************
771  Attempt to proccess all outstanding blocking locks pending on
772  the request queue.
773 *****************************************************************/
774
775 void process_blocking_lock_queue_smb2(
776         struct smbd_server_connection *sconn, struct timeval tv_curr)
777 {
778         struct smbd_smb2_request *smb2req, *nextreq;
779
780         for (smb2req = sconn->smb2.requests; smb2req; smb2req = nextreq) {
781                 const uint8_t *inhdr;
782
783                 nextreq = smb2req->next;
784
785                 if (smb2req->subreq == NULL) {
786                         /* This message has been processed. */
787                         continue;
788                 }
789                 if (!tevent_req_is_in_progress(smb2req->subreq)) {
790                         /* This message has been processed. */
791                         continue;
792                 }
793
794                 inhdr = SMBD_SMB2_IN_HDR_PTR(smb2req);
795                 if (SVAL(inhdr, SMB2_HDR_OPCODE) == SMB2_OP_LOCK) {
796                         reprocess_blocked_smb2_lock(smb2req, tv_curr);
797                 }
798         }
799
800         recalc_smb2_brl_timeout(sconn);
801 }
802
803 /****************************************************************************
804  Remove any locks on this fd. Called from file_close().
805 ****************************************************************************/
806
807 void cancel_pending_lock_requests_by_fid_smb2(files_struct *fsp,
808                         struct byte_range_lock *br_lck,
809                         enum file_close_type close_type)
810 {
811         struct smbd_server_connection *sconn = fsp->conn->sconn;
812         struct smbd_smb2_request *smb2req, *nextreq;
813
814         for (smb2req = sconn->smb2.requests; smb2req; smb2req = nextreq) {
815                 struct smbd_smb2_lock_state *state = NULL;
816                 files_struct *fsp_curr = NULL;
817                 struct blocking_lock_record *blr = NULL;
818                 const uint8_t *inhdr;
819
820                 nextreq = smb2req->next;
821
822                 if (smb2req->subreq == NULL) {
823                         /* This message has been processed. */
824                         continue;
825                 }
826                 if (!tevent_req_is_in_progress(smb2req->subreq)) {
827                         /* This message has been processed. */
828                         continue;
829                 }
830
831                 inhdr = SMBD_SMB2_IN_HDR_PTR(smb2req);
832                 if (SVAL(inhdr, SMB2_HDR_OPCODE) != SMB2_OP_LOCK) {
833                         /* Not a lock call. */
834                         continue;
835                 }
836
837                 state = tevent_req_data(smb2req->subreq,
838                                 struct smbd_smb2_lock_state);
839                 if (!state) {
840                         /* Strange - is this even possible ? */
841                         continue;
842                 }
843
844                 fsp_curr = smb2req->compat_chain_fsp;
845                 if (fsp_curr == NULL) {
846                         /* Strange - is this even possible ? */
847                         continue;
848                 }
849
850                 if (fsp_curr != fsp) {
851                         /* It's not our fid */
852                         continue;
853                 }
854
855                 blr = state->blr;
856
857                 /* Remove the entries from the lock db. */
858                 brl_lock_cancel(br_lck,
859                                 blr->smblctx,
860                                 messaging_server_id(sconn->msg_ctx),
861                                 blr->offset,
862                                 blr->count,
863                                 blr->lock_flav,
864                                 blr);
865
866                 /* Finally end the request. */
867                 if (close_type == SHUTDOWN_CLOSE) {
868                         tevent_req_done(smb2req->subreq);
869                 } else {
870                         tevent_req_nterror(smb2req->subreq,
871                                 NT_STATUS_RANGE_NOT_LOCKED);
872                 }
873         }
874 }