nfs-ganesha 1.4

nfs_Write.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 <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 */