nfs-ganesha 1.4

nfs4_op_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 "nfs4.h"
00055 #include "nfs_core.h"
00056 #include "sal_functions.h"
00057 #include "nfs_proto_functions.h"
00058 #include "nfs_proto_tools.h"
00059 #include "nfs_tools.h"
00060 #include "nfs_file_handle.h"
00061 #include "sal_functions.h"
00062 
00076 #define arg_RENAME4 op->nfs_argop4_u.oprename
00077 #define res_RENAME4 resp->nfs_resop4_u.oprename
00078 
00079 int nfs4_op_rename(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
00080 {
00081   char __attribute__ ((__unused__)) funcname[] = "nfs4_op_rename";
00082 
00083   cache_entry_t        * dst_entry = NULL;
00084   cache_entry_t        * src_entry = NULL;
00085   cache_entry_t        * tst_entry_dst = NULL;
00086   cache_entry_t        * tst_entry_src = NULL;
00087   fsal_attrib_list_t     attr_dst;
00088   fsal_attrib_list_t     attr_src;
00089   fsal_attrib_list_t     attr_tst_dst;
00090   fsal_attrib_list_t     attr_tst_src;
00091   cache_inode_status_t   cache_status;
00092   fsal_status_t          fsal_status;
00093   fsal_handle_t        * handlenew = NULL;
00094   fsal_handle_t        * handleold = NULL;
00095   fsal_name_t            oldname;
00096   fsal_name_t            newname;
00097 
00098   resp->resop = NFS4_OP_RENAME;
00099   res_RENAME4.status = NFS4_OK;
00100 
00101   /* Read oldname and newname from uft8 strings, if one is empty then returns NFS4ERR_INVAL */
00102   if((arg_RENAME4.oldname.utf8string_len == 0)
00103      || (arg_RENAME4.newname.utf8string_len == 0))
00104     {
00105       res_RENAME4.status = NFS4ERR_INVAL;
00106       return NFS4ERR_INVAL;
00107     }
00108 
00109   /* Do basic checks on a filehandle */
00110   res_RENAME4.status = nfs4_sanity_check_FH(data, 0LL);
00111   if(res_RENAME4.status != NFS4_OK)
00112     return res_RENAME4.status;
00113 
00114   /* If there is no FH */
00115   if(nfs4_Is_Fh_Empty(&(data->savedFH)))
00116     {
00117       res_RENAME4.status = NFS4ERR_NOFILEHANDLE;
00118       return res_RENAME4.status;
00119     }
00120 
00121   /* If the filehandle is invalid */
00122   if(nfs4_Is_Fh_Invalid(&(data->savedFH)))
00123     {
00124       res_RENAME4.status = NFS4ERR_BADHANDLE;
00125       return res_RENAME4.status;
00126     }
00127 
00128   /* Tests if the Filehandle is expired (for volatile filehandle) */
00129   if(nfs4_Is_Fh_Expired(&(data->savedFH)))
00130     {
00131       res_RENAME4.status = NFS4ERR_FHEXPIRED;
00132       return res_RENAME4.status;
00133     }
00134 
00135   /* Pseudo Fs is explictely a Read-Only File system */
00136   if(nfs4_Is_Fh_Pseudo(&(data->currentFH)))
00137     {
00138       res_RENAME4.status = NFS4ERR_ROFS;
00139       return res_RENAME4.status;
00140     }
00141 
00142   if (nfs_in_grace())
00143     {
00144       res_RENAME4.status = NFS4ERR_GRACE;
00145       return res_RENAME4.status;
00146     }
00147 
00148   /* If data->exportp is null, a junction from pseudo fs was traversed, credp and exportp have to be updated */
00149   if(data->pexport == NULL)
00150     {
00151       res_RENAME4.status = nfs4_SetCompoundExport(data);
00152       if(res_RENAME4.status != NFS4_OK)
00153         return res_RENAME4.status;
00154     }
00155 
00156   /* Read oldname and newname from uft8 strings, if one is empty then returns NFS4ERR_INVAL */
00157   if((arg_RENAME4.oldname.utf8string_len > FSAL_MAX_NAME_LEN)
00158      || (arg_RENAME4.newname.utf8string_len > FSAL_MAX_NAME_LEN))
00159     {
00160       res_RENAME4.status = NFS4ERR_NAMETOOLONG;
00161       return NFS4ERR_INVAL;
00162     }
00163 
00164   /* get the names from the RPC input */
00165   if((cache_status =
00166       cache_inode_error_convert(FSAL_buffdesc2name
00167                                 ((fsal_buffdesc_t *) & arg_RENAME4.oldname,
00168                                  &oldname))) != CACHE_INODE_SUCCESS)
00169     {
00170       res_RENAME4.status = NFS4ERR_INVAL;
00171       return res_RENAME4.status;
00172     }
00173 
00174   if((cache_status =
00175       cache_inode_error_convert(FSAL_buffdesc2name
00176                                 ((fsal_buffdesc_t *) & arg_RENAME4.newname,
00177                                  &newname))) != CACHE_INODE_SUCCESS)
00178     {
00179       res_RENAME4.status = NFS4ERR_INVAL;
00180       return res_RENAME4.status;
00181     }
00182 
00183   /* Sanuty check: never rename to '.' or '..' */
00184   if(!FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT)
00185      || !FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT_DOT))
00186     {
00187       res_RENAME4.status = NFS4ERR_BADNAME;
00188       return res_RENAME4.status;
00189     }
00190 
00191   /* Sanuty check: never rename to '.' or '..' */
00192   if(!FSAL_namecmp(&oldname, (fsal_name_t *) & FSAL_DOT)
00193      || !FSAL_namecmp(&oldname, (fsal_name_t *) & FSAL_DOT_DOT))
00194     {
00195       res_RENAME4.status = NFS4ERR_BADNAME;
00196       return res_RENAME4.status;
00197     }
00198 
00199   /*
00200    * This operation renames 
00201    *             - the object in directory pointed by savedFH, named arg_RENAME4.oldname
00202    *       into
00203    *             - an object in directory pointed by currentFH, named arg_RENAME4.newname
00204    *
00205    * Because of this, we will use 2 entry and we have verified both currentFH and savedFH */
00206 
00207   /* No Cross Device */
00208   if(((file_handle_v4_t *) (data->currentFH.nfs_fh4_val))->exportid !=
00209      ((file_handle_v4_t *) (data->savedFH.nfs_fh4_val))->exportid)
00210     {
00211       res_RENAME4.status = NFS4ERR_XDEV;
00212       return res_RENAME4.status;
00213     }
00214 
00215   /* destination must be a directory */
00216   dst_entry = data->current_entry;
00217 
00218   if(data->current_filetype != DIRECTORY)
00219     {
00220       res_RENAME4.status = NFS4ERR_NOTDIR;
00221       return res_RENAME4.status;
00222     }
00223 
00224   /* Convert saved FH into a vnode */
00225   src_entry = data->saved_entry;
00226 
00227   /* Source must be a directory */
00228   if(data->saved_filetype != DIRECTORY)
00229     {
00230       res_RENAME4.status = NFS4ERR_NOTDIR;
00231       return res_RENAME4.status;
00232     }
00233 
00234   /* Renaming a file to himself is allowed, returns NFS4_OK */
00235   if(src_entry == dst_entry)
00236     {
00237       if(!FSAL_namecmp(&oldname, &newname))
00238         {
00239           res_RENAME4.status = NFS4_OK;
00240           return res_RENAME4.status;
00241         }
00242     }
00243 
00244   /* For the change_info4, get the 'change' attributes for both directories */
00245   if((cache_status = cache_inode_getattr(src_entry,
00246                                          &attr_src,
00247                                          data->pcontext,
00248                                          &cache_status)) != CACHE_INODE_SUCCESS)
00249     {
00250       res_RENAME4.status = nfs4_Errno(cache_status);
00251       return res_RENAME4.status;
00252     }
00253 #ifdef BUGAZOMEU
00254   /* Ne devrait pas se produire dans le cas de exportid differents */
00255 
00256   /* Both object must resides on the same filesystem, return NFS4ERR_XDEV if not */
00257   if(attr_src.va_rdev != attr_dst.va_rdev)
00258     {
00259       res_RENAME4.status = NFS4ERR_XDEV;
00260       return res_RENAME4.status;
00261     }
00262 #endif
00263   /* Lookup oldfile to see if it exists (refcount +1) */
00264   if((tst_entry_src = cache_inode_lookup(src_entry,
00265                                          &oldname,
00266                                          &attr_tst_src,
00267                                          data->pcontext,
00268                                          &cache_status)) == NULL)
00269     {
00270       res_RENAME4.status = nfs4_Errno(cache_status);
00271       goto release;
00272     }
00273 
00274   /* Lookup file with new name to see if it already exists (refcount +1),
00275    * I expect to get NO_ERROR or ENOENT, anything else means an error */
00276   tst_entry_dst = cache_inode_lookup(dst_entry,
00277                                      &newname,
00278                                      &attr_tst_dst,
00279                                      data->pcontext,
00280                                      &cache_status);
00281   if((cache_status != CACHE_INODE_SUCCESS) &&
00282      (cache_status != CACHE_INODE_NOT_FOUND))
00283     {
00284         /* Unexpected status at this step, exit with an error */
00285         res_RENAME4.status = nfs4_Errno(cache_status);
00286         goto release;
00287     }
00288 
00289   if(cache_status == CACHE_INODE_NOT_FOUND)
00290     tst_entry_dst = NULL;       /* Just to make sure */
00291 
00292   /* Renaming a file to one of its own hardlink is allowed, return NFS4_OK */
00293   if(tst_entry_src == tst_entry_dst) {
00294       res_RENAME4.status = NFS4_OK;
00295       goto release;
00296     }
00297 
00298   /* Renaming dir into existing file should return NFS4ERR_EXIST */
00299   if ((tst_entry_src->type == DIRECTORY) &&
00300       ((tst_entry_dst != NULL) &&
00301        (tst_entry_dst->type == REGULAR_FILE)))
00302     {
00303       res_RENAME4.status = NFS4ERR_EXIST;
00304       goto release;
00305     }
00306 
00307   /* Renaming file into existing dir should return NFS4ERR_EXIST */
00308   if(tst_entry_src->type == REGULAR_FILE)
00309     {
00310       if(tst_entry_dst != NULL)
00311         {
00312           if(tst_entry_dst->type == DIRECTORY)
00313             {
00314               res_RENAME4.status = NFS4ERR_EXIST;
00315               goto release;
00316             }
00317         }
00318     }
00319 
00320   /* Renaming dir1 into existing, nonempty dir2 should return NFS4ERR_EXIST
00321    * Renaming file into existing, nonempty dir should return NFS4ERR_EXIST */
00322   if(tst_entry_dst != NULL)
00323     {
00324       if((tst_entry_dst->type == DIRECTORY)
00325          && ((tst_entry_src->type == DIRECTORY)
00326              || (tst_entry_src->type == REGULAR_FILE)))
00327         {
00328           if(cache_inode_is_dir_empty_WithLock(tst_entry_dst) ==
00329              CACHE_INODE_DIR_NOT_EMPTY)
00330             {
00331               res_RENAME4.status = NFS4ERR_EXIST;
00332               goto release;
00333             }
00334         }
00335     }
00336 
00337   res_RENAME4.RENAME4res_u.resok4.source_cinfo.before
00338        = cache_inode_get_changeid4(src_entry);
00339   res_RENAME4.RENAME4res_u.resok4.target_cinfo.before
00340        = cache_inode_get_changeid4(dst_entry);
00341 
00342   if(cache_status == CACHE_INODE_SUCCESS)
00343     {
00344       /* New entry already exists, its attributes are in attr_tst_*,
00345          check for old entry to see if types are compatible */
00346       handlenew = &tst_entry_dst->handle;
00347       handleold = &tst_entry_src->handle;
00348 
00349       if(!FSAL_handlecmp(handlenew, handleold, &fsal_status))
00350         {
00351           /* For the change_info4, get the 'change' attributes for both directories */
00352           res_RENAME4.RENAME4res_u.resok4.source_cinfo.before
00353             = cache_inode_get_changeid4(src_entry);
00354           res_RENAME4.RENAME4res_u.resok4.target_cinfo.before
00355             = cache_inode_get_changeid4(dst_entry);
00356           res_RENAME4.RENAME4res_u.resok4.target_cinfo.atomic = FALSE;
00357           res_RENAME4.RENAME4res_u.resok4.source_cinfo.atomic = FALSE;
00358 
00359           res_RENAME4.status = NFS4_OK;
00360           goto release;
00361         }
00362       else
00363         {
00364           /* Destination exists and is something different from source */
00365           if(( tst_entry_src->type == REGULAR_FILE &&
00366               tst_entry_dst->type == REGULAR_FILE ) ||
00367               ( tst_entry_src->type == DIRECTORY &&
00368               tst_entry_dst->type == DIRECTORY ))
00369             {
00370               if(cache_inode_rename(src_entry,
00371                                     &oldname,
00372                                     dst_entry,
00373                                     &newname,
00374                                     &attr_src,
00375                                     &attr_dst,
00376                                     data->pcontext, &cache_status) != CACHE_INODE_SUCCESS)
00377                {
00378 
00379                  res_RENAME4.status = nfs4_Errno(cache_status);
00380                  goto release;
00381                }
00382             }
00383           else
00384             { 
00385               res_RENAME4.status = NFS4ERR_EXIST;
00386               goto release;
00387             }
00388         }
00389     }
00390   else
00391     {
00392       /* New entry does not already exist, call cache_entry_rename */
00393       if(cache_inode_rename(src_entry,
00394                             &oldname,
00395                             dst_entry,
00396                             &newname,
00397                             &attr_src,
00398                             &attr_dst,
00399                             data->pcontext, &cache_status) != CACHE_INODE_SUCCESS)
00400         {
00401           res_RENAME4.status = nfs4_Errno(cache_status);
00402           goto release;
00403         }
00404     }
00405 
00406   /* If you reach this point, then everything was alright */
00407   /* For the change_info4, get the 'change' attributes for both directories */
00408 
00409   res_RENAME4.RENAME4res_u.resok4.source_cinfo.after =
00410        cache_inode_get_changeid4(src_entry);
00411   res_RENAME4.RENAME4res_u.resok4.target_cinfo.after =
00412        cache_inode_get_changeid4(dst_entry);
00413   res_RENAME4.RENAME4res_u.resok4.target_cinfo.atomic = FALSE;
00414   res_RENAME4.RENAME4res_u.resok4.source_cinfo.atomic = FALSE;
00415   res_RENAME4.status = nfs4_Errno(cache_status);
00416 
00417 release:
00418   if (tst_entry_src)
00419       (void) cache_inode_put(tst_entry_src);
00420   if (tst_entry_dst)
00421       (void) cache_inode_put(tst_entry_dst);
00422 
00423   return (res_RENAME4.status);
00424 }                               /* nfs4_op_rename */
00425 
00436 void nfs4_op_rename_Free(RENAME4res * resp)
00437 {
00438   /* Nothing to be done */
00439   return;
00440 }                               /* nfs4_op_rename_Free */