1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include <windows.h> 23 #include <stdio.h> 24 25 #include "daemon_debug.h" 26 #include "delegation.h" 27 #include "nfs41_ops.h" 28 #include "upcall.h" 29 #include "util.h" 30 31 32 #define LKLVL 2 /* dprintf level for lock logging */ 33 34 35 static void lock_stateid_arg( 36 IN nfs41_open_state *state, 37 OUT stateid_arg *arg) 38 { 39 arg->open = state; 40 arg->delegation = NULL; 41 42 AcquireSRWLockShared(&state->lock); 43 if (state->locks.stateid.seqid) { 44 memcpy(&arg->stateid, &state->locks.stateid, sizeof(stateid4)); 45 arg->type = STATEID_LOCK; 46 } else if (state->do_close) { 47 memcpy(&arg->stateid, &state->stateid, sizeof(stateid4)); 48 arg->type = STATEID_OPEN; 49 } else { 50 memset(&arg->stateid, 0, sizeof(stateid4)); 51 arg->type = STATEID_SPECIAL; 52 } 53 ReleaseSRWLockShared(&state->lock); 54 } 55 56 /* expects the caller to hold an exclusive lock on nfs41_open_state.lock */ 57 static void lock_stateid_update( 58 OUT nfs41_open_state *state, 59 IN const stateid4 *stateid) 60 { 61 if (state->locks.stateid.seqid == 0) { 62 /* if it's a new lock stateid, copy it in */ 63 memcpy(&state->locks.stateid, stateid, sizeof(stateid4)); 64 } else if (stateid->seqid > state->locks.stateid.seqid) { 65 /* update the seqid if it's more recent */ 66 state->locks.stateid.seqid = stateid->seqid; 67 } 68 } 69 70 static void open_lock_add( 71 IN nfs41_open_state *open, 72 IN const stateid_arg *stateid, 73 IN nfs41_lock_state *lock) 74 { 75 AcquireSRWLockExclusive(&open->lock); 76 77 if (stateid->type == STATEID_LOCK) 78 lock_stateid_update(open, &stateid->stateid); 79 80 lock->id = open->locks.counter++; 81 list_add_tail(&open->locks.list, &lock->open_entry); 82 83 ReleaseSRWLockExclusive(&open->lock); 84 } 85 86 static bool_t open_lock_delegate( 87 IN nfs41_open_state *open, 88 IN nfs41_lock_state *lock) 89 { 90 bool_t delegated = FALSE; 91 92 AcquireSRWLockExclusive(&open->lock); 93 if (open->delegation.state) { 94 nfs41_delegation_state *deleg = open->delegation.state; 95 AcquireSRWLockShared(&deleg->lock); 96 if (deleg->state.type == OPEN_DELEGATE_WRITE 97 && deleg->status == DELEGATION_GRANTED) { 98 lock->delegated = 1; 99 lock->id = open->locks.counter++; 100 list_add_tail(&open->locks.list, &lock->open_entry); 101 delegated = TRUE; 102 } 103 ReleaseSRWLockShared(&deleg->lock); 104 } 105 ReleaseSRWLockExclusive(&open->lock); 106 107 return delegated; 108 } 109 110 #define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry) 111 112 static int lock_range_cmp(const struct list_entry *entry, const void *value) 113 { 114 const nfs41_lock_state *lhs = lock_entry(entry); 115 const nfs41_lock_state *rhs = (const nfs41_lock_state*)value; 116 if (lhs->offset != rhs->offset) return -1; 117 if (lhs->length != rhs->length) return -1; 118 return 0; 119 } 120 121 static int open_unlock_delegate( 122 IN nfs41_open_state *open, 123 IN const nfs41_lock_state *input) 124 { 125 struct list_entry *entry; 126 int status = ERROR_NOT_LOCKED; 127 128 AcquireSRWLockExclusive(&open->lock); 129 130 /* find lock state that matches this range */ 131 entry = list_search(&open->locks.list, input, lock_range_cmp); 132 if (entry) { 133 nfs41_lock_state *lock = lock_entry(entry); 134 if (lock->delegated) { 135 /* if the lock was delegated, remove/free it and return success */ 136 list_remove(entry); 137 free(lock); 138 status = NO_ERROR; 139 } else 140 status = ERROR_LOCKED; 141 } 142 143 ReleaseSRWLockExclusive(&open->lock); 144 return status; 145 } 146 147 static void open_unlock_remove( 148 IN nfs41_open_state *open, 149 IN const stateid_arg *stateid, 150 IN const nfs41_lock_state *input) 151 { 152 struct list_entry *entry; 153 154 AcquireSRWLockExclusive(&open->lock); 155 if (stateid->type == STATEID_LOCK) 156 lock_stateid_update(open, &stateid->stateid); 157 158 /* find and remove the unlocked range */ 159 entry = list_search(&open->locks.list, input, lock_range_cmp); 160 if (entry) { 161 list_remove(entry); 162 free(lock_entry(entry)); 163 } 164 ReleaseSRWLockExclusive(&open->lock); 165 } 166 167 168 /* NFS41_LOCK */ 169 static int parse_lock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall) 170 { 171 int status; 172 lock_upcall_args *args = &upcall->args.lock; 173 174 status = safe_read(&buffer, &length, &args->offset, sizeof(LONGLONG)); 175 if (status) goto out; 176 status = safe_read(&buffer, &length, &args->length, sizeof(LONGLONG)); 177 if (status) goto out; 178 status = safe_read(&buffer, &length, &args->exclusive, sizeof(BOOLEAN)); 179 if (status) goto out; 180 status = safe_read(&buffer, &length, &args->blocking, sizeof(BOOLEAN)); 181 if (status) goto out; 182 183 dprintf(1, "parsing NFS41_LOCK: offset=0x%llx length=0x%llx exclusive=%u " 184 "blocking=%u\n", args->offset, args->length, args->exclusive, 185 args->blocking); 186 out: 187 return status; 188 } 189 190 static __inline uint32_t get_lock_type(BOOLEAN exclusive, BOOLEAN blocking) 191 { 192 return blocking == 0 193 ? ( exclusive == 0 ? READ_LT : WRITE_LT ) 194 : ( exclusive == 0 ? READW_LT : WRITEW_LT ); 195 } 196 197 static int handle_lock(nfs41_upcall *upcall) 198 { 199 stateid_arg stateid; 200 lock_upcall_args *args = &upcall->args.lock; 201 nfs41_open_state *state = upcall->state_ref; 202 nfs41_lock_state *lock; 203 const uint32_t type = get_lock_type(args->exclusive, args->blocking); 204 int status = NO_ERROR; 205 206 /* 18.10.3. Operation 12: LOCK - Create Lock 207 * "To lock the file from a specific offset through the end-of-file 208 * (no matter how long the file actually is) use a length field equal 209 * to NFS4_UINT64_MAX." */ 210 if (args->length >= NFS4_UINT64_MAX - args->offset) 211 args->length = NFS4_UINT64_MAX; 212 213 /* allocate the lock state */ 214 lock = calloc(1, sizeof(nfs41_lock_state)); 215 if (lock == NULL) { 216 status = GetLastError(); 217 goto out; 218 } 219 lock->offset = args->offset; 220 lock->length = args->length; 221 lock->exclusive = args->exclusive; 222 223 /* if we hold a write delegation, handle the lock locally */ 224 if (open_lock_delegate(state, lock)) { 225 dprintf(LKLVL, "delegated lock { %llu, %llu }\n", 226 lock->offset, lock->length); 227 args->acquired = TRUE; /* for cancel_lock() */ 228 goto out; 229 } 230 231 /* open_to_lock_owner4 requires an open stateid; if we 232 * have a delegation, convert it to an open stateid */ 233 status = nfs41_delegation_to_open(state, TRUE); 234 if (status) { 235 status = ERROR_FILE_INVALID; 236 goto out_free; 237 } 238 239 EnterCriticalSection(&state->locks.lock); 240 241 lock_stateid_arg(state, &stateid); 242 243 status = nfs41_lock(state->session, &state->file, &state->owner, 244 type, lock->offset, lock->length, FALSE, TRUE, &stateid); 245 if (status) { 246 dprintf(LKLVL, "nfs41_lock failed with %s\n", 247 nfs_error_string(status)); 248 status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP); 249 LeaveCriticalSection(&state->locks.lock); 250 goto out_free; 251 } 252 253 /* save lock state with the open */ 254 open_lock_add(state, &stateid, lock); 255 LeaveCriticalSection(&state->locks.lock); 256 257 args->acquired = TRUE; /* for cancel_lock() */ 258 out: 259 return status; 260 261 out_free: 262 free(lock); 263 goto out; 264 } 265 266 static void cancel_lock(IN nfs41_upcall *upcall) 267 { 268 stateid_arg stateid; 269 nfs41_lock_state input; 270 lock_upcall_args *args = &upcall->args.lock; 271 nfs41_open_state *state = upcall->state_ref; 272 int status = NO_ERROR; 273 274 dprintf(1, "--> cancel_lock()\n"); 275 276 /* can't do 'if (upcall->status)' here, because a handle_lock() success 277 * could be overwritten by upcall_marshall() or allocation failure */ 278 if (!args->acquired) 279 goto out; 280 281 input.offset = args->offset; 282 input.length = args->length; 283 284 /* search for the range to unlock, and remove if delegated */ 285 status = open_unlock_delegate(state, &input); 286 if (status != ERROR_LOCKED) 287 goto out; 288 289 EnterCriticalSection(&state->locks.lock); 290 lock_stateid_arg(state, &stateid); 291 292 status = nfs41_unlock(state->session, &state->file, 293 args->offset, args->length, &stateid); 294 295 open_unlock_remove(state, &stateid, &input); 296 LeaveCriticalSection(&state->locks.lock); 297 298 status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP); 299 out: 300 dprintf(1, "<-- cancel_lock() returning %d\n", status); 301 } 302 303 304 /* NFS41_UNLOCK */ 305 static int parse_unlock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall) 306 { 307 int status; 308 unlock_upcall_args *args = &upcall->args.unlock; 309 310 status = safe_read(&buffer, &length, &args->count, sizeof(ULONG)); 311 if (status) goto out; 312 313 args->buf = buffer; 314 args->buf_len = length; 315 316 dprintf(1, "parsing NFS41_UNLOCK: count=%u\n", args->count); 317 out: 318 return status; 319 } 320 321 static int handle_unlock(nfs41_upcall *upcall) 322 { 323 nfs41_lock_state input; 324 stateid_arg stateid; 325 unlock_upcall_args *args = &upcall->args.unlock; 326 nfs41_open_state *state = upcall->state_ref; 327 unsigned char *buf = args->buf; 328 uint32_t buf_len = args->buf_len; 329 uint32_t i; 330 int status = NO_ERROR; 331 332 for (i = 0; i < args->count; i++) { 333 if (safe_read(&buf, &buf_len, &input.offset, sizeof(LONGLONG))) break; 334 if (safe_read(&buf, &buf_len, &input.length, sizeof(LONGLONG))) break; 335 336 /* do the same translation as LOCK, or the ranges won't match */ 337 if (input.length >= NFS4_UINT64_MAX - input.offset) 338 input.length = NFS4_UINT64_MAX; 339 340 /* search for the range to unlock, and remove if delegated */ 341 status = open_unlock_delegate(state, &input); 342 if (status != ERROR_LOCKED) 343 continue; 344 345 EnterCriticalSection(&state->locks.lock); 346 lock_stateid_arg(state, &stateid); 347 348 status = nfs41_unlock(state->session, &state->file, 349 input.offset, input.length, &stateid); 350 351 open_unlock_remove(state, &stateid, &input); 352 LeaveCriticalSection(&state->locks.lock); 353 354 status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP); 355 } 356 return status; 357 } 358 359 360 const nfs41_upcall_op nfs41_op_lock = { 361 parse_lock, 362 handle_lock, 363 NULL, 364 cancel_lock 365 }; 366 const nfs41_upcall_op nfs41_op_unlock = { 367 parse_unlock, 368 handle_unlock 369 }; 370