nfs-ganesha 1.4

nfs_Rename.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 <fcntl.h>
00049 #include <sys/file.h>           /* for having FNDELAY */
00050 #include "HashData.h"
00051 #include "HashTable.h"
00052 #include "log.h"
00053 #include "ganesha_rpc.h"
00054 #include "nfs23.h"
00055 #include "nfs4.h"
00056 #include "mount.h"
00057 #include "nfs_core.h"
00058 #include "cache_inode.h"
00059 #include "nfs_exports.h"
00060 #include "nfs_creds.h"
00061 #include "nfs_proto_functions.h"
00062 #include "nfs_tools.h"
00063 #include "nfs_proto_tools.h"
00064 
00084 int nfs_Rename(nfs_arg_t *parg,
00085                exportlist_t *pexport,
00086                fsal_op_context_t *pcontext,
00087                nfs_worker_data_t *pworker,
00088                struct svc_req *preq,
00089                nfs_res_t *pres)
00090 {
00091   char *str_entry_name = NULL;
00092   fsal_name_t entry_name;
00093   char *str_new_entry_name = NULL;
00094   fsal_name_t new_entry_name;
00095   cache_entry_t *parent_pentry = NULL;
00096   cache_entry_t *new_parent_pentry = NULL;
00097   cache_entry_t *should_not_exists = NULL;
00098   cache_entry_t *should_exists = NULL;
00099   cache_inode_status_t cache_status;
00100   fsal_attrib_list_t *ppre_attr;
00101   fsal_attrib_list_t pre_attr;
00102   fsal_attrib_list_t *pnew_pre_attr;
00103   fsal_attrib_list_t new_attr;
00104   fsal_attrib_list_t new_parent_attr;
00105   fsal_attrib_list_t attr;
00106   fsal_attrib_list_t tst_attr;
00107   cache_inode_file_type_t parent_filetype;
00108   cache_inode_file_type_t new_parent_filetype;
00109   int rc = NFS_REQ_OK;
00110 
00111   if(isDebug(COMPONENT_NFSPROTO))
00112     {
00113      char strto[LEN_FH_STR], strfrom[LEN_FH_STR];
00114 
00115       switch (preq->rq_vers)
00116         {
00117         case NFS_V2:
00118             str_entry_name = parg->arg_rename2.from.name;
00119             str_new_entry_name = parg->arg_rename2.to.name;
00120             break;
00121 
00122         case NFS_V3:
00123             str_entry_name = parg->arg_rename3.from.name;
00124             str_new_entry_name = parg->arg_rename3.to.name;
00125             break;
00126         }
00127 
00128       nfs_FhandleToStr(preq->rq_vers,
00129                        &(parg->arg_rename2.from.dir),
00130                        &(parg->arg_rename3.from.dir),
00131                        NULL,
00132                        strfrom);
00133 
00134       nfs_FhandleToStr(preq->rq_vers,
00135                        &(parg->arg_rename2.to.dir),
00136                        &(parg->arg_rename3.to.dir),
00137                        NULL,
00138                        strto);
00139       LogDebug(COMPONENT_NFSPROTO,
00140                "REQUEST PROCESSING: Calling nfs_Rename from handle: %s name %s to handle: %s name: %s",
00141                strfrom, str_entry_name, strto, str_new_entry_name);
00142     }
00143 
00144   if(preq->rq_vers == NFS_V3)
00145     {
00146       /* to avoid setting it on each error case */
00147       pres->res_rename3.RENAME3res_u.resfail.fromdir_wcc.before.attributes_follow = FALSE;
00148       pres->res_rename3.RENAME3res_u.resfail.fromdir_wcc.after.attributes_follow = FALSE;
00149       pres->res_rename3.RENAME3res_u.resfail.todir_wcc.before.attributes_follow = FALSE;
00150       pres->res_rename3.RENAME3res_u.resfail.todir_wcc.after.attributes_follow = FALSE;
00151       ppre_attr = NULL;
00152       pnew_pre_attr = NULL;
00153     }
00154 
00155   /* Convert fromdir file handle into a cache_entry */
00156   if((parent_pentry = nfs_FhandleToCache(preq->rq_vers,
00157                                          &(parg->arg_rename2.from.dir),
00158                                          &(parg->arg_rename3.from.dir),
00159                                          NULL,
00160                                          &(pres->res_dirop2.status),
00161                                          &(pres->res_create3.status),
00162                                          NULL,
00163                                          &pre_attr, pcontext, &rc)) == NULL)
00164     {
00165       /* Stale NFS FH ? */
00166       goto out;
00167     }
00168 
00169   /* Convert todir file handle into a cache_entry */
00170   if((new_parent_pentry = nfs_FhandleToCache(preq->rq_vers,
00171                                              &(parg->arg_rename2.to.dir),
00172                                              &(parg->arg_rename3.to.dir),
00173                                              NULL,
00174                                              &(pres->res_dirop2.status),
00175                                              &(pres->res_create3.status),
00176                                              NULL,
00177                                              &new_parent_attr,
00178                                              pcontext, &rc)) == NULL)
00179     {
00180       /* Stale NFS FH ? */
00181       goto out;
00182     }
00183 
00184   /* get the attr pointers */
00185   ppre_attr = &pre_attr;
00186   pnew_pre_attr = &new_parent_attr;
00187 
00188   /* Get the filetypes */
00189   parent_filetype = cache_inode_fsal_type_convert(pre_attr.type);
00190   new_parent_filetype = cache_inode_fsal_type_convert(new_parent_attr.type);
00191 
00192   /*
00193    * Sanity checks: we must manage directories
00194    */
00195   if((parent_filetype != DIRECTORY) ||
00196      (new_parent_filetype != DIRECTORY))
00197     {
00198       switch (preq->rq_vers)
00199         {
00200         case NFS_V2:
00201           pres->res_stat2 = NFSERR_NOTDIR;
00202           break;
00203 
00204         case NFS_V3:
00205           pres->res_rename3.status = NFS3ERR_NOTDIR;
00206           break;
00207         }
00208 
00209       rc = NFS_REQ_OK;
00210       goto out;
00211     }
00212 
00213   switch (preq->rq_vers)
00214     {
00215     case NFS_V2:
00216       str_entry_name = parg->arg_rename2.from.name;
00217       str_new_entry_name = parg->arg_rename2.to.name;
00218       break;
00219 
00220     case NFS_V3:
00221       str_entry_name = parg->arg_rename3.from.name;
00222       str_new_entry_name = parg->arg_rename3.to.name;
00223       break;
00224     }
00225 
00226   if(str_entry_name == NULL ||
00227      *str_entry_name == '\0' ||
00228      str_new_entry_name == NULL ||
00229      *str_new_entry_name == '\0' ||
00230      FSAL_IS_ERROR(FSAL_str2name(str_entry_name, FSAL_MAX_NAME_LEN, &entry_name)) ||
00231      FSAL_IS_ERROR(FSAL_str2name(str_new_entry_name, FSAL_MAX_NAME_LEN, &new_entry_name)))
00232     {
00233       cache_status = CACHE_INODE_INVALID_ARGUMENT;
00234     }
00235   else
00236     {
00237       /*
00238        * Lookup file to see if new entry exists
00239        *
00240        */
00241       should_not_exists = cache_inode_lookup(new_parent_pentry,
00242                                              &new_entry_name,
00243                                              &tst_attr,
00244                                              pcontext,
00245                                              &cache_status);
00246 
00247       if(cache_status == CACHE_INODE_NOT_FOUND)
00248         {
00249           /* We need to lookup over the old entry also */
00250           should_exists = cache_inode_lookup(parent_pentry,
00251                                              &entry_name,
00252                                              &tst_attr,
00253                                              pcontext,
00254                                              &cache_status);
00255 
00256           /* Rename entry */
00257           if(cache_status == CACHE_INODE_SUCCESS)
00258             cache_inode_rename(parent_pentry,
00259                                &entry_name,
00260                                new_parent_pentry,
00261                                &new_entry_name,
00262                                &attr, &new_attr,
00263                                pcontext, &cache_status);
00264 
00265           if(cache_status == CACHE_INODE_SUCCESS)
00266             {
00267               switch (preq->rq_vers)
00268                 {
00269                 case NFS_V2:
00270                   pres->res_stat2 = NFS_OK;
00271                   break;
00272 
00273                 case NFS_V3:
00274                   /*
00275                    * Build Weak Cache Coherency
00276                    * data 
00277                    */
00278                   nfs_SetWccData(pexport,
00279                                  ppre_attr,
00280                                  &attr,
00281                                  &(pres->res_rename3.RENAME3res_u.resok.fromdir_wcc));
00282 
00283                   nfs_SetWccData(pexport,
00284                                  pnew_pre_attr,
00285                                  &new_attr,
00286                                  &(pres->res_rename3.RENAME3res_u.resok.todir_wcc));
00287 
00288                   pres->res_rename3.status = NFS3_OK;
00289                   break;
00290 
00291                 }
00292 
00293               rc = NFS_REQ_OK;
00294               goto out;
00295             }
00296         }
00297       else
00298         {
00299           /* If name are the same (basically renaming a/file1 to a/file1, this is a non-erroneous situation to be managed */
00300           if(new_parent_pentry == parent_pentry)
00301             {
00302               if(!FSAL_namecmp(&new_entry_name, &entry_name))
00303                 {
00304                   /* trying to rename a file to himself, this is allowed */
00305                   cache_status = CACHE_INODE_SUCCESS;
00306                   switch (preq->rq_vers)
00307                     {
00308                     case NFS_V2:
00309                       pres->res_stat2 = NFS_OK;
00310                       break;
00311 
00312                     case NFS_V3:
00313                       /*
00314                        * Build Weak Cache Coherency
00315                        * data 
00316                        */
00317                       nfs_SetWccData(pexport,
00318                                      ppre_attr,
00319                                      &attr,
00320                                      &(pres->res_rename3.RENAME3res_u.resok.fromdir_wcc));
00321 
00322                       nfs_SetWccData(pexport,
00323                                      ppre_attr,
00324                                      &attr,
00325                                      &(pres->res_rename3.RENAME3res_u.resok.todir_wcc));
00326 
00327                       pres->res_rename3.status = NFS3_OK;
00328                       break;
00329 
00330                     }
00331 
00332                   rc = NFS_REQ_OK;
00333                   goto out;
00334                 }
00335 
00336             }
00337 
00338           /* New entry already exists. In this case (see RFC), entry
00339            * should be compatible: Both are non-directories or both
00340            * are directories and 'todir' is empty. If compatible, old
00341            * 'todir' entry is scratched, if not returns EEXISTS */
00342           if(should_not_exists != NULL)
00343             {
00344               /* We need to lookup over the old entry also */
00345               if((should_exists = cache_inode_lookup(parent_pentry,
00346                                                      &entry_name,
00347                                                      &tst_attr,
00348                                                      pcontext,
00349                                                      &cache_status))
00350                  != NULL)
00351                 {
00352                   /* If pentry is the same for source and target, then
00353                    * we are trying to rename a hard link to another
00354                    * hard link with the same inode. This is a noop. */
00355                   if (should_not_exists == should_exists)
00356                     {
00357                       switch (preq->rq_vers)
00358                         {
00359                         case NFS_V2:
00360                           pres->res_stat2 = NFS_OK;
00361                           break;
00362 
00363                         case NFS_V3:
00364                           /*
00365                            * Build Weak Cache Coherency
00366                            * data 
00367                            */
00368                           nfs_SetWccData(pexport,
00369                                          ppre_attr,
00370                                          ppre_attr, /* Attributes before and after will be unchanged. */
00371                                          &(pres->res_rename3.RENAME3res_u.resok.
00372                                            fromdir_wcc));
00373                           
00374                           nfs_SetWccData(pexport,
00375                                          ppre_attr,
00376                                          ppre_attr, /* Attributes before and after will be unchanged. */
00377                                          &(pres->res_rename3.RENAME3res_u.resok.
00378                                            todir_wcc));
00379                           
00380                           pres->res_rename3.status = NFS3_OK;
00381                           break;
00382                           
00383                         }
00384                       
00385                       rc = NFS_REQ_OK;
00386                       goto out;
00387                     }
00388                   
00389                   if(cache_inode_types_are_rename_compatible
00390                      (should_exists, should_not_exists))
00391                     {
00392                       /* Remove the old entry before renaming it */
00393                       if(cache_inode_remove(new_parent_pentry,
00394                                             &new_entry_name,
00395                                             &tst_attr,
00396                                             pcontext,
00397                                             &cache_status) == CACHE_INODE_SUCCESS)
00398                         {
00399                           if(cache_inode_rename(parent_pentry,
00400                                                 &entry_name,
00401                                                 new_parent_pentry,
00402                                                 &new_entry_name,
00403                                                 &attr,
00404                                                 &new_attr,
00405                                                 pcontext,
00406                                                 &cache_status) == CACHE_INODE_SUCCESS)
00407                             {
00408                               switch (preq->rq_vers)
00409                                 {
00410                                 case NFS_V2:
00411                                   pres->res_stat2 = NFS_OK;
00412                                   break;
00413 
00414                                 case NFS_V3:
00415                                   /*
00416                                    * Build Weak Cache Coherency
00417                                    * data 
00418                                    */
00419                                   nfs_SetWccData(pexport,
00420                                                  ppre_attr,
00421                                                  &attr,
00422                                                  &(pres->res_rename3.RENAME3res_u.resok.
00423                                                    fromdir_wcc));
00424 
00425                                   nfs_SetWccData(pexport,
00426                                                  ppre_attr,
00427                                                  &attr,
00428                                                  &(pres->res_rename3.RENAME3res_u.resok.
00429                                                    todir_wcc));
00430 
00431                                   pres->res_rename3.status = NFS3_OK;
00432                                   break;
00433 
00434                                 }
00435 
00436                               rc = NFS_REQ_OK;
00437                               goto out;
00438                             }
00439                         }
00440 
00441                     }
00442                 }
00443             }
00444 
00445           /* If this point is reached, then destination object already
00446              exists with that name in the directory and types are not
00447              compatible, we should return that the file exists */
00448           cache_status = CACHE_INODE_ENTRY_EXISTS;
00449         }                       /* if( should_not_exists != NULL ) */
00450     }
00451 
00452   /* If we are here, there was an error */
00453   if(nfs_RetryableError(cache_status))
00454     {
00455       rc = NFS_REQ_DROP;
00456       goto out;
00457     }
00458 
00459   nfs_SetFailedStatus(pcontext, pexport,
00460                       preq->rq_vers,
00461                       cache_status,
00462                       &pres->res_stat2,
00463                       &pres->res_rename3.status,
00464                       NULL, NULL,
00465                       parent_pentry,
00466                       ppre_attr,
00467                       &(pres->res_rename3.RENAME3res_u.resfail.fromdir_wcc),
00468                       new_parent_pentry,
00469                       pnew_pre_attr, &(pres->res_rename3.RENAME3res_u.resfail.todir_wcc));
00470 
00471   rc = NFS_REQ_OK;
00472 
00473 out:
00474   if (parent_pentry)
00475       cache_inode_put(parent_pentry);
00476 
00477   if (new_parent_pentry)
00478       cache_inode_put(new_parent_pentry);
00479 
00480   if (should_not_exists)
00481       cache_inode_put(should_not_exists);
00482 
00483   if (should_exists)
00484       cache_inode_put(should_exists);
00485 
00486   return (rc);
00487 
00488 }                               /* nfs_Rename */
00489 
00498 void nfs_Rename_Free(nfs_res_t * resp)
00499 {
00500   /* Nothing to do here */
00501   return;
00502 }                               /* nfs_Rename_Free */