nfs-ganesha 1.4

nfs41_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 nfs41_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_nfs4_owner_name_t   owner_name;
00091   fsal_lock_param_t         lock_desc, conflict_desc;
00092   state_blocking_t          blocking = STATE_NON_BLOCKING;
00093   const char              * tag = "LOCK";
00094 
00095   LogDebug(COMPONENT_NFS_V4_LOCK,
00096            "Entering NFS v4.1 LOCK handler -----------------------------------------------------");
00097 
00098   /* Initialize to sane starting values */
00099   resp->resop = NFS4_OP_LOCK;
00100   res_LOCK4.status = NFS4_OK;
00101 
00102   /*
00103    * Do basic checks on a filehandle
00104    * Lock is done only on a file
00105    */
00106   res_LOCK4.status = nfs4_sanity_check_FH(data, REGULAR_FILE);
00107   if(res_LOCK4.status != NFS4_OK)
00108     return res_LOCK4.status;
00109 
00110   /* Convert lock parameters to internal types */
00111   switch(arg_LOCK4.locktype)
00112     {
00113       case READ_LT:
00114         lock_desc.lock_type = FSAL_LOCK_R;
00115         blocking            = STATE_NON_BLOCKING;
00116         break;
00117 
00118       case WRITE_LT:
00119         lock_desc.lock_type = FSAL_LOCK_W;
00120         blocking            = STATE_NON_BLOCKING;
00121         break;
00122 
00123       case READW_LT:
00124         lock_desc.lock_type = FSAL_LOCK_R;
00125         blocking            = STATE_NFSV4_BLOCKING;
00126         break;
00127 
00128       case WRITEW_LT:
00129         lock_desc.lock_type = FSAL_LOCK_W;
00130         blocking            = STATE_NFSV4_BLOCKING;
00131         break;
00132     }
00133 
00134   lock_desc.lock_start = arg_LOCK4.offset;
00135 
00136   if(arg_LOCK4.length != STATE_LOCK_OFFSET_EOF)
00137     lock_desc.lock_length = arg_LOCK4.length;
00138   else
00139     lock_desc.lock_length = 0;
00140 
00141   if(arg_LOCK4.locker.new_lock_owner)
00142     {
00143       /* New lock owner, Find the open owner */
00144       tag = "LOCK (new owner)";
00145 
00146       /* Check stateid correctness and get pointer to state */
00147       if((rc = nfs4_Check_Stateid(&arg_LOCK4.locker.locker4_u.open_owner.open_stateid,
00148                                   data->current_entry,
00149                                   &pstate_open,
00150                                   data,
00151                                   STATEID_SPECIAL_FOR_LOCK,
00152                                   tag)) != NFS4_OK)
00153         {
00154           res_LOCK4.status = rc;
00155           LogDebug(COMPONENT_NFS_V4_LOCK,
00156                    "LOCK failed nfs4_Check_Stateid for open owner");
00157           return res_LOCK4.status;
00158         }
00159 
00160       popen_owner = pstate_open->state_powner;
00161       plock_state = NULL;
00162       plock_owner = NULL;
00163 
00164       LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG,
00165               "LOCK New lock owner from open owner",
00166               data->current_entry,
00167               data->pcontext,
00168               popen_owner,
00169               &lock_desc);
00170 
00171       /* An open state has been found. Check its type */
00172       if(pstate_open->state_type != STATE_TYPE_SHARE)
00173         {
00174           res_LOCK4.status = NFS4ERR_BAD_STATEID;
00175           LogDebug(COMPONENT_NFS_V4_LOCK,
00176                    "LOCK failed open stateid is not a SHARE");
00177           return res_LOCK4.status;
00178         }
00179 
00180       /* Is this lock_owner known ? */
00181       convert_nfs4_lock_owner(&arg_LOCK4.locker.locker4_u.open_owner.lock_owner,
00182                               &owner_name, data->psession->clientid);
00183     }
00184   else
00185     {
00186       /* Existing lock owner
00187        * Find the lock stateid
00188        * From that, get the open_owner
00189        */
00190       tag = "LOCK (existing owner)";
00191 
00192       /* There was code here before to handle all-0 stateid, but that
00193        * really doesn't apply - when we handle temporary locks for
00194        * I/O operations (which is where we will see all-0 or all-1
00195        * stateid, those will not come in through nfs4_op_lock.
00196        */
00197 
00198       /* Check stateid correctness and get pointer to state */
00199       if((rc = nfs4_Check_Stateid(&arg_LOCK4.locker.locker4_u.lock_owner.lock_stateid,
00200                                   data->current_entry,
00201                                   &plock_state,
00202                                   data,
00203                                   STATEID_SPECIAL_FOR_LOCK,
00204                                   tag)) != NFS4_OK)
00205         {
00206           res_LOCK4.status = rc;
00207           LogDebug(COMPONENT_NFS_V4_LOCK,
00208                    "LOCK failed nfs4_Check_Stateid for existing lock owner");
00209           return res_LOCK4.status;
00210         }
00211 
00212       /* Check if lock state belongs to same export */
00213       if(plock_state->state_pexport != data->pexport)
00214         {
00215           LogEvent(COMPONENT_STATE,
00216                    "Lock Owner Export Conflict, Lock held for export %d (%s), request for export %d (%s)",
00217                    plock_state->state_pexport->id,
00218                    plock_state->state_pexport->fullpath,
00219                    data->pexport->id,
00220                    data->pexport->fullpath);
00221           res_LOCK4.status = STATE_INVALID_ARGUMENT;
00222           return res_LOCK4.status;
00223         }
00224 
00225       /* An lock state has been found. Check its type */
00226       if(plock_state->state_type != STATE_TYPE_LOCK)
00227         {
00228           res_LOCK4.status = NFS4ERR_BAD_STATEID;
00229           LogDebug(COMPONENT_NFS_V4_LOCK,
00230                    "LOCK failed existing lock owner, state type is not LOCK");
00231           return res_LOCK4.status;
00232         }
00233 
00234       /* Get the old lockowner. We can do the following 'cast', in NFSv4 lock_owner4 and open_owner4
00235        * are different types but with the same definition*/
00236       plock_owner = plock_state->state_powner;
00237       popen_owner = plock_owner->so_owner.so_nfs4_owner.so_related_owner;
00238       pstate_open = plock_state->state_data.lock.popenstate;
00239 
00240       LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG,
00241               "LOCK Existing lock owner",
00242               data->current_entry,
00243               data->pcontext,
00244               plock_owner,
00245               &lock_desc);
00246     }                           /* if( arg_LOCK4.locker.new_lock_owner ) */
00247 
00248   /* Lock length should not be 0 */
00249   if(arg_LOCK4.length == 0LL)
00250     {
00251       res_LOCK4.status = NFS4ERR_INVAL;
00252       LogDebug(COMPONENT_NFS_V4_LOCK,
00253                "LOCK failed length == 0");
00254 
00255       return res_LOCK4.status;
00256     }
00257 
00258   /* Check for range overflow.
00259    * Comparing beyond 2^64 is not possible int 64 bits precision,
00260    * but off+len > 2^64-1 is equivalent to len > 2^64-1 - off
00261    */
00262   if(lock_desc.lock_length > (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start))
00263     {
00264       res_LOCK4.status = NFS4ERR_INVAL;
00265       LogDebug(COMPONENT_NFS_V4_LOCK,
00266                "LOCK failed length overflow");
00267 
00268       return res_LOCK4.status;
00269     }
00270 
00271   /* check if open state has correct access for type of lock.
00272    * Don't need to check for conflicting states since this open
00273    * state assures there are no conflicting states.
00274    */
00275   if(((arg_LOCK4.locktype == WRITE_LT || arg_LOCK4.locktype == WRITEW_LT) &&
00276       ((pstate_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0)) ||
00277      ((arg_LOCK4.locktype == READ_LT || arg_LOCK4.locktype == READW_LT) &&
00278       ((pstate_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_READ) == 0)))
00279     {
00280       /* The open state doesn't allow access based on the type of lock */
00281       LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
00282               "LOCK failed, SHARE doesn't allow access",
00283               data->current_entry,
00284               data->pcontext,
00285               plock_owner,
00286               &lock_desc);
00287 
00288       res_LOCK4.status = NFS4ERR_OPENMODE;
00289       return res_LOCK4.status;
00290     }
00291 
00292   /*
00293    * do grace period checking
00294    */
00295   if (nfs_in_grace() && !arg_LOCK4.reclaim)
00296     {
00297       LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
00298               "LOCK failed, non-reclaim while in grace",
00299               data->current_entry,
00300               data->pcontext,
00301               plock_owner,
00302               &lock_desc);
00303       res_LOCK4.status = NFS4ERR_GRACE;
00304       return res_LOCK4.status;
00305     }
00306 
00307   if (nfs_in_grace() && arg_LOCK4.reclaim &&
00308       !data->psession->pclientid_record->cid_allow_reclaim)
00309     {
00310       LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
00311               "LOCK failed, invalid reclaim while in grace",
00312               data->current_entry,
00313               data->pcontext,
00314               plock_owner,
00315               &lock_desc);
00316       res_LOCK4.status = NFS4ERR_NO_GRACE;
00317       return res_LOCK4.status;
00318     }
00319 
00320   if (!nfs_in_grace() && arg_LOCK4.reclaim)
00321     {
00322       LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
00323               "LOCK failed, reclaim while not in grace",
00324               data->current_entry,
00325               data->pcontext,
00326               plock_owner,
00327               &lock_desc);
00328       res_LOCK4.status = NFS4ERR_NO_GRACE;
00329       return res_LOCK4.status;
00330     }
00331 
00332   if(arg_LOCK4.locker.new_lock_owner)
00333     {
00334       /* A lock owner is always associated with a previously made open
00335        * which has itself a previously made stateid
00336        */
00337 
00338       if(nfs4_owner_Get_Pointer(&owner_name, &plock_owner))
00339         {
00340           /* Lock owner already exists. */
00341           if(plock_owner->so_owner.so_nfs4_owner.so_related_owner == NULL)
00342             {
00343               /* Attach open owner to lock owner now that we know it. */
00344               inc_state_owner_ref(popen_owner);
00345               plock_owner->so_owner.so_nfs4_owner.so_related_owner = popen_owner;
00346             }
00347           else if(plock_owner->so_owner.so_nfs4_owner.so_related_owner != popen_owner)
00348             {
00349               res_LOCK4.status = NFS4ERR_INVAL;
00350               LogDebug(COMPONENT_NFS_V4_LOCK,
00351                        "LOCK failed related owner %p doesn't match open owner %p",
00352                        plock_owner->so_owner.so_nfs4_owner.so_related_owner,
00353                        popen_owner);
00354               return res_LOCK4.status;
00355             }
00356         }
00357       else
00358         {
00359           /* This lock owner is not known yet, allocated and set up a new one */
00360           plock_owner = create_nfs4_owner(&owner_name,
00361                                           data->psession->pclientid_record,
00362                                           STATE_LOCK_OWNER_NFSV4,
00363                                           popen_owner,
00364                                           0);
00365 
00366           if(plock_owner == NULL)
00367             {
00368               res_LOCK4.status = NFS4ERR_RESOURCE;
00369 
00370               LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
00371                       "LOCK failed to create new lock owner",
00372                       data->current_entry,
00373                       data->pcontext,
00374                       popen_owner,
00375                       &lock_desc);
00376 
00377               return res_LOCK4.status;
00378             }
00379         }
00380 
00381       /* Prepare state management structure */
00382       memset(&candidate_type, 0, sizeof(candidate_type));
00383       candidate_type = STATE_TYPE_LOCK;
00384       candidate_data.lock.popenstate = pstate_open;
00385 
00386       /* Add the lock state to the lock table */
00387       if(state_add(data->current_entry,
00388                    candidate_type,
00389                    &candidate_data,
00390                    plock_owner,
00391                    data->pcontext,
00392                    &plock_state, &state_status) != STATE_SUCCESS)
00393         {
00394           res_LOCK4.status = NFS4ERR_RESOURCE;
00395 
00396           LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
00397                   "LOCK failed to add new stateid",
00398                   data->current_entry,
00399                   data->pcontext,
00400                   plock_owner,
00401                   &lock_desc);
00402 
00403           dec_state_owner_ref(plock_owner);
00404 
00405           return res_LOCK4.status;
00406         }
00407 
00408       init_glist(&plock_state->state_data.lock.state_locklist);
00409 
00410       /* Attach this lock to an export */
00411       plock_state->state_pexport = data->pexport;
00412       P(data->pexport->exp_state_mutex);
00413       glist_add_tail(&data->pexport->exp_state_list, &plock_state->state_export_list);
00414       V(data->pexport->exp_state_mutex);
00415 
00416       /* Add lock state to the list of lock states belonging to the open state */
00417       glist_add_tail(&pstate_open->state_data.share.share_lockstates,
00418                      &plock_state->state_data.lock.state_sharelist);
00419     }                           /* if( arg_LOCK4.locker.new_lock_owner ) */
00420 
00421   /* Now we have a lock owner and a stateid.
00422    * Go ahead and push lock into SAL (and FSAL).
00423    */
00424   if(state_lock(data->current_entry,
00425                 data->pcontext,
00426                 data->pexport,
00427                 plock_owner,
00428                 plock_state,
00429                 blocking,
00430                 NULL,     /* No block data for now */
00431                 &lock_desc,
00432                 &conflict_owner,
00433                 &conflict_desc,
00434                 &state_status) != STATE_SUCCESS)
00435     {
00436       if(state_status == STATE_LOCK_CONFLICT)
00437         {
00438           /* A  conflicting lock from a different lock_owner, returns NFS4ERR_DENIED */
00439           Process_nfs4_conflict(&res_LOCK4.LOCK4res_u.denied,
00440                                 conflict_owner,
00441                                 &conflict_desc);
00442         }
00443 
00444       LogDebug(COMPONENT_NFS_V4_LOCK,
00445                "LOCK failed with status %s",
00446                state_err_str(state_status));
00447 
00448       res_LOCK4.status = nfs4_Errno_state(state_status);
00449 
00450       if(arg_LOCK4.locker.new_lock_owner)
00451         {
00452           /* Need to destroy lock owner and state */
00453           if(state_del(plock_state,
00454                        &state_status) != STATE_SUCCESS)
00455             LogDebug(COMPONENT_NFS_V4_LOCK,
00456                      "state_del failed with status %s",
00457                      state_err_str(state_status));
00458         }
00459 
00460       return res_LOCK4.status;
00461     }
00462 
00463   res_LOCK4.status = NFS4_OK;
00464 
00465   /* Handle stateid/seqid for success */
00466   update_stateid(plock_state,
00467                  &res_LOCK4.LOCK4res_u.resok4.lock_stateid,
00468                  data,
00469                  tag);
00470 
00471   LogFullDebug(COMPONENT_NFS_V4_LOCK,
00472                "LOCK state_seqid = %u, plock_state = %p",
00473                plock_state->state_seqid,
00474                plock_state);
00475 
00476   LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG,
00477           "LOCK applied",
00478           data->current_entry,
00479           data->pcontext,
00480           plock_owner,
00481           &lock_desc);
00482 
00483   return res_LOCK4.status;
00484 
00485 }                               /* nfs41_op_lock */
00486 
00497 void nfs41_op_lock_Free(LOCK4res * resp)
00498 {
00499   if(resp->status == NFS4ERR_DENIED)
00500     Release_nfs4_denied(&resp->LOCK4res_u.denied);
00501   return;
00502 }                               /* nfs41_op_lock_Free */