nfs-ganesha 1.4

nfs4_op_lock.c

Go to the documentation of this file.
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 }