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 <assert.h> 00048 #include <pthread.h> 00049 #include <fcntl.h> 00050 #include <sys/file.h> /* for having FNDELAY */ 00051 #include "HashData.h" 00052 #include "HashTable.h" 00053 #include "log.h" 00054 #include "ganesha_rpc.h" 00055 #include "nfs23.h" 00056 #include "nfs4.h" 00057 #include "mount.h" 00058 #include "nfs_core.h" 00059 #include "cache_inode.h" 00060 #include "nfs_exports.h" 00061 #include "nfs_creds.h" 00062 #include "nfs_proto_functions.h" 00063 #include "nfs_tools.h" 00064 #include "nfs_proto_tools.h" 00065 00085 int nfs_Write(nfs_arg_t *parg, 00086 exportlist_t *pexport, 00087 fsal_op_context_t *pcontext, 00088 nfs_worker_data_t *pworker, 00089 struct svc_req *preq, 00090 nfs_res_t *pres) 00091 { 00092 cache_entry_t *pentry; 00093 fsal_attrib_list_t attr; 00094 fsal_attrib_list_t pre_attr; 00095 fsal_attrib_list_t *ppre_attr; 00096 cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; 00097 size_t size = 0; 00098 size_t written_size; 00099 fsal_off_t offset = 0; 00100 caddr_t data = NULL; 00101 cache_inode_file_type_t filetype; 00102 fsal_boolean_t eof_met; 00103 cache_inode_stability_t stability = CACHE_INODE_SAFE_WRITE_TO_FS; 00104 int rc = NFS_REQ_OK; 00105 #ifdef _USE_QUOTA 00106 fsal_status_t fsal_status ; 00107 #endif 00108 00109 if(isDebug(COMPONENT_NFSPROTO)) 00110 { 00111 char str[LEN_FH_STR], *stables = ""; 00112 00113 switch (preq->rq_vers) 00114 { 00115 case NFS_V2: 00116 offset = parg->arg_write2.offset; 00117 size = parg->arg_write2.data.nfsdata2_len; 00118 stables = "FILE_SYNC"; 00119 break; 00120 case NFS_V3: 00121 offset = parg->arg_write3.offset; 00122 size = parg->arg_write3.count; 00123 switch (parg->arg_write3.stable) 00124 { 00125 case UNSTABLE: stables = "UNSTABLE"; break; 00126 case DATA_SYNC: stables = "DATA_SYNC"; break; 00127 case FILE_SYNC: stables = "FILE_SYNC"; break; 00128 } 00129 } 00130 00131 nfs_FhandleToStr(preq->rq_vers, 00132 &(parg->arg_write2.file), 00133 &(parg->arg_write3.file), 00134 NULL, 00135 str); 00136 LogDebug(COMPONENT_NFSPROTO, 00137 "REQUEST PROCESSING: Calling nfs_Write handle: %s start: %llx len: %llx %s", 00138 str, 00139 (unsigned long long) offset, 00140 (unsigned long long) size, 00141 stables); 00142 } 00143 00144 if(preq->rq_vers == NFS_V3) 00145 { 00146 /* to avoid setting it on each error case */ 00147 pres->res_write3.WRITE3res_u.resfail.file_wcc.before.attributes_follow = FALSE; 00148 pres->res_write3.WRITE3res_u.resfail.file_wcc.after.attributes_follow = FALSE; 00149 ppre_attr = NULL; 00150 } 00151 00152 /* Convert file handle into a cache entry */ 00153 if((pentry = nfs_FhandleToCache(preq->rq_vers, 00154 &(parg->arg_write2.file), 00155 &(parg->arg_write3.file), 00156 NULL, 00157 &(pres->res_attr2.status), 00158 &(pres->res_write3.status), 00159 NULL, &pre_attr, pcontext, &rc)) == NULL) 00160 { 00161 /* Stale NFS FH ? */ 00162 goto out; 00163 } 00164 00165 if((preq->rq_vers == NFS_V3) && (nfs3_Is_Fh_Xattr(&(parg->arg_write3.file)))) 00166 { 00167 rc = nfs3_Write_Xattr(parg, pexport, pcontext, preq, pres); 00168 goto out; 00169 } 00170 00171 if(cache_inode_access(pentry, 00172 FSAL_WRITE_ACCESS, 00173 pcontext, 00174 &cache_status) != CACHE_INODE_SUCCESS) 00175 { 00176 /* NFSv3 exception : if user wants to write to a file that is readonly 00177 * but belongs to him, then allow it to do it, push the permission check 00178 * to the client side */ 00179 if( ( cache_status == CACHE_INODE_FSAL_EACCESS ) && 00180 ( pentry->attributes.owner == FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ) 00181 { 00182 LogDebug( COMPONENT_NFSPROTO, 00183 "Exception management: allowed user %u to write to read-only file belonging to him", 00184 pentry->attributes.owner ) ; 00185 } 00186 else 00187 { 00188 switch (preq->rq_vers) 00189 { 00190 case NFS_V2: 00191 pres->res_attr2.status = nfs2_Errno(cache_status); 00192 break; 00193 00194 case NFS_V3: 00195 pres->res_write3.status = nfs3_Errno(cache_status); 00196 break; 00197 } 00198 rc = NFS_REQ_OK; 00199 goto out; 00200 } 00201 00202 rc = NFS_REQ_OK; 00203 goto out; 00204 } 00205 00206 /* get directory attributes before action (for V3 reply) */ 00207 ppre_attr = &pre_attr; 00208 00209 /* Extract the filetype */ 00210 filetype = cache_inode_fsal_type_convert(pre_attr.type); 00211 00212 /* Sanity check: write only a regular file */ 00213 if(filetype != REGULAR_FILE) 00214 { 00215 switch (preq->rq_vers) 00216 { 00217 case NFS_V2: 00218 /* 00219 * In the RFC tell it not good but it does 00220 * not tell what to do ... 00221 * We use NFSERR_ISDIR for lack of better 00222 */ 00223 pres->res_attr2.status = NFSERR_ISDIR; 00224 break; 00225 00226 case NFS_V3: 00227 if(filetype == DIRECTORY) 00228 pres->res_write3.status = NFS3ERR_ISDIR; 00229 else 00230 pres->res_write3.status = NFS3ERR_INVAL; 00231 break; 00232 } 00233 rc = NFS_REQ_OK; 00234 goto out; 00235 } 00236 00237 /* For MDONLY export, reject write operation */ 00238 /* Request of type MDONLY_RO were rejected at the nfs_rpc_dispatcher level */ 00239 /* This is done by replying EDQUOT (this error is known for not disturbing the client's requests cache */ 00240 if( pexport->access_type != ACCESSTYPE_RW ) 00241 { 00242 switch (preq->rq_vers) 00243 { 00244 case NFS_V2: 00245 switch( pexport->access_type ) 00246 { 00247 case ACCESSTYPE_MDONLY: 00248 case ACCESSTYPE_MDONLY_RO: 00249 pres->res_attr2.status = NFSERR_DQUOT; 00250 break; 00251 00252 case ACCESSTYPE_RO: 00253 pres->res_attr2.status = NFSERR_ROFS; 00254 break ; 00255 00256 default: 00257 assert(0); // if we get here than the value is an invalid enum - corruption 00258 break ; 00259 } 00260 break ; 00261 00262 case NFS_V3: 00263 switch( pexport->access_type ) 00264 { 00265 case ACCESSTYPE_MDONLY: 00266 case ACCESSTYPE_MDONLY_RO: 00267 pres->res_write3.status = NFS3ERR_DQUOT; 00268 break; 00269 00270 case ACCESSTYPE_RO: 00271 pres->res_write3.status = NFS3ERR_ROFS; 00272 break ; 00273 00274 default: 00275 assert(0); // if we get here than the value is an invalid enum - corruption 00276 break ; 00277 } 00278 break; 00279 } /* switch (preq->rq_vers) */ 00280 00281 nfs_SetFailedStatus(pcontext, pexport, 00282 preq->rq_vers, 00283 cache_status, 00284 &pres->res_attr2.status, 00285 &pres->res_write3.status, 00286 NULL, NULL, 00287 pentry, 00288 ppre_attr, 00289 &(pres->res_write3.WRITE3res_u.resfail.file_wcc), 00290 NULL, NULL, NULL); 00291 00292 rc = NFS_REQ_OK; 00293 goto out; 00294 } 00295 00296 #ifdef _USE_QUOTA 00297 /* if quota support is active, then we should check is the FSAL allows inode creation or not */ 00298 fsal_status = FSAL_check_quota( pexport->fullpath, 00299 FSAL_QUOTA_BLOCKS, 00300 FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ; 00301 if( FSAL_IS_ERROR( fsal_status ) ) 00302 { 00303 00304 switch (preq->rq_vers) 00305 { 00306 case NFS_V2: 00307 pres->res_attr2.status = NFSERR_DQUOT; 00308 break; 00309 00310 case NFS_V3: 00311 pres->res_write3.status = NFS3ERR_DQUOT; 00312 break; 00313 } 00314 00315 rc = NFS_REQ_OK ; 00316 goto out; 00317 } 00318 #endif /* _USE_QUOTA */ 00319 00320 00321 /* Extract the argument from the request */ 00322 switch (preq->rq_vers) 00323 { 00324 case NFS_V2: 00325 if(ppre_attr && ppre_attr->filesize > NFS2_MAX_FILESIZE) 00326 { 00327 /* 00328 * V2 clients don't understand filesizes > 00329 * 2GB, so we don't allow them to alter 00330 * them in any way. BJP 6/26/2001 00331 */ 00332 pres->res_attr2.status = NFSERR_FBIG; 00333 rc = NFS_REQ_OK; 00334 goto out; 00335 } 00336 00337 offset = parg->arg_write2.offset; /* beginoffset is obsolete */ 00338 size = parg->arg_write2.data.nfsdata2_len; /* totalcount is obsolete */ 00339 data = parg->arg_write2.data.nfsdata2_val; 00340 if (pexport->use_commit == TRUE) 00341 stability = CACHE_INODE_SAFE_WRITE_TO_FS; 00342 break; 00343 00344 case NFS_V3: 00345 offset = parg->arg_write3.offset; 00346 size = parg->arg_write3.count; 00347 00348 if(size > parg->arg_write3.data.data_len) 00349 { 00350 /* should never happen */ 00351 pres->res_write3.status = NFS3ERR_INVAL; 00352 rc = NFS_REQ_OK; 00353 goto out; 00354 } 00355 00356 if((pexport->use_commit == TRUE) && 00357 (pexport->use_ganesha_write_buffer == FALSE) && 00358 (parg->arg_write3.stable == UNSTABLE)) 00359 { 00360 stability = CACHE_INODE_UNSAFE_WRITE_TO_FS_BUFFER; 00361 } 00362 else if((pexport->use_commit == TRUE) && 00363 (pexport->use_ganesha_write_buffer == TRUE) && 00364 (parg->arg_write3.stable == UNSTABLE)) 00365 { 00366 stability = CACHE_INODE_UNSAFE_WRITE_TO_GANESHA_BUFFER; 00367 } 00368 else 00369 { 00370 stability = CACHE_INODE_SAFE_WRITE_TO_FS; 00371 } 00372 data = parg->arg_write3.data.data_val; 00373 break; 00374 } 00375 00376 /* 00377 * do not exceed maxium WRITE offset if set 00378 */ 00379 if((pexport->options & EXPORT_OPTION_MAXOFFSETWRITE) == EXPORT_OPTION_MAXOFFSETWRITE) 00380 { 00381 LogFullDebug(COMPONENT_NFSPROTO, 00382 "-----> Write offset=%llu count=%llu MaxOffSet=%llu", 00383 (unsigned long long) offset, 00384 (unsigned long long) size, 00385 (unsigned long long) pexport->MaxOffsetWrite); 00386 00387 if((fsal_off_t) (offset + size) > pexport->MaxOffsetWrite) 00388 { 00389 LogEvent(COMPONENT_NFSPROTO, 00390 "NFS WRITE: A client tryed to violate max file size %llu for exportid #%hu", 00391 (unsigned long long) pexport->MaxOffsetWrite, pexport->id); 00392 00393 switch (preq->rq_vers) 00394 { 00395 case NFS_V2: 00396 pres->res_attr2.status = NFSERR_DQUOT; 00397 break; 00398 00399 case NFS_V3: 00400 pres->res_write3.status = NFS3ERR_INVAL; 00401 break; 00402 } 00403 00404 nfs_SetFailedStatus(pcontext, pexport, 00405 preq->rq_vers, 00406 cache_status, 00407 &pres->res_attr2.status, 00408 &pres->res_write3.status, 00409 NULL, NULL, 00410 pentry, 00411 ppre_attr, 00412 &(pres->res_write3.WRITE3res_u.resfail.file_wcc), 00413 NULL, NULL, NULL); 00414 00415 rc = NFS_REQ_OK; 00416 goto out; 00417 } 00418 } 00419 00420 /* 00421 * We should take care not to exceed FSINFO wtmax 00422 * field for the size 00423 */ 00424 if(((pexport->options & EXPORT_OPTION_MAXWRITE) == EXPORT_OPTION_MAXWRITE) && 00425 size > pexport->MaxWrite) 00426 { 00427 /* 00428 * The client asked for too much data, we 00429 * must restrict him 00430 */ 00431 size = pexport->MaxWrite; 00432 } 00433 00434 if(size == 0) 00435 { 00436 cache_status = CACHE_INODE_SUCCESS; 00437 written_size = 0; 00438 } 00439 else 00440 { 00441 /* An actual write is to be made, prepare it */ 00442 if((cache_inode_rdwr(pentry, 00443 CACHE_INODE_WRITE, 00444 offset, 00445 size, 00446 &written_size, 00447 data, 00448 &eof_met, 00449 pcontext, 00450 stability, 00451 &cache_status) == CACHE_INODE_SUCCESS) && 00452 (cache_inode_getattr(pentry, &attr, pcontext, 00453 &cache_status) == CACHE_INODE_SUCCESS)) { 00454 00455 00456 switch (preq->rq_vers) 00457 { 00458 case NFS_V2: 00459 nfs2_FSALattr_To_Fattr(pexport, 00460 &attr, &(pres->res_attr2.ATTR2res_u.attributes)); 00461 00462 pres->res_attr2.status = NFS_OK; 00463 break; 00464 00465 case NFS_V3: 00466 00467 /* Build Weak Cache Coherency data */ 00468 nfs_SetWccData(pexport, ppre_attr, 00469 &attr, &(pres->res_write3.WRITE3res_u.resok.file_wcc)); 00470 00471 /* Set the written size */ 00472 pres->res_write3.WRITE3res_u.resok.count = written_size; 00473 00474 /* How do we commit data ? */ 00475 if(stability == CACHE_INODE_SAFE_WRITE_TO_FS) 00476 { 00477 pres->res_write3.WRITE3res_u.resok.committed = FILE_SYNC; 00478 } 00479 else 00480 { 00481 pres->res_write3.WRITE3res_u.resok.committed = UNSTABLE; 00482 } 00483 00484 /* Set the write verifier */ 00485 memcpy(pres->res_write3.WRITE3res_u.resok.verf, 00486 NFS3_write_verifier, sizeof(writeverf3)); 00487 00488 pres->res_write3.status = NFS3_OK; 00489 break; 00490 } 00491 00492 rc = NFS_REQ_OK; 00493 goto out; 00494 } 00495 } 00496 00497 LogFullDebug(COMPONENT_NFSPROTO, 00498 "---> failed write: cache_status=%d", cache_status); 00499 00500 /* If we are here, there was an error */ 00501 if(nfs_RetryableError(cache_status)) 00502 { 00503 rc = NFS_REQ_DROP; 00504 goto out; 00505 } 00506 00507 nfs_SetFailedStatus(pcontext, pexport, 00508 preq->rq_vers, 00509 cache_status, 00510 &pres->res_attr2.status, 00511 &pres->res_write3.status, 00512 NULL, NULL, 00513 pentry, 00514 ppre_attr, 00515 &(pres->res_write3.WRITE3res_u.resfail.file_wcc), NULL, NULL, NULL); 00516 00517 rc = NFS_REQ_OK; 00518 00519 out: 00520 /* return references */ 00521 if (pentry) 00522 cache_inode_put(pentry); 00523 00524 return (rc); 00525 00526 } /* nfs_Write.c */ 00527 00536 void nfs_Write_Free(nfs_res_t * resp) 00537 { 00538 return; 00539 } /* nfs_Write_Free */