root/source4/ntvfs/posix/pvfs_oplock.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pvfs_oplock_release_internal
  2. pvfs_oplock_break
  3. pvfs_oplock_break_dispatch
  4. pvfs_oplock_destructor
  5. pvfs_setup_oplock
  6. pvfs_oplock_release
  7. pvfs_break_level2_oplocks

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

/* [<][>][^][v][top][bottom][index][help] */