nfs-ganesha 1.4
|
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 */