LCOV - code coverage report
Current view: top level - source4/ntvfs/posix - pvfs_oplock.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 100 140 71.4 %
Date: 2024-04-21 15:09:00 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    POSIX NTVFS backend - oplock handling
       5             : 
       6             :    Copyright (C) Stefan Metzmacher 2008
       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 "lib/messaging/messaging.h"
      24             : #include "lib/messaging/irpc.h"
      25             : #include "system/time.h"
      26             : #include "vfs_posix.h"
      27             : 
      28             : 
      29             : struct pvfs_oplock {
      30             :         struct pvfs_file_handle *handle;
      31             :         struct pvfs_file *file;
      32             :         uint32_t level;
      33             :         struct timeval break_to_level_II;
      34             :         struct timeval break_to_none;
      35             :         struct imessaging_context *msg_ctx;
      36             : };
      37             : 
      38          86 : static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
      39             :                                              uint8_t oplock_break)
      40             : {
      41           0 :         struct odb_lock *olck;
      42           0 :         NTSTATUS status;
      43             : 
      44          86 :         if (h->fd == -1) {
      45           0 :                 return NT_STATUS_FILE_IS_A_DIRECTORY;
      46             :         }
      47             : 
      48          86 :         if (!h->have_opendb_entry) {
      49           0 :                 return NT_STATUS_FOOBAR;
      50             :         }
      51             : 
      52          86 :         if (!h->oplock) {
      53           0 :                 return NT_STATUS_FOOBAR;
      54             :         }
      55             : 
      56          86 :         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
      57          86 :         if (olck == NULL) {
      58           0 :                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
      59           0 :                 return NT_STATUS_FOOBAR;
      60             :         }
      61             : 
      62          86 :         if (oplock_break == OPLOCK_BREAK_TO_NONE) {
      63          18 :                 h->oplock->level = OPLOCK_NONE;
      64          68 :         } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
      65          68 :                 h->oplock->level = OPLOCK_LEVEL_II;
      66             :         } else {
      67             :                 /* fallback to level II in case of a invalid value */
      68           0 :                 DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
      69           0 :                 h->oplock->level = OPLOCK_LEVEL_II;
      70             :         }
      71          86 :         status = odb_update_oplock(olck, h, h->oplock->level);
      72          86 :         if (!NT_STATUS_IS_OK(status)) {
      73           0 :                 DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
      74             :                          h->name->full_name, nt_errstr(status)));
      75           0 :                 talloc_free(olck);
      76           0 :                 return status;
      77             :         }
      78             : 
      79          86 :         talloc_free(olck);
      80             : 
      81             :         /* after a break to none, we no longer have an oplock attached */
      82          86 :         if (h->oplock->level == OPLOCK_NONE) {
      83          18 :                 talloc_free(h->oplock);
      84          18 :                 h->oplock = NULL;
      85             :         }
      86             : 
      87          86 :         return NT_STATUS_OK;
      88             : }
      89             : 
      90             : /*
      91             :   receive oplock breaks and forward them to the client
      92             : */
      93         108 : static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
      94             : {
      95           0 :         NTSTATUS status;
      96         108 :         struct pvfs_file *f = opl->file;
      97         108 :         struct pvfs_file_handle *h = opl->handle;
      98         108 :         struct pvfs_state *pvfs = h->pvfs;
      99         108 :         struct timeval cur = timeval_current();
     100         108 :         struct timeval *last = NULL;
     101           0 :         struct timeval end;
     102             : 
     103         108 :         switch (level) {
     104          75 :         case OPLOCK_BREAK_TO_LEVEL_II:
     105          75 :                 last = &opl->break_to_level_II;
     106          75 :                 break;
     107          33 :         case OPLOCK_BREAK_TO_NONE:
     108          33 :                 last = &opl->break_to_none;
     109          33 :                 break;
     110             :         }
     111             : 
     112         108 :         if (!last) {
     113           0 :                 DEBUG(0,("%s: got unexpected level[0x%02X]\n",
     114             :                         __FUNCTION__, level));
     115         107 :                 return;
     116             :         }
     117             : 
     118         108 :         if (timeval_is_zero(last)) {
     119             :                 /*
     120             :                  * this is the first break we for this level
     121             :                  * remember the time
     122             :                  */
     123         104 :                 *last = cur;
     124             : 
     125         104 :                 DEBUG(5,("%s: sending oplock break level %d for '%s' %p\n",
     126             :                         __FUNCTION__, level, h->name->original_name, h));
     127         104 :                 status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
     128         104 :                 if (!NT_STATUS_IS_OK(status)) {
     129           0 :                         DEBUG(0,("%s: sending oplock break failed: %s\n",
     130             :                                 __FUNCTION__, nt_errstr(status)));
     131             :                 }
     132         104 :                 return;
     133             :         }
     134             : 
     135           4 :         end = timeval_add(last, pvfs->oplock_break_timeout, 0);
     136             : 
     137           4 :         if (timeval_compare(&cur, &end) < 0) {
     138             :                 /*
     139             :                  * If it's not expired just ignore the break
     140             :                  * as we already sent the break request to the client
     141             :                  */
     142           3 :                 DEBUG(0,("%s: do not resend oplock break level %d for '%s' %p\n",
     143             :                         __FUNCTION__, level, h->name->original_name, h));
     144           3 :                 return;
     145             :         }
     146             : 
     147             :         /*
     148             :          * If the client did not send a release within the
     149             :          * oplock break timeout time frame we auto release
     150             :          * the oplock
     151             :          */
     152           1 :         DEBUG(0,("%s: auto release oplock level %d for '%s' %p\n",
     153             :                 __FUNCTION__, level, h->name->original_name, h));
     154           1 :         status = pvfs_oplock_release_internal(h, level);
     155           1 :         if (!NT_STATUS_IS_OK(status)) {
     156           0 :                 DEBUG(0,("%s: failed to auto release the oplock[0x%02X]: %s\n",
     157             :                         __FUNCTION__, level, nt_errstr(status)));
     158             :         }
     159             : }
     160             : 
     161         108 : static void pvfs_oplock_break_dispatch(struct imessaging_context *msg,
     162             :                                        void *private_data,
     163             :                                        uint32_t msg_type,
     164             :                                        struct server_id src,
     165             :                                        size_t num_fds,
     166             :                                        int *fds,
     167             :                                        DATA_BLOB *data)
     168             : {
     169         108 :         struct pvfs_oplock *opl = talloc_get_type(private_data,
     170             :                                                   struct pvfs_oplock);
     171           0 :         struct opendb_oplock_break opb;
     172             : 
     173         108 :         if (num_fds != 0) {
     174           0 :                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
     175           0 :                 return;
     176             :         }
     177             : 
     178         108 :         ZERO_STRUCT(opb);
     179             : 
     180             :         /* we need to check that this one is for us. See
     181             :            imessaging_send_ptr() for the other side of this.
     182             :          */
     183         108 :         if (data->length == sizeof(struct opendb_oplock_break)) {
     184           0 :                 struct opendb_oplock_break *p;
     185         108 :                 p = (struct opendb_oplock_break *)data->data;
     186         108 :                 opb = *p;
     187             :         } else {
     188           0 :                 DEBUG(0,("%s: ignore oplock break with length[%u]\n",
     189             :                          __location__, (unsigned)data->length));
     190           0 :                 return;
     191             :         }
     192         108 :         if (opb.file_handle != opl->handle) {
     193           0 :                 return;
     194             :         }
     195             : 
     196             :         /*
     197             :          * maybe we should use ntvfs_setup_async()
     198             :          */
     199         108 :         pvfs_oplock_break(opl, opb.level);
     200             : }
     201             : 
     202         164 : static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
     203             : {
     204         164 :         imessaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
     205         164 :         return 0;
     206             : }
     207             : 
     208         164 : NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
     209             : {
     210           0 :         NTSTATUS status;
     211           0 :         struct pvfs_oplock *opl;
     212         164 :         uint32_t level = OPLOCK_NONE;
     213             : 
     214         164 :         f->handle->oplock = NULL;
     215             : 
     216         164 :         switch (oplock_granted) {
     217          31 :         case EXCLUSIVE_OPLOCK_RETURN:
     218          31 :                 level = OPLOCK_EXCLUSIVE;
     219          31 :                 break;
     220          86 :         case BATCH_OPLOCK_RETURN:
     221          86 :                 level = OPLOCK_BATCH;
     222          86 :                 break;
     223          47 :         case LEVEL_II_OPLOCK_RETURN:
     224          47 :                 level = OPLOCK_LEVEL_II;
     225          47 :                 break;
     226             :         }
     227             : 
     228         164 :         if (level == OPLOCK_NONE) {
     229           0 :                 return NT_STATUS_OK;
     230             :         }
     231             : 
     232         164 :         opl = talloc_zero(f->handle, struct pvfs_oplock);
     233         164 :         NT_STATUS_HAVE_NO_MEMORY(opl);
     234             : 
     235         164 :         opl->handle  = f->handle;
     236         164 :         opl->file    = f;
     237         164 :         opl->level   = level;
     238         164 :         opl->msg_ctx = f->pvfs->ntvfs->ctx->msg_ctx;
     239             : 
     240         164 :         status = imessaging_register(opl->msg_ctx,
     241             :                                     opl,
     242             :                                     MSG_NTVFS_OPLOCK_BREAK,
     243             :                                     pvfs_oplock_break_dispatch);
     244         164 :         NT_STATUS_NOT_OK_RETURN(status);
     245             : 
     246             :         /* destructor */
     247         164 :         talloc_set_destructor(opl, pvfs_oplock_destructor);
     248             : 
     249         164 :         f->handle->oplock = opl;
     250             : 
     251         164 :         return NT_STATUS_OK;
     252             : }
     253             : 
     254          85 : NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
     255             :                              struct ntvfs_request *req, union smb_lock *lck)
     256             : {
     257          85 :         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
     258             :                                   struct pvfs_state);
     259           0 :         struct pvfs_file *f;
     260           0 :         uint8_t oplock_break;
     261           0 :         NTSTATUS status;
     262             : 
     263          85 :         f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
     264          85 :         if (!f) {
     265           0 :                 return NT_STATUS_INVALID_HANDLE;
     266             :         }
     267             : 
     268          85 :         oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
     269             : 
     270          85 :         status = pvfs_oplock_release_internal(f->handle, oplock_break);
     271          85 :         if (!NT_STATUS_IS_OK(status)) {
     272           0 :                 DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
     273             :                         __FUNCTION__, oplock_break, nt_errstr(status)));
     274           0 :                 return status;
     275             :         }
     276             : 
     277          85 :         return NT_STATUS_OK;
     278             : }
     279             : 
     280       31552 : NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
     281             : {
     282       31552 :         struct pvfs_file_handle *h = f->handle;
     283           0 :         struct odb_lock *olck;
     284           0 :         NTSTATUS status;
     285             : 
     286       31552 :         if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
     287          14 :                 return NT_STATUS_OK;
     288             :         }
     289             : 
     290       31538 :         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
     291       31538 :         if (olck == NULL) {
     292           0 :                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
     293           0 :                 return NT_STATUS_FOOBAR;
     294             :         }
     295             : 
     296       31538 :         status = odb_break_oplocks(olck);
     297       31538 :         if (!NT_STATUS_IS_OK(status)) {
     298           0 :                 DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
     299             :                          h->name->full_name, nt_errstr(status)));
     300           0 :                 talloc_free(olck);
     301           0 :                 return status;
     302             :         }
     303             : 
     304       31538 :         talloc_free(olck);
     305             : 
     306       31538 :         return NT_STATUS_OK;
     307             : }

Generated by: LCOV version 1.14