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 29struct 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 38static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h, 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*/ 93static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level) 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(5,("%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 161static void pvfs_oplock_break_dispatch(struct messaging_context *msg, 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 193static int pvfs_oplock_destructor(struct pvfs_oplock *opl) 194{ 195 messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl); 196 return 0; 197} 198 199NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted) 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 245NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs, 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 271NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f) 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} 299