nfs-ganesha 1.4
|
00001 /* 00002 * vim:expandtab:shiftwidth=8:tabstop=8: 00003 * 00004 * Copyright CEA/DAM/DIF (2008) 00005 * contributeur : Philippe DENIEL philippe.deniel@cea.fr 00006 * Thomas LEIBOVICI thomas.leibovici@cea.fr 00007 * 00008 * 00009 * This program is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 3 of the License, or (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00022 * 00023 * --------------------------------------- 00024 */ 00025 00037 #ifdef HAVE_CONFIG_H 00038 #include "config.h" 00039 #endif 00040 00041 #ifdef _SOLARIS 00042 #include "solaris_port.h" 00043 #endif 00044 00045 #include <stdio.h> 00046 #include <string.h> 00047 #include <pthread.h> 00048 #include "HashData.h" 00049 #include "HashTable.h" 00050 #include "log.h" 00051 #include "ganesha_rpc.h" 00052 #include "nfs4.h" 00053 #include "nfs_core.h" 00054 #include "sal_functions.h" 00055 #include "nfs_proto_functions.h" 00056 #include "nfs_proto_tools.h" 00057 #include "nlm_list.h" 00058 00076 #define arg_LOCK4 op->nfs_argop4_u.oplock 00077 #define res_LOCK4 resp->nfs_resop4_u.oplock 00078 00079 int nfs4_op_lock(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) 00080 { 00081 state_status_t state_status; 00082 state_data_t candidate_data; 00083 state_type_t candidate_type; 00084 int rc = 0; 00085 state_t * plock_state; /* state for the lock */ 00086 state_t * pstate_open; /* state for the open owner */ 00087 state_owner_t * plock_owner; 00088 state_owner_t * popen_owner; 00089 state_owner_t * conflict_owner = NULL; 00090 state_owner_t * presp_owner; /* Owner to store response in */ 00091 seqid4 seqid; 00092 nfs_client_id_t * pclientid; 00093 state_nfs4_owner_name_t owner_name; 00094 fsal_lock_param_t lock_desc, conflict_desc; 00095 state_blocking_t blocking = STATE_NON_BLOCKING; 00096 const char * tag = "LOCK"; 00097 00098 LogDebug(COMPONENT_NFS_V4_LOCK, 00099 "Entering NFS v4 LOCK handler -----------------------------------------------------"); 00100 00101 /* Initialize to sane starting values */ 00102 resp->resop = NFS4_OP_LOCK; 00103 res_LOCK4.status = NFS4_OK; 00104 00105 /* 00106 * Do basic checks on a filehandle 00107 * Lock is done only on a file 00108 */ 00109 res_LOCK4.status = nfs4_sanity_check_FH(data, REGULAR_FILE); 00110 if(res_LOCK4.status != NFS4_OK) 00111 return res_LOCK4.status; 00112 00113 /* This can't be done on the pseudofs */ 00114 if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) 00115 { 00116 res_LOCK4.status = NFS4ERR_ROFS; 00117 LogDebug(COMPONENT_STATE, 00118 "NFS4 LOCK returning NFS4ERR_ROFS"); 00119 return res_LOCK4.status; 00120 } 00121 00122 if (nfs_export_check_security(data->reqp, data->pexport) == FALSE) 00123 { 00124 res_LOCK4.status = NFS4ERR_PERM; 00125 return res_LOCK4.status; 00126 } 00127 00128 /* Convert lock parameters to internal types */ 00129 switch(arg_LOCK4.locktype) 00130 { 00131 case READ_LT: 00132 lock_desc.lock_type = FSAL_LOCK_R; 00133 blocking = STATE_NON_BLOCKING; 00134 break; 00135 00136 case WRITE_LT: 00137 lock_desc.lock_type = FSAL_LOCK_W; 00138 blocking = STATE_NON_BLOCKING; 00139 break; 00140 00141 case READW_LT: 00142 lock_desc.lock_type = FSAL_LOCK_R; 00143 blocking = STATE_NFSV4_BLOCKING; 00144 break; 00145 00146 case WRITEW_LT: 00147 lock_desc.lock_type = FSAL_LOCK_W; 00148 blocking = STATE_NFSV4_BLOCKING; 00149 break; 00150 } 00151 00152 lock_desc.lock_start = arg_LOCK4.offset; 00153 00154 if(arg_LOCK4.length != STATE_LOCK_OFFSET_EOF) 00155 lock_desc.lock_length = arg_LOCK4.length; 00156 else 00157 lock_desc.lock_length = 0; 00158 00159 if(arg_LOCK4.locker.new_lock_owner) 00160 { 00161 /* New lock owner, Find the open owner */ 00162 tag = "LOCK (new owner)"; 00163 00164 /* Check stateid correctness and get pointer to state */ 00165 if((rc = nfs4_Check_Stateid(&arg_LOCK4.locker.locker4_u.open_owner.open_stateid, 00166 data->current_entry, 00167 &pstate_open, 00168 data, 00169 STATEID_SPECIAL_FOR_LOCK, 00170 tag)) != NFS4_OK) 00171 { 00172 res_LOCK4.status = rc; 00173 LogDebug(COMPONENT_NFS_V4_LOCK, 00174 "LOCK failed nfs4_Check_Stateid for open owner"); 00175 return res_LOCK4.status; 00176 } 00177 00178 popen_owner = pstate_open->state_powner; 00179 plock_state = NULL; 00180 plock_owner = NULL; 00181 presp_owner = popen_owner; 00182 seqid = arg_LOCK4.locker.locker4_u.open_owner.open_seqid; 00183 00184 LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, 00185 "LOCK New lock owner from open owner", 00186 data->current_entry, 00187 data->pcontext, 00188 popen_owner, 00189 &lock_desc); 00190 00191 /* Check is the clientid is known or not */ 00192 if(nfs_client_id_get_confirmed(arg_LOCK4.locker.locker4_u.open_owner.lock_owner.clientid, 00193 &pclientid) == CLIENT_ID_NOT_FOUND) 00194 { 00195 res_LOCK4.status = NFS4ERR_STALE_CLIENTID; 00196 LogDebug(COMPONENT_NFS_V4_LOCK, 00197 "LOCK failed nfs_client_id_get"); 00198 return res_LOCK4.status; 00199 } 00200 00201 if(isDebug(COMPONENT_CLIENTID) && 00202 pclientid != popen_owner->so_owner.so_nfs4_owner.so_pclientid) 00203 { 00204 char str_open[HASHTABLE_DISPLAY_STRLEN]; 00205 char str_lock[HASHTABLE_DISPLAY_STRLEN]; 00206 00207 display_client_id_rec(popen_owner->so_owner.so_nfs4_owner.so_pclientid, str_open); 00208 display_client_id_rec(pclientid, str_lock); 00209 00210 LogDebug(COMPONENT_CLIENTID, 00211 "Unexpected, new lock owner clientid {%s} doesn't match open owner clientid {%s}", 00212 str_lock, str_open); 00213 } 00214 00215 /* The related stateid is already stored in pstate_open */ 00216 00217 /* An open state has been found. Check its type */ 00218 if(pstate_open->state_type != STATE_TYPE_SHARE) 00219 { 00220 res_LOCK4.status = NFS4ERR_BAD_STATEID; 00221 LogDebug(COMPONENT_NFS_V4_LOCK, 00222 "LOCK failed open stateid is not a SHARE"); 00223 goto out2; 00224 } 00225 00226 #ifdef _CONFORM_TO_TEST_LOCK8c 00227 /* Lock seqid (seqid wanted for new lock) should be 0 (see newpynfs test LOCK8c) */ 00228 if(arg_LOCK4.locker.locker4_u.open_owner.lock_seqid != 0) 00229 { 00230 res_LOCK4.status = NFS4ERR_BAD_SEQID; 00231 LogDebug(COMPONENT_NFS_V4_LOCK, 00232 "LOCK failed new lock seqid is not 0, it is set to: %d", 00233 arg_LOCK4.locker.locker4_u.open_owner.lock_seqid ); 00234 goto out2; 00235 } 00236 #endif/* _CONFORM_TO_TEST_LOCK8c */ 00237 00238 /* Is this lock_owner known ? */ 00239 convert_nfs4_lock_owner(&arg_LOCK4.locker.locker4_u.open_owner.lock_owner, 00240 &owner_name, 0LL); 00241 } 00242 else 00243 { 00244 /* Existing lock owner 00245 * Find the lock stateid 00246 * From that, get the open_owner 00247 */ 00248 tag = "LOCK (existing owner)"; 00249 00250 /* There was code here before to handle all-0 stateid, but that 00251 * really doesn't apply - when we handle temporary locks for 00252 * I/O operations (which is where we will see all-0 or all-1 00253 * stateid, those will not come in through nfs4_op_lock. 00254 */ 00255 00256 /* Check stateid correctness and get pointer to state */ 00257 if((rc = nfs4_Check_Stateid(&arg_LOCK4.locker.locker4_u.lock_owner.lock_stateid, 00258 data->current_entry, 00259 &plock_state, 00260 data, 00261 STATEID_SPECIAL_FOR_LOCK, 00262 tag)) != NFS4_OK) 00263 { 00264 res_LOCK4.status = rc; 00265 LogDebug(COMPONENT_NFS_V4_LOCK, 00266 "LOCK failed nfs4_Check_Stateid for existing lock owner"); 00267 return res_LOCK4.status; 00268 } 00269 00270 /* Check if lock state belongs to same export */ 00271 if(plock_state->state_pexport != data->pexport) 00272 { 00273 LogEvent(COMPONENT_STATE, 00274 "Lock Owner Export Conflict, Lock held for export %d (%s), request for export %d (%s)", 00275 plock_state->state_pexport->id, 00276 plock_state->state_pexport->fullpath, 00277 data->pexport->id, 00278 data->pexport->fullpath); 00279 res_LOCK4.status = STATE_INVALID_ARGUMENT; 00280 return res_LOCK4.status; 00281 } 00282 00283 /* An lock state has been found. Check its type */ 00284 if(plock_state->state_type != STATE_TYPE_LOCK) 00285 { 00286 res_LOCK4.status = NFS4ERR_BAD_STATEID; 00287 LogDebug(COMPONENT_NFS_V4_LOCK, 00288 "LOCK failed existing lock owner, state type is not LOCK"); 00289 return res_LOCK4.status; 00290 } 00291 00292 /* Get the old lockowner. We can do the following 'cast', in NFSv4 lock_owner4 and open_owner4 00293 * are different types but with the same definition*/ 00294 plock_owner = plock_state->state_powner; 00295 popen_owner = plock_owner->so_owner.so_nfs4_owner.so_related_owner; 00296 pstate_open = plock_state->state_data.lock.popenstate; 00297 presp_owner = plock_owner; 00298 seqid = arg_LOCK4.locker.locker4_u.lock_owner.lock_seqid; 00299 00300 LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, 00301 "LOCK Existing lock owner", 00302 data->current_entry, 00303 data->pcontext, 00304 plock_owner, 00305 &lock_desc); 00306 00307 #ifdef _CONFORM_TO_TEST_LOCK8c 00308 /* Check validity of the seqid */ 00309 if(arg_LOCK4.locker.locker4_u.lock_owner.lock_seqid != 0) 00310 { 00311 res_LOCK4.status = NFS4ERR_BAD_SEQID; 00312 LogDebug(COMPONENT_NFS_V4_LOCK, 00313 "LOCK failed existing lock owner, lock seqid != 0"); 00314 return res_LOCK4.status; 00315 } 00316 #endif 00317 00318 /* get the client for this open owner */ 00319 pclientid = popen_owner->so_owner.so_nfs4_owner.so_pclientid; 00320 00321 inc_client_id_ref(pclientid); 00322 00323 } /* if( arg_LOCK4.locker.new_lock_owner ) */ 00324 00325 /* Check seqid (lock_seqid or open_seqid) */ 00326 if(!Check_nfs4_seqid(presp_owner, seqid, op, data, resp, tag)) 00327 { 00328 /* Response is all setup for us and LogDebug told what was wrong */ 00329 goto out2; 00330 } 00331 00332 /* Lock length should not be 0 */ 00333 if(arg_LOCK4.length == 0LL) 00334 { 00335 res_LOCK4.status = NFS4ERR_INVAL; 00336 LogDebug(COMPONENT_NFS_V4_LOCK, 00337 "LOCK failed length == 0"); 00338 goto out; 00339 } 00340 00341 /* Check for range overflow. 00342 * Comparing beyond 2^64 is not possible int 64 bits precision, 00343 * but off+len > 2^64-1 is equivalent to len > 2^64-1 - off 00344 */ 00345 if(lock_desc.lock_length > (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start)) 00346 { 00347 res_LOCK4.status = NFS4ERR_INVAL; 00348 LogDebug(COMPONENT_NFS_V4_LOCK, 00349 "LOCK failed length overflow"); 00350 goto out; 00351 } 00352 00353 /* check if open state has correct access for type of lock. 00354 * Don't need to check for conflicting states since this open 00355 * state assures there are no conflicting states. 00356 */ 00357 if(((arg_LOCK4.locktype == WRITE_LT || arg_LOCK4.locktype == WRITEW_LT) && 00358 ((pstate_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0)) || 00359 ((arg_LOCK4.locktype == READ_LT || arg_LOCK4.locktype == READW_LT) && 00360 ((pstate_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_READ) == 0))) 00361 { 00362 /* The open state doesn't allow access based on the type of lock */ 00363 LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, 00364 "LOCK failed, SHARE doesn't allow access", 00365 data->current_entry, 00366 data->pcontext, 00367 plock_owner, 00368 &lock_desc); 00369 00370 res_LOCK4.status = NFS4ERR_OPENMODE; 00371 00372 goto out; 00373 } 00374 00375 /* 00376 * do grace period checking 00377 */ 00378 if (nfs_in_grace() && !arg_LOCK4.reclaim) 00379 { 00380 LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, 00381 "LOCK failed, non-reclaim while in grace", 00382 data->current_entry, 00383 data->pcontext, 00384 plock_owner, 00385 &lock_desc); 00386 res_LOCK4.status = NFS4ERR_GRACE; 00387 goto out; 00388 } 00389 00390 if (nfs_in_grace() && arg_LOCK4.reclaim && 00391 !pclientid->cid_allow_reclaim) 00392 { 00393 LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, 00394 "LOCK failed, invalid reclaim while in grace", 00395 data->current_entry, 00396 data->pcontext, 00397 plock_owner, 00398 &lock_desc); 00399 res_LOCK4.status = NFS4ERR_NO_GRACE; 00400 goto out; 00401 } 00402 00403 if (!nfs_in_grace() && arg_LOCK4.reclaim) 00404 { 00405 LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, 00406 "LOCK failed, reclaim while not in grace", 00407 data->current_entry, 00408 data->pcontext, 00409 plock_owner, 00410 &lock_desc); 00411 res_LOCK4.status = NFS4ERR_NO_GRACE; 00412 goto out; 00413 } 00414 00415 if(arg_LOCK4.locker.new_lock_owner) 00416 { 00417 /* A lock owner is always associated with a previously made open 00418 * which has itself a previously made stateid 00419 */ 00420 00421 if(nfs4_owner_Get_Pointer(&owner_name, &plock_owner)) 00422 { 00423 /* Lock owner already exists. */ 00424 /* Check lock_seqid if it has attached locks. */ 00425 if(!glist_empty(&plock_owner->so_lock_list) && 00426 !Check_nfs4_seqid(plock_owner, 00427 arg_LOCK4.locker.locker4_u.open_owner.lock_seqid, 00428 op, 00429 data, 00430 resp, 00431 "LOCK (new owner but owner exists)")) 00432 { 00433 LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, 00434 "LOCK failed to create new lock owner, re-use", 00435 data->current_entry, 00436 data->pcontext, 00437 popen_owner, 00438 &lock_desc); 00439 dump_all_locks("All locks (re-use of lock owner)"); 00440 /* Response is all setup for us and LogDebug told what was wrong */ 00441 goto out2; 00442 } 00443 00444 if(plock_owner->so_owner.so_nfs4_owner.so_related_owner == NULL) 00445 { 00446 /* Attach open owner to lock owner now that we know it. */ 00447 inc_state_owner_ref(popen_owner); 00448 plock_owner->so_owner.so_nfs4_owner.so_related_owner = popen_owner; 00449 } 00450 else if(plock_owner->so_owner.so_nfs4_owner.so_related_owner != popen_owner) 00451 { 00452 res_LOCK4.status = NFS4ERR_INVAL; 00453 LogDebug(COMPONENT_NFS_V4_LOCK, 00454 "LOCK failed related owner %p doesn't match open owner %p", 00455 plock_owner->so_owner.so_nfs4_owner.so_related_owner, 00456 popen_owner); 00457 goto out2; 00458 } 00459 } 00460 else 00461 { 00462 /* This lock owner is not known yet, allocated and set up a new one */ 00463 plock_owner = create_nfs4_owner(&owner_name, 00464 pclientid, 00465 STATE_LOCK_OWNER_NFSV4, 00466 popen_owner, 00467 0); 00468 00469 if(plock_owner == NULL) 00470 { 00471 res_LOCK4.status = NFS4ERR_RESOURCE; 00472 00473 LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, 00474 "LOCK failed to create new lock owner", 00475 data->current_entry, 00476 data->pcontext, 00477 popen_owner, 00478 &lock_desc); 00479 00480 goto out2; 00481 } 00482 } 00483 00484 /* Prepare state management structure */ 00485 memset(&candidate_type, 0, sizeof(candidate_type)); 00486 candidate_type = STATE_TYPE_LOCK; 00487 candidate_data.lock.popenstate = pstate_open; 00488 00489 /* Add the lock state to the lock table */ 00490 if(state_add(data->current_entry, 00491 candidate_type, 00492 &candidate_data, 00493 plock_owner, 00494 data->pcontext, 00495 &plock_state, &state_status) != STATE_SUCCESS) 00496 { 00497 res_LOCK4.status = NFS4ERR_RESOURCE; 00498 00499 LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, 00500 "LOCK failed to add new stateid", 00501 data->current_entry, 00502 data->pcontext, 00503 plock_owner, 00504 &lock_desc); 00505 00506 dec_state_owner_ref(plock_owner); 00507 00508 goto out2; 00509 } 00510 00511 init_glist(&plock_state->state_data.lock.state_locklist); 00512 00513 /* Attach this lock to an export */ 00514 plock_state->state_pexport = data->pexport; 00515 P(data->pexport->exp_state_mutex); 00516 glist_add_tail(&data->pexport->exp_state_list, &plock_state->state_export_list); 00517 V(data->pexport->exp_state_mutex); 00518 00519 /* Add lock state to the list of lock states belonging to the open state */ 00520 glist_add_tail(&pstate_open->state_data.share.share_lockstates, 00521 &plock_state->state_data.lock.state_sharelist); 00522 } /* if( arg_LOCK4.locker.new_lock_owner ) */ 00523 00524 /* Now we have a lock owner and a stateid. 00525 * Go ahead and push lock into SAL (and FSAL). 00526 */ 00527 if(state_lock(data->current_entry, 00528 data->pcontext, 00529 data->pexport, 00530 plock_owner, 00531 plock_state, 00532 blocking, 00533 NULL, /* No block data for now */ 00534 &lock_desc, 00535 &conflict_owner, 00536 &conflict_desc, 00537 &state_status) != STATE_SUCCESS) 00538 { 00539 if(state_status == STATE_LOCK_CONFLICT) 00540 { 00541 /* A conflicting lock from a different lock_owner, returns NFS4ERR_DENIED */ 00542 Process_nfs4_conflict(&res_LOCK4.LOCK4res_u.denied, 00543 conflict_owner, 00544 &conflict_desc); 00545 } 00546 00547 LogDebug(COMPONENT_NFS_V4_LOCK, 00548 "LOCK failed with status %s", 00549 state_err_str(state_status)); 00550 00551 res_LOCK4.status = nfs4_Errno_state(state_status); 00552 00553 /* Save the response in the lock or open owner */ 00554 if(res_LOCK4.status != NFS4ERR_RESOURCE && 00555 res_LOCK4.status != NFS4ERR_BAD_STATEID) 00556 Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); 00557 00558 if(arg_LOCK4.locker.new_lock_owner) 00559 { 00560 /* Need to destroy lock owner and state */ 00561 if(state_del(plock_state, 00562 &state_status) != STATE_SUCCESS) 00563 LogDebug(COMPONENT_NFS_V4_LOCK, 00564 "state_del failed with status %s", 00565 state_err_str(state_status)); 00566 } 00567 00568 goto out2; 00569 } 00570 00571 res_LOCK4.status = NFS4_OK; 00572 00573 /* Handle stateid/seqid for success */ 00574 update_stateid(plock_state, 00575 &res_LOCK4.LOCK4res_u.resok4.lock_stateid, 00576 data, 00577 tag); 00578 00579 LogFullDebug(COMPONENT_NFS_V4_LOCK, 00580 "LOCK state_seqid = %u, plock_state = %p", 00581 plock_state->state_seqid, 00582 plock_state); 00583 00584 if(arg_LOCK4.locker.new_lock_owner) 00585 { 00586 /* Also save the response in the lock owner */ 00587 Copy_nfs4_state_req(plock_owner, 00588 arg_LOCK4.locker.locker4_u.open_owner.lock_seqid, 00589 op, 00590 data, 00591 resp, 00592 tag); 00593 tag = "LOCK (open owner)"; 00594 } 00595 00596 LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, 00597 "LOCK applied", 00598 data->current_entry, 00599 data->pcontext, 00600 plock_owner, 00601 &lock_desc); 00602 out: 00603 00604 /* Save the response in the lock or open owner */ 00605 Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); 00606 00607 out2: 00608 00609 dec_client_id_ref(pclientid); 00610 00611 return res_LOCK4.status; 00612 } /* nfs4_op_lock */ 00613 00624 void nfs4_op_lock_Free(LOCK4res * resp) 00625 { 00626 if(resp->status == NFS4ERR_DENIED) 00627 Release_nfs4_denied(&resp->LOCK4res_u.denied); 00628 } /* nfs4_op_lock_Free */ 00629 00630 void nfs4_op_lock_CopyRes(LOCK4res * resp_dst, LOCK4res * resp_src) 00631 { 00632 if(resp_src->status == NFS4ERR_DENIED) 00633 Copy_nfs4_denied(&resp_dst->LOCK4res_u.denied, 00634 &resp_src->LOCK4res_u.denied); 00635 }