nfs-ganesha 1.4

nfs4_op_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 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif
00039 
00040 #ifdef _SOLARIS
00041 #include "solaris_port.h"
00042 #endif
00043 
00044 #include <stdio.h>
00045 #include <string.h>
00046 #include <pthread.h>
00047 #include "HashData.h"
00048 #include "HashTable.h"
00049 #include "log.h"
00050 #include "ganesha_rpc.h"
00051 #include "nfs4.h"
00052 #include "nfs_core.h"
00053 #include "sal_functions.h"
00054 #include "nfs_proto_functions.h"
00055 #include "nfs_proto_tools.h"
00056 #ifdef _PNFS_DS
00057 #include "fsal_pnfs.h"
00058 #endif /* _PNFS_DS */
00059 
00060 #ifdef _PNFS_DS
00061 static int op_dswrite(struct nfs_argop4 *op,
00062                       compound_data_t * data,
00063                       struct nfs_resop4 *resp);
00064 #endif /* _PNFS_DS */
00065 
00079 #define arg_WRITE4 op->nfs_argop4_u.opwrite
00080 #define res_WRITE4 resp->nfs_resop4_u.opwrite
00081 
00082 int nfs4_op_write(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
00083 {
00084   fsal_size_t              size;
00085   fsal_size_t              written_size;
00086   fsal_off_t               offset;
00087   fsal_boolean_t           eof_met;
00088   cache_inode_stability_t  stability = CACHE_INODE_SAFE_WRITE_TO_FS;
00089   caddr_t                  bufferdata;
00090   stable_how4              stable_how;
00091   state_t                * pstate_found = NULL;
00092   state_t                * pstate_open;
00093   cache_inode_status_t     cache_status;
00094   cache_entry_t          * pentry = NULL;
00095 #ifdef _USE_QUOTA
00096   fsal_status_t            fsal_status ;
00097 #endif
00098   /* This flag is set to true in the case of an anonymous read so that
00099      we know to release the state lock afterward.  The state lock does
00100      not need to be held during a non-anonymous read, since the open
00101      state itself prevents a conflict. */
00102   bool_t                   anonymous = FALSE;
00103 
00104   /* Lock are not supported */
00105   resp->resop = NFS4_OP_WRITE;
00106   res_WRITE4.status = NFS4_OK;
00107 
00108   /*
00109    * Do basic checks on a filehandle
00110    * Only files can be written
00111    */
00112   res_WRITE4.status = nfs4_sanity_check_FH(data, REGULAR_FILE);
00113   if(res_WRITE4.status != NFS4_OK)
00114     return res_WRITE4.status;
00115 
00116 #ifdef _USE_QUOTA
00117     /* if quota support is active, then we should check is the FSAL allows inode creation or not */
00118     fsal_status = FSAL_check_quota( data->pexport->fullpath, 
00119                                     FSAL_QUOTA_BLOCKS,
00120                                     FSAL_OP_CONTEXT_TO_UID( data->pcontext ) ) ;
00121     if( FSAL_IS_ERROR( fsal_status ) )
00122      {
00123       res_WRITE4.status = NFS4ERR_DQUOT ;
00124       return res_WRITE4.status;
00125      }
00126 #endif /* _USE_QUOTA */
00127 
00128   /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */
00129   if(nfs4_Is_Fh_Xattr(&(data->currentFH)))
00130     return nfs4_op_write_xattr(op, data, resp);
00131 
00132 #ifdef _PNFS_DS
00133   if((data->minorversion == 1) &&
00134      (nfs4_Is_Fh_DSHandle(&data->currentFH)))
00135     {
00136       return(op_dswrite(op, data, resp));
00137     }
00138 #endif /* _PNFS_DS */
00139 
00140   /* Manage access type */
00141   switch( data->pexport->access_type )
00142    {
00143      case ACCESSTYPE_MDONLY:
00144      case ACCESSTYPE_MDONLY_RO:
00145         res_WRITE4.status = NFS4ERR_DQUOT;
00146         return res_WRITE4.status;
00147         break ;
00148 
00149      case ACCESSTYPE_RO:
00150         res_WRITE4.status = NFS4ERR_ROFS ;
00151         return res_WRITE4.status;
00152         break ;
00153 
00154      default:
00155         break ;
00156    } /* switch( data->pexport->access_type ) */
00157 
00158   /* vnode to manage is the current one */
00159   pentry = data->current_entry;
00160 
00161   /* Check stateid correctness and get pointer to state
00162    * (also checks for special stateids)
00163    */
00164   res_WRITE4.status = nfs4_Check_Stateid(&arg_WRITE4.stateid,
00165                                          pentry,
00166                                          &pstate_found,
00167                                          data,
00168                                          STATEID_SPECIAL_ANY,
00169                                          "WRITE");
00170   if(res_WRITE4.status != NFS4_OK)
00171     return res_WRITE4.status;
00172 
00173   /* NB: After this points, if pstate_found == NULL, then the stateid is all-0 or all-1 */
00174 
00175   if(pstate_found != NULL)
00176     {
00177       switch(pstate_found->state_type)
00178         {
00179           case STATE_TYPE_SHARE:
00180             pstate_open = pstate_found;
00181             // TODO FSF: need to check against existing locks
00182             break;
00183 
00184           case STATE_TYPE_LOCK:
00185             pstate_open = pstate_found->state_data.lock.popenstate;
00186             // TODO FSF: should check that write is in range of an exclusive lock...
00187             break;
00188 
00189           case STATE_TYPE_DELEG:
00190 #ifdef _USE_NFS4_1
00191           case STATE_TYPE_LAYOUT:
00192 #endif /* _USE_NFS4_1 */
00193             pstate_open = NULL;
00194             // TODO FSF: should check that this is a write delegation?
00195             break;
00196 
00197           default:
00198             res_WRITE4.status = NFS4ERR_BAD_STATEID;
00199             LogDebug(COMPONENT_NFS_V4_LOCK,
00200                      "WRITE with invalid stateid of type %d",
00201                      (int) pstate_found->state_type);
00202             return res_WRITE4.status;
00203         }
00204 
00205       /* This is a write operation, this means that the file MUST have been opened for writing */
00206       if(pstate_open != NULL &&
00207          (pstate_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0)
00208         {
00209           /* Bad open mode, return NFS4ERR_OPENMODE */
00210           res_WRITE4.status = NFS4ERR_OPENMODE;
00211           LogDebug(COMPONENT_NFS_V4_LOCK,
00212                    "WRITE state %p doesn't have OPEN4_SHARE_ACCESS_WRITE",
00213                    pstate_found);
00214           return res_WRITE4.status;
00215         }
00216     }
00217   else
00218     {
00219       /* Special stateid, no open state, check to see if any share conflicts */
00220       pstate_open = NULL;
00221 
00222       pthread_rwlock_rdlock(&pentry->state_lock);
00223       anonymous = TRUE;
00224 
00225       /*
00226        * Special stateid, no open state, check to see if any share conflicts
00227        * The stateid is all-0 or all-1
00228        */
00229       res_WRITE4.status = nfs4_check_special_stateid(pentry,"WRITE",
00230                                                      FATTR4_ATTR_WRITE);
00231       if(res_WRITE4.status != NFS4_OK)
00232         {
00233           pthread_rwlock_unlock(&pentry->state_lock); 
00234           return res_WRITE4.status;
00235         }
00236     }
00237 
00238   if (pstate_open == NULL)
00239     {
00240       if(cache_inode_access(pentry,
00241                             FSAL_WRITE_ACCESS,
00242                             data->pcontext,
00243                             &cache_status) != CACHE_INODE_SUCCESS)
00244         {
00245           res_WRITE4.status = nfs4_Errno(cache_status);;
00246           pthread_rwlock_unlock(&pentry->state_lock);
00247           return res_WRITE4.status;
00248         }
00249     }
00250 
00251   /* Get the characteristics of the I/O to be made */
00252   offset = arg_WRITE4.offset;
00253   size = arg_WRITE4.data.data_len;
00254   stable_how = arg_WRITE4.stable;
00255   LogFullDebug(COMPONENT_NFS_V4,
00256                "NFS4_OP_WRITE: offset = %"PRIu64"  length = %zu  stable = %d",
00257                offset, size, stable_how);
00258 
00259   if((data->pexport->options & EXPORT_OPTION_MAXOFFSETWRITE) ==
00260      EXPORT_OPTION_MAXOFFSETWRITE)
00261     if((fsal_off_t) (offset + size) > data->pexport->MaxOffsetWrite)
00262       {
00263         res_WRITE4.status = NFS4ERR_DQUOT;
00264         if (anonymous)
00265           {
00266             pthread_rwlock_unlock(&pentry->state_lock);
00267           }
00268         return res_WRITE4.status;
00269       }
00270 
00271   /* The size to be written should not be greater than FATTR4_MAXWRITESIZE because this value is asked
00272    * by the client at mount time, but we check this by security */
00273 
00274   if( size > data->pexport->MaxWrite )
00275     {
00276       /*
00277        * The client asked for too much data, we
00278        * must restrict him
00279        */
00280 
00281       LogFullDebug(COMPONENT_NFS_V4,
00282                "NFS4_OP_WRITE: write requested size = %"PRIu64" write allowed size = %"PRIu64,
00283                size, data->pexport->MaxWrite);
00284 
00285       size = data->pexport->MaxWrite;
00286     }
00287 
00288   /* Where are the data ? */
00289   bufferdata = arg_WRITE4.data.data_val;
00290 
00291   LogFullDebug(COMPONENT_NFS_V4,
00292                "NFS4_OP_WRITE: offset = %"PRIu64" length = %zu",
00293                offset, size);
00294 
00295   /* if size == 0 , no I/O) are actually made and everything is alright */
00296   if(size == 0)
00297     {
00298       res_WRITE4.WRITE4res_u.resok4.count = 0;
00299       res_WRITE4.WRITE4res_u.resok4.committed = FILE_SYNC4;
00300 
00301       memcpy(res_WRITE4.WRITE4res_u.resok4.writeverf, NFS4_write_verifier,
00302              sizeof(verifier4));
00303 
00304       res_WRITE4.status = NFS4_OK;
00305       if (anonymous)
00306         {
00307           pthread_rwlock_unlock(&pentry->state_lock);
00308         }
00309       return res_WRITE4.status;
00310     }
00311 
00312   if(arg_WRITE4.stable == UNSTABLE4)
00313     {
00314       stability = CACHE_INODE_UNSAFE_WRITE_TO_FS_BUFFER;
00315     }
00316   else
00317     {
00318       stability = CACHE_INODE_SAFE_WRITE_TO_FS;
00319     }
00320 
00321   if(cache_inode_rdwr(pentry,
00322                       CACHE_INODE_WRITE,
00323                       offset,
00324                       size,
00325                       &written_size,
00326                       bufferdata,
00327                       &eof_met,
00328                       data->pcontext,
00329                       stability,
00330                       &cache_status) != CACHE_INODE_SUCCESS)
00331     {
00332       LogDebug(COMPONENT_NFS_V4,
00333                "cache_inode_rdwr returned %s",
00334                cache_inode_err_str(cache_status));
00335       res_WRITE4.status = nfs4_Errno(cache_status);
00336       if (anonymous)
00337         {
00338           pthread_rwlock_unlock(&pentry->state_lock);
00339         }
00340       return res_WRITE4.status;
00341     }
00342 
00343   /* Set the returned value */
00344   if(stability == CACHE_INODE_SAFE_WRITE_TO_FS)
00345     res_WRITE4.WRITE4res_u.resok4.committed = FILE_SYNC4;
00346   else
00347     res_WRITE4.WRITE4res_u.resok4.committed = UNSTABLE4;
00348 
00349   res_WRITE4.WRITE4res_u.resok4.count = written_size;
00350   memcpy(res_WRITE4.WRITE4res_u.resok4.writeverf, NFS4_write_verifier, sizeof(verifier4));
00351 
00352   res_WRITE4.status = NFS4_OK;
00353 
00354   if (anonymous)
00355     {
00356       pthread_rwlock_unlock(&pentry->state_lock);
00357     }
00358 
00359   return res_WRITE4.status;
00360 }                               /* nfs4_op_write */
00361 
00372 void nfs4_op_write_Free(WRITE4res * resp)
00373 {
00374   /* Nothing to be done */
00375   return;
00376 }                               /* nfs4_op_write_Free */
00377 
00378 #ifdef _PNFS_DS
00379 
00391 static int op_dswrite(struct nfs_argop4 *op,
00392                       compound_data_t * data,
00393                       struct nfs_resop4 *resp)
00394 {
00395   /* FSAL file handle */
00396   fsal_handle_t handle;
00397   /* NFSv4 return code */
00398   nfsstat4 nfs_status = 0;
00399   struct fsal_handle_desc fh_desc;
00400 
00401   /* Construct the FSAL file handle */
00402 
00403   if ((nfs4_FhandleToFSAL(&data->currentFH,
00404                           &fh_desc,
00405                           data->pcontext)) == 0)
00406     {
00407       res_WRITE4.status = NFS4ERR_INVAL;
00408       return res_WRITE4.status;
00409     }
00410 
00411   memset((caddr_t) &handle, 0, sizeof(handle));
00412   memcpy((caddr_t) &handle, fh_desc.start, fh_desc.len);
00413   nfs_status
00414     = fsal_dsfunctions.DS_write(&handle,
00415                                 data->pcontext,
00416                                 &arg_WRITE4.stateid,
00417                                 arg_WRITE4.offset,
00418                                 arg_WRITE4.data.data_len,
00419                                 arg_WRITE4.data.data_val,
00420                                 arg_WRITE4.stable,
00421                                 &res_WRITE4.WRITE4res_u.resok4.count,
00422                                 &res_WRITE4.WRITE4res_u.resok4.writeverf,
00423                                 &res_WRITE4.WRITE4res_u.resok4.committed);
00424 
00425   res_WRITE4.status = nfs_status;
00426   return res_WRITE4.status;
00427 }
00428 #endif /* _PNFS_DS */