nfs-ganesha 1.4

nfs_Create.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 "nfs23.h"
00055 #include "nfs4.h"
00056 #include "mount.h"
00057 #include "nfs_core.h"
00058 #include "cache_inode.h"
00059 #include "nfs_exports.h"
00060 #include "nfs_creds.h"
00061 #include "nfs_proto_functions.h"
00062 #include "nfs_tools.h"
00063 #include "nfs_file_handle.h"
00064 #include "nfs_proto_tools.h"
00065 
00085 int nfs_Create(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   char *str_file_name = NULL;
00093   fsal_name_t file_name;
00094   fsal_accessmode_t mode = 0;
00095   cache_entry_t *file_pentry = NULL;
00096   cache_entry_t *parent_pentry = NULL;
00097   fsal_attrib_list_t parent_attr;
00098   fsal_attrib_list_t attr;
00099   fsal_attrib_list_t attr_parent_after;
00100   fsal_attrib_list_t attr_newfile;
00101   fsal_attrib_list_t attributes_create;
00102   fsal_attrib_list_t *ppre_attr;
00103   cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
00104   cache_inode_status_t cache_status_lookup;
00105   cache_inode_file_type_t parent_filetype;
00106   int rc = NFS_REQ_OK;
00107 #ifdef _USE_QUOTA
00108   fsal_status_t fsal_status ;
00109 #endif
00110 
00111   if(isDebug(COMPONENT_NFSPROTO))
00112     {
00113       char str[LEN_FH_STR];
00114 
00115       switch (preq->rq_vers)
00116         {
00117         case NFS_V2:
00118           str_file_name = parg->arg_create2.where.name;
00119           break;
00120         case NFS_V3:
00121           str_file_name = parg->arg_create3.where.name;
00122           break;
00123         }
00124 
00125       nfs_FhandleToStr(preq->rq_vers,
00126                        &(parg->arg_create2.where.dir),
00127                        &(parg->arg_create3.where.dir),
00128                        NULL,
00129                        str);
00130       LogDebug(COMPONENT_NFSPROTO,
00131                "REQUEST PROCESSING: Calling nfs_Create handle: %s name: %s",
00132                str, str_file_name);
00133     }
00134 
00135   if((preq->rq_vers == NFS_V3) && (nfs3_Is_Fh_Xattr(&(parg->arg_create3.where.dir))))
00136     {
00137       rc = nfs3_Create_Xattr(parg, pexport, pcontext, preq, pres);
00138       goto out;
00139     }
00140 
00141   if(preq->rq_vers == NFS_V3)
00142     {
00143       /* to avoid setting it on each error case */
00144       pres->res_create3.CREATE3res_u.resfail.dir_wcc.before.attributes_follow = FALSE;
00145       pres->res_create3.CREATE3res_u.resfail.dir_wcc.after.attributes_follow = FALSE;
00146       ppre_attr = NULL;
00147     }
00148 
00149   if((parent_pentry = nfs_FhandleToCache(preq->rq_vers,
00150                                          &(parg->arg_create2.where.dir),
00151                                          &(parg->arg_create3.where.dir),
00152                                          NULL,
00153                                          &(pres->res_dirop2.status),
00154                                          &(pres->res_create3.status),
00155                                          NULL,
00156                                          &parent_attr,
00157                                          pcontext, &rc)) == NULL)
00158     {
00159       /* Stale NFS FH ? */
00160       goto out;
00161     }
00162 
00163   /* get directory attributes before action (for V3 reply) */
00164   ppre_attr = &parent_attr;
00165 
00166   /* Extract the filetype */
00167   parent_filetype = cache_inode_fsal_type_convert(parent_attr.type);
00168 
00169   /*
00170    * Sanity checks: new file name must be non-null; parent must be a
00171    * directory. 
00172    */
00173   if(parent_filetype != DIRECTORY)
00174     {
00175       switch (preq->rq_vers)
00176         {
00177         case NFS_V2:
00178           pres->res_dirop2.status = NFSERR_NOTDIR;
00179           break;
00180 
00181         case NFS_V3:
00182           pres->res_create3.status = NFS3ERR_NOTDIR;
00183           break;
00184         }
00185 
00186       rc = NFS_REQ_OK;
00187       goto out;
00188     }
00189 
00190   switch (preq->rq_vers)
00191     {
00192     case NFS_V2:
00193       str_file_name = parg->arg_create2.where.name;
00194 
00195       if(parg->arg_create2.attributes.mode != (unsigned int)-1)
00196         {
00197           mode = unix2fsal_mode(parg->arg_create2.attributes.mode);
00198         }
00199       else
00200         {
00201           mode = 0;
00202         }
00203 
00204       break;
00205 
00206     case NFS_V3:
00207       str_file_name = parg->arg_create3.where.name;
00208       if(parg->arg_create3.how.mode == EXCLUSIVE)
00209         {
00210           /*
00211            * Client has not provided mode information.
00212            * If the create works, the client will issue
00213            * a separate setattr request to fix up the
00214            * file's mode, so pick arbitrary value for now.
00215            */
00216 
00217           mode = 0;
00218         }
00219       else if(parg->arg_create3.how.createhow3_u.obj_attributes.mode.set_it == TRUE)
00220         mode =
00221             unix2fsal_mode(parg->arg_create3.how.createhow3_u.obj_attributes.mode.
00222                            set_mode3_u.mode);
00223       else
00224         mode = 0;
00225       break;
00226     }
00227 
00228 #ifdef _USE_QUOTA
00229     /* if quota support is active, then we should check is the FSAL allows inode creation or not */
00230     fsal_status = FSAL_check_quota( pexport->fullpath, 
00231                                     FSAL_QUOTA_INODES,
00232                                     FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ;
00233     if( FSAL_IS_ERROR( fsal_status ) )
00234      {
00235 
00236        switch (preq->rq_vers)
00237          {
00238            case NFS_V2:
00239              pres->res_dirop2.status = NFSERR_DQUOT ;
00240              break;
00241 
00242            case NFS_V3:
00243              pres->res_create3.status = NFS3ERR_DQUOT;
00244              break;
00245          }
00246 
00247        rc = NFS_REQ_OK ;
00248        goto out;
00249      }
00250 #endif /* _USE_QUOTA */
00251 
00252   // if(str_file_name == NULL || strlen(str_file_name) == 0)
00253   if(str_file_name == NULL || *str_file_name == '\0' )
00254     {
00255       if(preq->rq_vers == NFS_V2)
00256         pres->res_dirop2.status = NFSERR_IO;
00257       if(preq->rq_vers == NFS_V3)
00258         pres->res_create3.status = NFS3ERR_INVAL;
00259     }
00260   else
00261     {
00262       if((cache_status = cache_inode_error_convert(FSAL_str2name(str_file_name,
00263                                                                  FSAL_MAX_NAME_LEN,
00264                                                                  &file_name))) ==
00265          CACHE_INODE_SUCCESS)
00266         {
00267           /*
00268            * Lookup file to see if it exists.  If so, use it.  Otherwise
00269            * create a new one.
00270            */
00271           file_pentry = cache_inode_lookup(parent_pentry,
00272                                            &file_name,
00273                                            &attr,
00274                                            pcontext,
00275                                            &cache_status_lookup);
00276 
00277           if((cache_status_lookup == CACHE_INODE_NOT_FOUND) ||
00278              ((cache_status_lookup == CACHE_INODE_SUCCESS)
00279               && (parg->arg_create3.how.mode == UNCHECKED)))
00280             {
00281               /* Create the file */
00282               if((parg->arg_create3.how.mode == UNCHECKED)
00283                  && (cache_status_lookup == CACHE_INODE_SUCCESS))
00284                 {
00285                   cache_status = CACHE_INODE_SUCCESS;
00286                   attr_newfile = attr;
00287                 }
00288               else
00289                 file_pentry = cache_inode_create(parent_pentry,
00290                                                  &file_name,
00291                                                  REGULAR_FILE,
00292                                                  mode,
00293                                                  NULL,
00294                                                  &attr_newfile,
00295                                                  pcontext, &cache_status);
00296 
00297               if(file_pentry != NULL)
00298                 {
00299                   /*
00300                    * Look at sattr to see if some attributes are to be set at creation time 
00301                    */
00302                   attributes_create.asked_attributes = 0ULL;
00303 
00304                   switch (preq->rq_vers)
00305                     {
00306                     case NFS_V2:
00307 
00308                       if(nfs2_Sattr_To_FSALattr(&attributes_create,
00309                                                 &parg->arg_create2.attributes) == 0)
00310                         {
00311                           pres->res_dirop2.status = NFSERR_IO;
00312                           rc = NFS_REQ_OK;
00313                           goto out;
00314                           break;
00315                         }
00316                       break;
00317 
00318                     case NFS_V3:
00319 
00320                       if(nfs3_Sattr_To_FSALattr(&attributes_create,
00321                                                 &parg->arg_create3.how.createhow3_u.
00322                                                 obj_attributes) == 0)
00323                         {
00324                           pres->res_create3.status = NFS3ERR_INVAL;
00325                           rc = NFS_REQ_OK;
00326                           goto out;
00327                         }
00328                       break;
00329                     }
00330 
00331                   /* Mode is managed above (in cache_inode_create), there is no need 
00332                    * to manage it */
00333                   if(attributes_create.asked_attributes & FSAL_ATTR_MODE)
00334                     attributes_create.asked_attributes &= ~FSAL_ATTR_MODE;
00335 
00336                   /* Some clients (like Solaris 10) try to set the size of the file to 0
00337                    * at creation time. The FSAL create empty file, so we ignore this */
00338                   if(attributes_create.asked_attributes & FSAL_ATTR_SIZE)
00339                     attributes_create.asked_attributes &= ~FSAL_ATTR_SIZE;
00340 
00341                   if(attributes_create.asked_attributes & FSAL_ATTR_SPACEUSED)
00342                     attributes_create.asked_attributes &= ~FSAL_ATTR_SPACEUSED;
00343 
00344                   /* Are there attributes to be set (additional to the mode) ? */
00345                   if(attributes_create.asked_attributes != 0ULL &&
00346                      attributes_create.asked_attributes != FSAL_ATTR_MODE)
00347                     {
00348                       /* A call to cache_inode_setattr is required */
00349                       if(cache_inode_setattr(file_pentry,
00350                                              &attributes_create,
00351                                              pcontext,
00352                                              &cache_status) != CACHE_INODE_SUCCESS)
00353                         {
00354                           /* If we are here, there was an error */
00355                           nfs_SetFailedStatus(pcontext, pexport,
00356                                               preq->rq_vers,
00357                                               cache_status,
00358                                               &pres->res_dirop2.status,
00359                                               &pres->res_create3.status,
00360                                               NULL, NULL,
00361                                               parent_pentry,
00362                                               ppre_attr,
00363                                               &(pres->res_create3.CREATE3res_u.resfail.
00364                                                 dir_wcc), NULL, NULL, NULL);
00365 
00366                           if(nfs_RetryableError(cache_status)) {
00367                               rc = NFS_REQ_DROP;
00368                               goto out;
00369                           }
00370 
00371                           rc = NFS_REQ_OK;
00372                           goto out;
00373                         }
00374 
00375                       /* Get the resulting attributes from the Cache Inode */
00376                       if(cache_inode_getattr(file_pentry,
00377                                              &attr_newfile,
00378                                              pcontext,
00379                                              &cache_status) != CACHE_INODE_SUCCESS)
00380                         {
00381                           /* If we are here, there was an error */
00382 
00383                           nfs_SetFailedStatus(pcontext, pexport,
00384                                               preq->rq_vers,
00385                                               cache_status,
00386                                               &pres->res_dirop2.status,
00387                                               &pres->res_create3.status,
00388                                               NULL, NULL,
00389                                               parent_pentry,
00390                                               ppre_attr,
00391                                               &(pres->res_create3.CREATE3res_u.resfail.
00392                                                 dir_wcc), NULL, NULL, NULL);
00393 
00394                           if(nfs_RetryableError(cache_status)) {
00395                             rc = NFS_REQ_DROP;
00396                             goto out;
00397                           }
00398 
00399                           rc = NFS_REQ_OK;
00400                           goto out;
00401                         }
00402 
00403                     }
00404 
00405                   switch (preq->rq_vers)
00406                     {
00407                     case NFS_V2:
00408                       /* Build file handle */
00409                       if(nfs2_FSALToFhandle(
00410                               &(pres->res_dirop2.DIROP2res_u.diropok.file),
00411                               &file_pentry->handle,
00412                               pexport) == 0)
00413                         pres->res_dirop2.status = NFSERR_IO;
00414                       else
00415                         {
00416                           if(!nfs2_FSALattr_To_Fattr(
00417                                   pexport, &attr_newfile,
00418                                   &(pres->res_dirop2.DIROP2res_u.
00419                                     diropok.attributes)))
00420                             pres->res_dirop2.status = NFSERR_IO;
00421                           else
00422                             pres->res_dirop2.status = NFS_OK;
00423                         }
00424                       break;
00425 
00426                     case NFS_V3:
00427                       /* Build file handle */
00428                       pres->res_create3.status =
00429                         nfs3_AllocateFH(&pres->res_create3.CREATE3res_u
00430                                         .resok.obj.post_op_fh3_u.handle);
00431                       if (pres->res_create3.status != NFS3_OK)
00432                         {
00433                           rc = NFS_REQ_OK;
00434                           goto out;
00435                         }
00436 
00437                       /* Set Post Op Fh3 structure */
00438                       if(nfs3_FSALToFhandle(
00439                               &(pres->res_create3.CREATE3res_u.resok
00440                                 .obj.post_op_fh3_u.handle),
00441                               &file_pentry->handle, pexport) == 0)
00442                         {
00443                           gsh_free(pres->res_create3.CREATE3res_u.resok.obj.
00444                                    post_op_fh3_u.handle.data.data_val);
00445 
00446                           pres->res_create3.status = NFS3ERR_BADHANDLE;
00447                           rc = NFS_REQ_OK;
00448                           goto out;
00449                         }
00450 
00451                       /* Set Post Op Fh3 structure */
00452                       pres->res_create3.CREATE3res_u.resok.obj.handle_follows
00453                         = TRUE;
00454 
00455                       /* Get the attributes of the parent after the
00456                          operation */
00457                       attr_parent_after = parent_pentry->attributes;
00458 
00459                       /* Build entry attributes */
00460                       nfs_SetPostOpAttr(pexport,
00461                                         &attr_newfile,
00462                                         &(pres->res_create3.CREATE3res_u.resok.
00463                                           obj_attributes));
00464 
00465                       /*
00466                        * Build Weak Cache Coherency data
00467                        */
00468                       nfs_SetWccData(pexport,
00469                                      ppre_attr,
00470                                      &attr_parent_after,
00471                                      &(pres->res_create3.CREATE3res_u
00472                                        .resok.dir_wcc));
00473 
00474                       pres->res_create3.status = NFS3_OK;
00475                       break;
00476                     }       /* switch */
00477                   rc = NFS_REQ_OK;
00478                   goto out;
00479                 }
00480             }
00481           else
00482             {
00483               if(cache_status_lookup == CACHE_INODE_SUCCESS)
00484                 {
00485                   /* Trying to create a file that already exists */
00486                   cache_status = CACHE_INODE_ENTRY_EXISTS;
00487 
00488                   switch (preq->rq_vers)
00489                     {
00490                     case NFS_V2:
00491                       pres->res_dirop2.status = NFSERR_EXIST;
00492                       break;
00493 
00494                     case NFS_V3:
00495                       pres->res_create3.status = NFS3ERR_EXIST;
00496                       break;
00497                     }
00498                 }
00499               else
00500                 {
00501                   /* Server fault */
00502                   cache_status = cache_status_lookup;
00503 
00504                   switch (preq->rq_vers)
00505                     {
00506                     case NFS_V2:
00507                       pres->res_dirop2.status = NFSERR_IO;
00508                       break;
00509 
00510                     case NFS_V3:
00511                       pres->res_create3.status = NFS3ERR_INVAL;
00512                       break;
00513                     }
00514                 }
00515 
00516               nfs_SetFailedStatus(pcontext, pexport,
00517                                   preq->rq_vers,
00518                                   cache_status,
00519                                   &pres->res_dirop2.status,
00520                                   &pres->res_create3.status,
00521                                   NULL, NULL,
00522                                   parent_pentry,
00523                                   ppre_attr,
00524                                   &(pres->res_create3.CREATE3res_u.resfail.dir_wcc),
00525                                   NULL, NULL, NULL);
00526 
00527               rc = NFS_REQ_OK;
00528               goto out;
00529             } /* if( cache_status_lookup == CACHE_INODE_NOT_FOUND ) */
00530         }
00531     }
00532 
00533   /* Set the exit status */
00534   nfs_SetFailedStatus(pcontext, pexport,
00535                       preq->rq_vers,
00536                       cache_status,
00537                       &pres->res_dirop2.status,
00538                       &pres->res_create3.status,
00539                       NULL, NULL,
00540                       parent_pentry,
00541                       ppre_attr,
00542                       &(pres->res_create3.CREATE3res_u.resfail.dir_wcc),
00543                       NULL, NULL, NULL);
00544 
00545   /* If we are here, there was an error */
00546   if(nfs_RetryableError(cache_status))
00547     {
00548       rc = NFS_REQ_DROP;
00549       goto out;
00550     }
00551 
00552   rc = NFS_REQ_OK;
00553 
00554 out:
00555   /* return references */
00556   if (file_pentry)
00557       cache_inode_put(file_pentry);
00558 
00559   if (parent_pentry)
00560       cache_inode_put(parent_pentry);
00561 
00562   return (rc);
00563 
00564 }                               /* nfs_Create */
00565 
00574 void nfs_Create_Free(nfs_res_t * resp)
00575 {
00576   if((resp->res_create3.status == NFS3_OK) &&
00577      (resp->res_create3.CREATE3res_u.resok.obj.handle_follows == TRUE))
00578     gsh_free(resp->res_create3.CREATE3res_u.resok.obj
00579              .post_op_fh3_u.handle.data.data_val);
00580 }