nfs-ganesha 1.4

nfs4_state_id.c

Go to the documentation of this file.
00001 
00032 #ifdef HAVE_CONFIG_H
00033 #include "config.h"
00034 #endif
00035 
00036 #ifdef _SOLARIS
00037 #include "solaris_port.h"
00038 #endif
00039 
00040 #include <stdio.h>
00041 #include <sys/types.h>
00042 #include <ctype.h>              /* for having isalnum */
00043 #include <stdlib.h>             /* for having atoi */
00044 #include <dirent.h>             /* for having MAXNAMLEN */
00045 #include <netdb.h>
00046 #include <netinet/in.h>
00047 #include <arpa/inet.h>
00048 #include <string.h>
00049 #include <pthread.h>
00050 #include <fcntl.h>
00051 #include <sys/file.h>           /* for having FNDELAY */
00052 #include <pwd.h>
00053 #include <grp.h>
00054 #include <pthread.h>
00055 #include "log.h"
00056 #include "ganesha_rpc.h"
00057 #include "HashData.h"
00058 #include "HashTable.h"
00059 #include "nfs_core.h"
00060 #include "nfs4.h"
00061 #include "fsal.h"
00062 #include "nfs_tools.h"
00063 #include "nfs_exports.h"
00064 #include "nfs_file_handle.h"
00065 #include "sal_functions.h"
00066 
00067 size_t strnlen(const char *s, size_t maxlen);
00068 
00069 hash_table_t *ht_state_id;
00070 
00071 char all_zero[OTHERSIZE];
00072 char all_one[OTHERSIZE];
00073 #define seqid_all_one 0xFFFFFFFF
00074 
00075 pthread_mutex_t StateIdMutex = PTHREAD_MUTEX_INITIALIZER;
00076 uint64_t state_id_counter;
00077 
00078 int display_stateid_other(char * other, char * str)
00079 {
00080   uint32_t epoch = *((uint32_t *)other);
00081   uint64_t count = *((uint64_t *)(other + sizeof(uint32_t)));
00082   return sprintf(str, "epoch=0x%08x counter=0x%016llx",
00083                  (unsigned int) epoch, (unsigned long long) count);
00084 }
00085 
00086 int display_state_id_key(hash_buffer_t * pbuff, char *str)
00087 {
00088   return display_stateid_other(pbuff->pdata, str);
00089 }                               /* display_state_id_val */
00090 
00091 int display_state_id_val(hash_buffer_t * pbuff, char *str)
00092 {
00093   state_t *pstate = (state_t *) (pbuff->pdata);
00094 
00095   return sprintf(str,
00096                  "state %p is associated with pentry=%p type=%u seqid=%u",
00097                  pstate,
00098                  pstate->state_pentry,
00099                  pstate->state_type,
00100                  pstate->state_seqid);
00101 }                               /* display_state_id_val */
00102 
00103 int compare_state_id(hash_buffer_t * buff1, hash_buffer_t * buff2)
00104 {
00105   if(isFullDebug(COMPONENT_STATE))
00106     {
00107       char str1[OTHERSIZE * 2 + 32], str2[OTHERSIZE * 2 + 32];
00108 
00109       display_stateid_other(buff1->pdata, str1);
00110       display_stateid_other(buff2->pdata, str2);
00111 
00112       if(isDebug(COMPONENT_HASHTABLE))
00113         LogFullDebug(COMPONENT_STATE,
00114                      "{%s} vs {%s}",
00115                      str1, str2);
00116     }
00117 
00118   return memcmp(buff1->pdata, buff2->pdata, OTHERSIZE);
00119 }                               /* compare_state_id */
00120 
00121 inline uint32_t compute_stateid_hash_value(uint32_t * pstate)
00122 {
00123   return pstate[1] ^ pstate[2];
00124 }
00125 
00126 uint32_t state_id_value_hash_func(hash_parameter_t * p_hparam,
00127                                   hash_buffer_t    * buffclef)
00128 {
00129   uint32_t val = compute_stateid_hash_value((uint32_t *) buffclef->pdata) %
00130                       p_hparam->index_size;
00131 
00132   if(isDebug(COMPONENT_HASHTABLE))
00133     LogFullDebug(COMPONENT_STATE, "val = %"PRIu32, val);
00134 
00135   return val;
00136 }
00137 
00138 uint64_t state_id_rbt_hash_func(hash_parameter_t * p_hparam,
00139                                 hash_buffer_t    * buffclef)
00140 {
00141   uint64_t val = compute_stateid_hash_value((uint32_t *) buffclef->pdata);
00142 
00143   if(isDebug(COMPONENT_HASHTABLE))
00144     LogFullDebug(COMPONENT_STATE, "rbt = %"PRIu64, val);
00145 
00146   return val;
00147 }
00148 
00160 int nfs4_Init_state_id(nfs_state_id_parameter_t param)
00161 {
00162   /* Init  all_one */
00163   memset(all_zero, 0, OTHERSIZE);
00164   memset(all_one, 0xFF, OTHERSIZE);
00165 
00166   if((ht_state_id = HashTable_Init(&param.hash_param)) == NULL)
00167     {
00168       LogCrit(COMPONENT_STATE, "Cannot init State Id cache");
00169       return -1;
00170     }
00171 
00172   return 0;
00173 }                               /* nfs_Init_client_id */
00174 
00184 void nfs4_BuildStateId_Other(char * other)
00185 {
00186   /* Use only 32 bits of server epoch */
00187   uint32_t epoch = (uint32_t) ServerEpoch;
00188   memcpy(other, &epoch, sizeof(uint32_t));
00189 
00190   P(StateIdMutex);
00191   memcpy(other + sizeof(uint32_t), &state_id_counter, sizeof(uint64_t));
00192   state_id_counter++;
00193   V(StateIdMutex);
00194 }
00195 
00207 int nfs4_State_Set(char other[OTHERSIZE], state_t * pstate_data)
00208 {
00209   hash_buffer_t buffkey;
00210   hash_buffer_t buffval;
00211 
00212   if((buffkey.pdata = gsh_malloc(OTHERSIZE)) == NULL)
00213     return 0;
00214 
00215   LogFullDebug(COMPONENT_STATE,
00216                "Allocating stateid key %p", buffkey.pdata);
00217 
00218   memcpy(buffkey.pdata, other, OTHERSIZE);
00219   buffkey.len = OTHERSIZE;
00220 
00221   buffval.pdata = (caddr_t) pstate_data;
00222   buffval.len = sizeof(state_t);
00223 
00224   if(HashTable_Test_And_Set(ht_state_id,
00225                             &buffkey,
00226                             &buffval,
00227                             HASHTABLE_SET_HOW_SET_NO_OVERWRITE) != HASHTABLE_SUCCESS)
00228     {
00229       LogDebug(COMPONENT_STATE,
00230                "HashTable_Test_And_Set failed for key %p",
00231                buffkey.pdata);
00232       gsh_free(buffkey.pdata);
00233       return 0;
00234     }
00235 
00236   return 1;
00237 }                               /* nfs4_State_Set */
00238 
00251 int nfs4_State_Get_Pointer(char other[OTHERSIZE], state_t * *pstate_data)
00252 {
00253   hash_buffer_t buffkey;
00254   hash_buffer_t buffval;
00255   int           rc;
00256 
00257   buffkey.pdata = (caddr_t) other;
00258   buffkey.len = OTHERSIZE;
00259 
00260   rc = HashTable_Get(ht_state_id, &buffkey, &buffval);
00261   if(rc != HASHTABLE_SUCCESS)
00262     {
00263       LogDebug(COMPONENT_STATE,
00264                "HashTable_Get returned %d", rc);
00265       return 0;
00266     }
00267 
00268   *pstate_data = (state_t *) buffval.pdata;
00269 
00270   return 1;
00271 }                               /* nfs4_State_Get_Pointer */
00272 
00284 int nfs4_State_Del(char other[OTHERSIZE])
00285 {
00286   hash_buffer_t buffkey, old_key, old_value;
00287 
00288   buffkey.pdata = (caddr_t) other;
00289   buffkey.len = OTHERSIZE;
00290 
00291   if(HashTable_Del(ht_state_id, &buffkey, &old_key, &old_value) == HASHTABLE_SUCCESS)
00292     {
00293       /* free the key that was stored in hash table */
00294       LogFullDebug(COMPONENT_STATE,
00295                    "Freeing stateid key %p", old_key.pdata);
00296       gsh_free(old_key.pdata);
00297 
00298       /* State is managed in stuff alloc, no fre is needed for old_value.pdata */
00299 
00300       return 1;
00301     }
00302   else
00303     return 0;
00304 }                               /* nfs4_State_Del */
00305 
00317 int nfs4_Check_Stateid(stateid4        * pstate,
00318                        cache_entry_t   * pentry,
00319                        state_t        ** ppstate,
00320                        compound_data_t * data,
00321                        char              flags,
00322                        const char      * tag)
00323 {
00324   uint32_t          epoch = 0;
00325   state_t         * pstate2;
00326   char              str[OTHERSIZE * 2 + 1 + 6];
00327   int32_t           diff;
00328 
00329   *ppstate = NULL;
00330   data->current_stateid_valid = FALSE;
00331 
00332   if(pstate == NULL)
00333     return NFS4ERR_SERVERFAULT;
00334 
00335   if(pentry == NULL)
00336     return NFS4ERR_SERVERFAULT;
00337 
00338   if(pentry->type != REGULAR_FILE)
00339     return NFS4ERR_SERVERFAULT;
00340 
00341   if(isDebug(COMPONENT_STATE))
00342     {
00343       sprint_mem(str, (char *)pstate->other, OTHERSIZE);
00344       sprintf(str + OTHERSIZE * 2, ":%u", (unsigned int) pstate->seqid);
00345     }
00346 
00347   /* Test for OTHER is all zeros */
00348   if(memcmp(pstate->other, all_zero, OTHERSIZE) == 0)
00349     {
00350       if(pstate->seqid == 0 && (flags & STATEID_SPECIAL_ALL_0) != 0)
00351         {
00352           /* All 0 stateid */
00353           LogDebug(COMPONENT_STATE,
00354                    "Check %s stateid found special all 0 stateid", tag);
00355           /* TODO FSF: eventually this may want to return an actual state for
00356            * use in temporary locks for I/O.
00357            */
00358           return NFS4_OK;
00359         }
00360       if(pstate->seqid == 1 && (flags & STATEID_SPECIAL_CURRENT) != 0)
00361         {
00362           /* Special current stateid */
00363           LogDebug(COMPONENT_STATE,
00364                    "Check %s stateid found special 'current' stateid", tag);
00365           /* Copy current stateid in and proceed to checks */
00366           *pstate = data->current_stateid;
00367         }
00368 
00369       LogDebug(COMPONENT_STATE,
00370                "Check %s stateid with OTHER all zeros, seqid %u unexpected",
00371                tag, (unsigned int) pstate->seqid);
00372       return NFS4ERR_BAD_STATEID;
00373     }
00374 
00375   /* Test for OTHER is all ones */
00376   if(memcmp(pstate->other, all_one, OTHERSIZE) == 0)
00377     {
00378       /* Test for special all ones stateid */
00379       if(pstate->seqid == seqid_all_one && (flags & STATEID_SPECIAL_ALL_1) != 0)
00380         {
00381           /* All 1 stateid */
00382           LogDebug(COMPONENT_STATE,
00383                    "Check %s stateid found special all 1 stateid", tag);
00384           /* TODO FSF: eventually this may want to return an actual state for
00385            * use in temporary locks for I/O.
00386            */
00387           return NFS4_OK;
00388         }
00389 
00390       LogDebug(COMPONENT_STATE,
00391                "Check %s stateid with OTHER all ones, seqid %u unexpected",
00392                tag, (unsigned int) pstate->seqid);
00393       return NFS4ERR_BAD_STATEID;
00394     }
00395 
00396   /* Check if stateid was made from this server instance */
00397   memcpy(&epoch, pstate->other, sizeof(uint32_t));
00398 
00399   if(epoch != ServerEpoch)
00400     {
00401       LogDebug(COMPONENT_STATE,
00402                "Check %s stateid found stale stateid %s", tag, str);
00403       return NFS4ERR_STALE_STATEID;
00404     }
00405 
00406   /* Try to get the related state */
00407   if(!nfs4_State_Get_Pointer(pstate->other, &pstate2))
00408     {
00409       /* State not found : return NFS4ERR_BAD_STATEID, RFC3530 page 129 */
00410       LogDebug(COMPONENT_STATE,
00411                "Check %s stateid could not find state %s", tag, str);
00412       if(nfs_param.nfsv4_param.return_bad_stateid == TRUE)      /* Dirty work-around for HPC environment */
00413         return NFS4ERR_BAD_STATEID;
00414       else
00415         return NFS4_OK;
00416     }
00417 
00418   /* Now, if this lease is not already reserved, reserve it */
00419   if(data->preserved_clientid != pstate2->state_powner->so_owner.so_nfs4_owner.so_pclientid)
00420     {
00421       if(data->preserved_clientid != NULL)
00422         {
00423           /* We don't ever expect this to happen, but, just in case...
00424            * Update and release already reserved lease.
00425            */
00426          P(data->preserved_clientid->cid_mutex);
00427 
00428          update_lease(data->preserved_clientid);
00429 
00430          V(data->preserved_clientid->cid_mutex);
00431 
00432          data->preserved_clientid = NULL;
00433         }
00434 
00435       /* Check if lease is expired and reserve it */
00436       P(pstate2->state_powner->so_owner.so_nfs4_owner.so_pclientid->cid_mutex);
00437 
00438       if(!reserve_lease(pstate2->state_powner->so_owner.so_nfs4_owner.so_pclientid))
00439         {
00440           LogDebug(COMPONENT_STATE,
00441                    "Returning NFS4ERR_EXPIRED");
00442 
00443           V(pstate2->state_powner->so_owner.so_nfs4_owner.so_pclientid->cid_mutex);
00444 
00445           dec_client_id_ref(pstate2->state_powner->so_owner.so_nfs4_owner.so_pclientid);
00446 
00447           return NFS4ERR_EXPIRED;
00448         }
00449 
00450       data->preserved_clientid = pstate2->state_powner->so_owner.so_nfs4_owner.so_pclientid;
00451 
00452       V(pstate2->state_powner->so_owner.so_nfs4_owner.so_pclientid->cid_mutex);
00453     }
00454 
00455   /* Sanity check : Is this the right file ? */
00456   if(pstate2->state_pentry != pentry)
00457     {
00458       LogDebug(COMPONENT_STATE,
00459                "Check %s stateid found stateid %s has wrong file", tag, str);
00460       return NFS4ERR_BAD_STATEID;
00461     }
00462 
00463   /* Whether stateid.seqid may be zero depends on the state type
00464      exclusively, See RFC 5661 pp. 161,287-288. */
00465   if((pstate2->state_type == STATE_TYPE_LAYOUT) ||
00466      (pstate->seqid != 0))
00467     {
00468       /* Check seqid in stateid */
00472       diff = pstate->seqid - pstate2->state_seqid;
00473       if(diff < 0)
00474         {
00475           /* OLD_STATEID */
00476           LogDebug(COMPONENT_STATE,
00477                    "Check %s stateid found OLD stateid %s, expected seqid %u",
00478                    tag, str, (unsigned int) pstate2->state_seqid);
00479           return NFS4ERR_OLD_STATEID;
00480         }
00481       else if(diff > 0)
00482         {
00483           /* BAD_STATEID */
00484           LogDebug(COMPONENT_STATE,
00485                    "Check %s stateid found BAD stateid %s, expected seqid %u",
00486                    tag, str, (unsigned int) pstate2->state_seqid);
00487           return NFS4ERR_BAD_STATEID;
00488         }
00489     }
00490 
00491   LogFullDebug(COMPONENT_STATE,
00492                "Check %s stateid found valid stateid %s - %p",
00493                tag, str, pstate2);
00494 
00495   /* Copy stateid into current for later use */
00496   data->current_stateid       = *pstate;
00497   data->current_stateid.seqid = pstate2->state_seqid;
00498   data->current_stateid_valid = TRUE;
00499 
00500   *ppstate = pstate2;
00501   return NFS4_OK;
00502 }                               /* nfs4_Check_Stateid */
00503 
00513 void nfs_State_PrintAll(void)
00514 {
00515   if(isFullDebug(COMPONENT_STATE))
00516     HashTable_Log(COMPONENT_STATE, ht_state_id);
00517 }                               /* nfs_State_PrintAll */
00518 
00519 void update_stateid(state_t         * pstate,
00520                     stateid4        * presp,
00521                     compound_data_t * data,
00522                     const char      * tag)
00523 {
00524   /* Increment state_seqid, handling wraparound */
00525   pstate->state_seqid += 1;
00526   if(pstate->state_seqid == 0)
00527     pstate->state_seqid = 1;
00528 
00529   /* Copy stateid into current for later use */
00530   data->current_stateid.seqid = pstate->state_seqid;
00531   memcpy(data->current_stateid.other, pstate->stateid_other, OTHERSIZE);
00532   data->current_stateid_valid = TRUE;
00533 
00534   /* Copy stateid into response */
00535   presp->seqid = pstate->state_seqid;
00536   memcpy(presp->other, pstate->stateid_other, OTHERSIZE);
00537 
00538   if(isFullDebug(COMPONENT_STATE))
00539     {
00540       char str[OTHERSIZE * 2 + 1 + 6];
00541       sprint_mem(str, (char *)pstate->stateid_other, OTHERSIZE);
00542       sprintf(str + OTHERSIZE * 2, ":%u", (unsigned int) pstate->state_seqid);
00543       LogDebug(COMPONENT_STATE,
00544                "Update %s stateid to %s for response",
00545                tag, str);
00546     }
00547 }
00548 
00559 int nfs4_check_special_stateid(cache_entry_t *pentry,
00560                                const char    *tag,
00561                                int access)
00562 {
00563 
00564   struct glist_head * glist;
00565   state_t           * pstate_iterate;
00566   int                 rc = NFS4_OK;
00567 
00568   if(pentry == NULL)
00569     {
00570       rc = NFS4ERR_SERVERFAULT;
00571       return rc;
00572     }
00573 
00574   /* Acquire lock to enter critical section on this entry */
00575   pthread_rwlock_rdlock(&pentry->state_lock);
00576 
00577   /* Iterate through file's state to look for conflicts */
00578   glist_for_each(glist, &pentry->state_list)
00579     {
00580       pstate_iterate = glist_entry(glist, state_t, state_list);
00581 
00582       switch(pstate_iterate->state_type)
00583         {
00584           case STATE_TYPE_SHARE:
00585             if((access == FATTR4_ATTR_READ) &&
00586                (pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_READ))
00587               {
00588                 /* Reading to this file is prohibited, file is read-denied */
00589                 rc = NFS4ERR_LOCKED;
00590                 LogDebug(COMPONENT_NFS_V4_LOCK,
00591                          "%s is denied by state %p",
00592                          tag,
00593                          pstate_iterate);
00594                 goto ssid_out;
00595               }
00596 
00597             if((access == FATTR4_ATTR_WRITE) &&
00598                (pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_WRITE))
00599               {
00600                 /* Writing to this file is prohibited, file is write-denied */
00601                 rc = NFS4ERR_LOCKED;
00602                 LogDebug(COMPONENT_NFS_V4_LOCK,
00603                          "%s is denied by state %p",
00604                          tag,
00605                          pstate_iterate);
00606                 goto ssid_out;
00607               }
00608 
00609             if((access == FATTR4_ATTR_READ_WRITE) &&
00610                (pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_BOTH))
00611               {
00612                 /* Reading and writing to this file is prohibited, file is rw-denied */
00613                 rc = NFS4ERR_LOCKED;
00614                 LogDebug(COMPONENT_NFS_V4_LOCK,
00615                          "%s is denied by state %p",
00616                          tag,
00617                          pstate_iterate);
00618                 goto ssid_out;
00619               }
00620 
00621             break;
00622 
00623           case STATE_TYPE_LOCK:
00624             /* Skip, will check for conflicting locks later */
00625             break;
00626 
00627           case STATE_TYPE_DELEG:
00628             // TODO FSF: should check for conflicting delegations, may need to recall
00629             break;
00630 
00631           case STATE_TYPE_LAYOUT:
00632             // TODO FSF: should check for conflicting layouts, may need to recall
00633             // Need to look at this even for NFS v4 WRITE since there may be NFS v4.1 users of the file
00634             break;
00635 
00636           case STATE_TYPE_NONE:
00637             break;
00638 
00639           default:
00640             break;
00641         }
00642     }
00643   // TODO FSF: need to check against existing locks
00644 
00645  ssid_out:  // Use this exit point if the lock was already acquired.
00646   pthread_rwlock_unlock(&pentry->state_lock);
00647   return rc;
00648 }