Пример #1
0
int nfs41_op_write(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs41_op_write";

  fsal_seek_t seek_descriptor;
  fsal_size_t size;
  fsal_size_t written_size;
  fsal_off_t offset;
  fsal_boolean_t eof_met;
  bool_t stable_flag = TRUE;
  caddr_t bufferdata;
  stable_how4 stable_how;
  cache_content_status_t content_status;
  cache_inode_state_t *pstate_found = NULL;
  cache_inode_status_t cache_status;
  fsal_attrib_list_t attr;
  cache_entry_t *entry = NULL;
  cache_inode_state_t *pstate_iterate = NULL;
  cache_inode_state_t *pstate_previous_iterate = NULL;

  int rc = 0;

  cache_content_policy_data_t datapol;

  datapol.UseMaxCacheSize = FALSE;

  /* Lock are not supported */
  resp->resop = NFS4_OP_WRITE;
  res_WRITE4.status = NFS4_OK;

  /* If there is no FH */
  if(nfs4_Is_Fh_Empty(&(data->currentFH)))
    {
      res_WRITE4.status = NFS4ERR_NOFILEHANDLE;
      return res_WRITE4.status;
    }

  /* If the filehandle is invalid */
  if(nfs4_Is_Fh_Invalid(&(data->currentFH)))
    {
      res_WRITE4.status = NFS4ERR_BADHANDLE;
      return res_WRITE4.status;
    }

  /* Tests if the Filehandle is expired (for volatile filehandle) */
  if(nfs4_Is_Fh_Expired(&(data->currentFH)))
    {
      res_WRITE4.status = NFS4ERR_FHEXPIRED;
      return res_WRITE4.status;
    }

  /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */
  if(nfs4_Is_Fh_Xattr(&(data->currentFH)))
    return nfs4_op_write_xattr(op, data, resp);

  /* Manage access type MDONLY */
  if(data->pexport->access_type == ACCESSTYPE_MDONLY)
    {
      res_WRITE4.status = NFS4ERR_DQUOT;
      return res_WRITE4.status;
    }

  /* vnode to manage is the current one */
  entry = data->current_entry;

  /* Check for special stateid */
  if(!memcmp((char *)all_zero, arg_WRITE4.stateid.other, 12) &&
     arg_WRITE4.stateid.seqid == 0)
    {
      /* "All 0 stateid special case", see RFC3530 page 220-221 for details 
       * This will be treated as a client that held no lock at all,
       * I set pstate_found to NULL to remember this situation later */
      pstate_found = NULL;
    }
  else if(!memcmp((char *)all_one, arg_WRITE4.stateid.other, 12) &&
          arg_WRITE4.stateid.seqid == 0xFFFFFFFF)
    {
      /* "All 1 stateid special case", see RFC3530 page 220-221 for details 
       * This will be treated as a client that held no lock at all,
       * I set pstate_found to NULL to remember this situation later */
      pstate_found = NULL;
    }

  /* NB: After this points, if pstate_found == NULL, then the stateid is all-0 or all-1 */

  /* Iterate through file's state to look for conflicts */
  pstate_iterate = NULL;
  pstate_previous_iterate = NULL;
  do
    {
      cache_inode_state_iterate(data->current_entry,
                                &pstate_iterate,
                                pstate_previous_iterate,
                                data->pclient, data->pcontext, &cache_status);
      if(cache_status == CACHE_INODE_STATE_ERROR)
        break;                  /* Get out of the loop */

      if(cache_status == CACHE_INODE_INVALID_ARGUMENT)
        {
          res_WRITE4.status = NFS4ERR_INVAL;
          return res_WRITE4.status;
        }

      if(pstate_iterate != NULL)
        {
          switch (pstate_iterate->state_type)
            {
            case CACHE_INODE_STATE_SHARE:
              if(pstate_found != pstate_iterate)
                {
                  if(pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_WRITE)
                    {
                      /* Writing to this file if prohibited, file is write-denied */
                      res_WRITE4.status = NFS4ERR_LOCKED;
                      return res_WRITE4.status;
                    }
                }
              break;
            }
        }
      pstate_previous_iterate = pstate_iterate;
    }
  while(pstate_iterate != NULL);

  /* Only files can be written */
  if(data->current_filetype != REGULAR_FILE)
    {
      /* If the destination is no file, return EISDIR if it is a directory and EINAVL otherwise */
      if(data->current_filetype == DIR_BEGINNING
         || data->current_filetype == DIR_CONTINUE)
        res_WRITE4.status = NFS4ERR_ISDIR;
      else
        res_WRITE4.status = NFS4ERR_INVAL;

      return res_WRITE4.status;
    }

  /* Get the characteristics of the I/O to be made */
  offset = arg_WRITE4.offset;
  size = arg_WRITE4.data.data_len;
  stable_how = arg_WRITE4.stable;
  LogFullDebug(COMPONENT_NFS_V4,"   NFS4_OP_WRITE: offset = %llu  length = %llu   stable = %d\n", offset, size,
         stable_how);

  if((data->pexport->options & EXPORT_OPTION_MAXOFFSETWRITE) ==
     EXPORT_OPTION_MAXOFFSETWRITE)
    if((fsal_off_t) (offset + size) > data->pexport->MaxOffsetWrite)
      {
        res_WRITE4.status = NFS4ERR_DQUOT;
        return res_WRITE4.status;
      }

  /* The size to be written should not be greater than FATTR4_MAXWRITESIZE because this value is asked 
   * by the client at mount time, but we check this by security */
  if((data->pexport->options & EXPORT_OPTION_MAXWRITE == EXPORT_OPTION_MAXWRITE) &&
     size > data->pexport->MaxWrite)
    {
      /*
       * The client asked for too much data, we
       * must restrict him 
       */
      size = data->pexport->MaxWrite;
    }

  /* Where are the data ? */
  bufferdata = arg_WRITE4.data.data_val;

  LogFullDebug(COMPONENT_NFS_V4, "             NFS4_OP_WRITE: offset = %llu  length = %llu\n", offset, size);

  /* if size == 0 , no I/O) are actually made and everything is alright */
  if(size == 0)
    {
      res_WRITE4.WRITE4res_u.resok4.count = 0;
      res_WRITE4.WRITE4res_u.resok4.committed = FILE_SYNC4;

      memcpy(res_WRITE4.WRITE4res_u.resok4.writeverf, NFS4_write_verifier,
             sizeof(verifier4));

      res_WRITE4.status = NFS4_OK;
      return res_WRITE4.status;
    }

  if((data->pexport->options & EXPORT_OPTION_USE_DATACACHE) &&
     (cache_content_cache_behaviour(entry,
                                    &datapol,
                                    (cache_content_client_t *) (data->pclient->
                                                                pcontent_client),
                                    &content_status) == CACHE_CONTENT_FULLY_CACHED)
     && (entry->object.file.pentry_content == NULL))
    {
      /* Entry is not in datacache, but should be in, cache it .
       * Several threads may call this function at the first time and a race condition can occur here
       * in order to avoid this, cache_inode_add_data_cache is "mutex protected" 
       * The first call will create the file content cache entry, the further will return
       * with error CACHE_INODE_CACHE_CONTENT_EXISTS which is not a pathological thing here */

      datapol.UseMaxCacheSize = data->pexport->options & EXPORT_OPTION_MAXCACHESIZE;
      datapol.MaxCacheSize = data->pexport->MaxCacheSize;

      /* Status is set in last argument */
      cache_inode_add_data_cache(entry, data->ht, data->pclient, data->pcontext,
                                 &cache_status);

      if((cache_status != CACHE_INODE_SUCCESS) &&
         (cache_status != CACHE_INODE_CACHE_CONTENT_EXISTS))
        {
          res_WRITE4.status = NFS4ERR_SERVERFAULT;
          return res_WRITE4.status;
        }

    }

  if((nfs_param.core_param.use_nfs_commit == TRUE) && (arg_WRITE4.stable == UNSTABLE4))
    {
      stable_flag = FALSE;
    }
  else
    {
      stable_flag = TRUE;
    }

  /* An actual write is to be made, prepare it */
  /* only FILE_SYNC mode is supported */
  /* Set up uio to define the transfer */
  seek_descriptor.whence = FSAL_SEEK_SET;
  seek_descriptor.offset = offset;

  if(cache_inode_rdwr(entry,
                      CACHE_CONTENT_WRITE,
                      &seek_descriptor,
                      size,
                      &written_size,
                      &attr,
                      bufferdata,
                      &eof_met,
                      data->ht,
                      data->pclient,
                      data->pcontext, stable_flag, &cache_status) != CACHE_INODE_SUCCESS)
    {
      res_WRITE4.status = nfs4_Errno(cache_status);
      return res_WRITE4.status;
    }

  /* Set the returned value */
  if(stable_flag == TRUE)
    res_WRITE4.WRITE4res_u.resok4.committed = FILE_SYNC4;
  else
    res_WRITE4.WRITE4res_u.resok4.committed = UNSTABLE4;

  res_WRITE4.WRITE4res_u.resok4.count = written_size;
  memcpy(res_WRITE4.WRITE4res_u.resok4.writeverf, NFS4_write_verifier, sizeof(verifier4));

  res_WRITE4.status = NFS4_OK;

  return res_WRITE4.status;
}                               /* nfs41_op_write */
Пример #2
0
int nfs_Read(nfs_arg_t *parg,
             exportlist_t *pexport,
             fsal_op_context_t *pcontext,
             nfs_worker_data_t *pworker,
             struct svc_req *preq,
             nfs_res_t *pres)
{
  cache_entry_t *pentry;
  fsal_attrib_list_t attr;
  fsal_attrib_list_t pre_attr;
  cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
  size_t size = 0;
  size_t read_size = 0;
  fsal_off_t offset = 0;
  void *data = NULL;
  cache_inode_file_type_t filetype;
  fsal_boolean_t eof_met=FALSE;
  int rc = NFS_REQ_OK;

  if(isDebug(COMPONENT_NFSPROTO))
    {
      char str[LEN_FH_STR];

      switch (preq->rq_vers)
        {
        case NFS_V2:
          offset = parg->arg_read2.offset;
          size = parg->arg_read2.count;
          break;
        case NFS_V3:
          offset = parg->arg_read3.offset;
          size = parg->arg_read3.count;
        }

      nfs_FhandleToStr(preq->rq_vers,
                       &(parg->arg_read2.file),
                       &(parg->arg_read3.file),
                       NULL,
                       str);
      LogDebug(COMPONENT_NFSPROTO,
               "REQUEST PROCESSING: Calling nfs_Read handle: %s start: %llx len: %llx",
               str, (unsigned long long) offset, (unsigned long long) size);
    }

  if(preq->rq_vers == NFS_V3)
    {
      /* to avoid setting it on each error case */
      pres->res_read3.READ3res_u.resfail.file_attributes.attributes_follow = FALSE;
      /* initialize for read of size 0 */
      pres->res_read3.READ3res_u.resok.eof = FALSE;
      pres->res_read3.READ3res_u.resok.count = 0;
      pres->res_read3.READ3res_u.resok.data.data_val = NULL;
      pres->res_read3.READ3res_u.resok.data.data_len = 0;
      pres->res_read3.status = NFS3_OK;
    }
  else if(preq->rq_vers == NFS_V2)
    {
      /* initialize for read of size 0 */
      pres->res_read2.READ2res_u.readok.data.nfsdata2_val = NULL;
      pres->res_read2.READ2res_u.readok.data.nfsdata2_len = 0;
      pres->res_attr2.status = NFS_OK;
    }

  /* Convert file handle into a cache entry */
  if((pentry = nfs_FhandleToCache(preq->rq_vers,
                                  &(parg->arg_read2.file),
                                  &(parg->arg_read3.file),
                                  NULL,
                                  &(pres->res_read2.status),
                                  &(pres->res_read3.status),
                                  NULL, &pre_attr, pcontext, &rc)) == NULL)
    {
      /* Stale NFS FH ? */
      goto out;
    }

  if((preq->rq_vers == NFS_V3) && (nfs3_Is_Fh_Xattr(&(parg->arg_read3.file))))
  {
    rc = nfs3_Read_Xattr(parg, pexport, pcontext, preq, pres);
    goto out;
  }

  if(cache_inode_access(pentry,
                        FSAL_READ_ACCESS,
                        pcontext,
                        &cache_status) != CACHE_INODE_SUCCESS)
    {
      switch (preq->rq_vers)
        {
        case NFS_V2:
          pres->res_attr2.status = nfs2_Errno(cache_status);
          break;

        case NFS_V3:
          pres->res_read3.status = nfs3_Errno(cache_status);
          break;
        }
      rc = NFS_REQ_OK;
      goto out;
    }

  /* Extract the filetype */
  filetype = cache_inode_fsal_type_convert(pre_attr.type);

  /* Sanity check: read only from a regular file */
  if(filetype != REGULAR_FILE)
    {
      switch (preq->rq_vers)
        {
        case NFS_V2:
          /*
           * In the RFC tell it not good but it does
           * not tell what to do ... 
           */
          pres->res_attr2.status = NFSERR_ISDIR;
          break;

        case NFS_V3:
          if(filetype == DIRECTORY)
            pres->res_read3.status = NFS3ERR_ISDIR;
          else
            pres->res_read3.status = NFS3ERR_INVAL;
          break;
        }

      rc = NFS_REQ_OK;
      goto out;
    }

  /* For MDONLY export, reject write operation */
  /* Request of type MDONLY_RO were rejected at the nfs_rpc_dispatcher level */
  /* This is done by replying EDQUOT (this error is known for not disturbing the client's requests cache */
  if(pexport->access_type == ACCESSTYPE_MDONLY
     || pexport->access_type == ACCESSTYPE_MDONLY_RO)
    {
      switch (preq->rq_vers)
        {
        case NFS_V2:
          pres->res_attr2.status = NFSERR_DQUOT;
          break;

        case NFS_V3:
          pres->res_read3.status = NFS3ERR_DQUOT;
          break;
        }

      nfs_SetFailedStatus(pcontext, pexport,
                          preq->rq_vers,
                          cache_status,
                          &pres->res_read2.status,
                          &pres->res_read3.status,
                          pentry,
                          &(pres->res_read3.READ3res_u.resfail.file_attributes),
                          NULL, NULL, NULL, NULL, NULL, NULL);

      rc = NFS_REQ_OK;
      goto out;
    }

  /* Extract the argument from the request */
  switch (preq->rq_vers)
    {
    case NFS_V2:
      offset = parg->arg_read2.offset;  /* beginoffset is obsolete */
      size = parg->arg_read2.count;     /* totalcount is obsolete  */
      break;

    case NFS_V3:
      offset = parg->arg_read3.offset;
      size = parg->arg_read3.count;
      break;
    }

  /* 
   * do not exceed maxium READ offset if set 
   */
  if((pexport->options & EXPORT_OPTION_MAXOFFSETREAD) == EXPORT_OPTION_MAXOFFSETREAD)
    {
      LogFullDebug(COMPONENT_NFSPROTO,
                   "-----> Read offset=%llu count=%llu MaxOffSet=%llu",
                   (unsigned long long) offset,
                   (unsigned long long) size,
                   (unsigned long long) pexport->MaxOffsetRead);

      if((fsal_off_t) (offset + size) > pexport->MaxOffsetRead)
        {
          LogEvent(COMPONENT_NFSPROTO,
                   "NFS READ: A client tryed to violate max file size %llu for exportid #%hu",
                   (unsigned long long) pexport->MaxOffsetRead, pexport->id);

          switch (preq->rq_vers)
            {
            case NFS_V2:
              pres->res_attr2.status = NFSERR_DQUOT;
              break;

            case NFS_V3:
              pres->res_read3.status = NFS3ERR_INVAL;
              break;
            }

          nfs_SetFailedStatus(pcontext, pexport,
                              preq->rq_vers,
                              cache_status,
                              &pres->res_read2.status,
                              &pres->res_read3.status,
                              pentry,
                              &(pres->res_read3.READ3res_u.resfail.file_attributes),
                              NULL, NULL, NULL, NULL, NULL, NULL);

          rc = NFS_REQ_OK;
          goto out;
        }
    }

  /*
   * We should not exceed the FSINFO rtmax field for
   * the size 
   */
  if(((pexport->options & EXPORT_OPTION_MAXREAD) == EXPORT_OPTION_MAXREAD) &&
     size > pexport->MaxRead)
    {
      /*
       * The client asked for too much, normally
       * this should not happen because the client
       * is calling nfs_Fsinfo at mount time and so
       * is aware of the server maximum write size 
       */
      size = pexport->MaxRead;
    }

  if(size == 0)
    {
      nfs_read_ok(pexport, preq, pres, NULL, 0, &pre_attr, 0);
      rc = NFS_REQ_OK;
      goto out;
    }
  else
    {
      data = gsh_malloc(size);
      if(data == NULL)
        {
          rc = NFS_REQ_DROP;
          goto out;
        }

      if((cache_inode_rdwr(pentry,
                           CACHE_INODE_READ,
                           offset,
                           size,
                           &read_size,
                           data,
                           &eof_met,
                           pcontext,
                           CACHE_INODE_SAFE_WRITE_TO_FS,
                           &cache_status) == CACHE_INODE_SUCCESS) &&
         (cache_inode_getattr(pentry, &attr, pcontext,
                              &cache_status)) == CACHE_INODE_SUCCESS)

        {
          nfs_read_ok(pexport, preq, pres, data, read_size, &attr, 
                      ((offset + read_size) >= attr.filesize));
          rc = NFS_REQ_OK;
          goto out;
        }
      gsh_free(data);
    }

  /* If we are here, there was an error */
  if(nfs_RetryableError(cache_status))
    {
      rc = NFS_REQ_DROP;
      goto out;
    }

  nfs_SetFailedStatus(pcontext, pexport,
                      preq->rq_vers,
                      cache_status,
                      &pres->res_read2.status,
                      &pres->res_read3.status,
                      pentry,
                      &(pres->res_read3.READ3res_u.resfail.file_attributes),
                      NULL, NULL, NULL, NULL, NULL, NULL);

  rc = NFS_REQ_OK;

out:
  /* return references */
  if (pentry)
      cache_inode_put(pentry);

  return (rc);

}                               /* nfs_Read */
Пример #3
0
int nfs41_op_read(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs41_op_read";

  fsal_seek_t seek_descriptor;
  fsal_size_t size;
  fsal_size_t read_size;
  fsal_off_t offset;
  fsal_boolean_t eof_met;
  caddr_t bufferdata;
  cache_inode_status_t cache_status;
  cache_inode_state_t *pstate_found = NULL;
  cache_content_status_t content_status;
  fsal_attrib_list_t attr;
  cache_entry_t *entry = NULL;
  cache_inode_state_t *pstate_iterate = NULL;
  cache_inode_state_t *pstate_previous_iterate = NULL;
  int rc = 0;

  cache_content_policy_data_t datapol;

  datapol.UseMaxCacheSize = FALSE;

  /* Say we are managing NFS4_OP_READ */
  resp->resop = NFS4_OP_READ;
  res_READ4.status = NFS4_OK;

  /* If there is no FH */
  if(nfs4_Is_Fh_Empty(&(data->currentFH)))
    {
      res_READ4.status = NFS4ERR_NOFILEHANDLE;
      return res_READ4.status;
    }

  /* If the filehandle is invalid */
  if(nfs4_Is_Fh_Invalid(&(data->currentFH)))
    {
      res_READ4.status = NFS4ERR_BADHANDLE;
      return res_READ4.status;
    }

  /* Tests if the Filehandle is expired (for volatile filehandle) */
  if(nfs4_Is_Fh_Expired(&(data->currentFH)))
    {
      res_READ4.status = NFS4ERR_FHEXPIRED;
      return res_READ4.status;
    }

  /* vnode to manage is the current one */
  entry = data->current_entry;

  /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */
  if(nfs4_Is_Fh_Xattr(&(data->currentFH)))
    return nfs4_op_read_xattr(op, data, resp);

  /* Manage access type MDONLY */
  if(data->pexport->access_type == ACCESSTYPE_MDONLY)
    {
      res_READ4.status = NFS4ERR_DQUOT;
      return res_READ4.status;
    }

  /* Check for special stateid */
  if(!memcmp((char *)all_zero, arg_READ4.stateid.other, 12) &&
     arg_READ4.stateid.seqid == 0)
    {
      /* "All 0 stateid special case" */
      /* This will be treated as a client that held no lock at all,
       * I set pstate_found to NULL to remember this situation later */
      pstate_found = NULL;
    }
  else if(!memcmp((char *)all_one, arg_READ4.stateid.other, 12) &&
          arg_READ4.stateid.seqid == 0xFFFFFFFF)
    {
      /* "All 1 stateid special case" */
      /* This will be treated as a client that held no lock at all, but may goes through locks 
       * I set pstate_found to 1 to remember this situation later */
      pstate_found = NULL;
    }

  /* NB: After this points, if pstate_found == NULL, then the stateid is all-0 or all-1 */

  /* Iterate through file's state to look for conflicts */
  pstate_iterate = NULL;
  pstate_previous_iterate = NULL;
  do
    {
      cache_inode_state_iterate(data->current_entry,
                                &pstate_iterate,
                                pstate_previous_iterate,
                                data->pclient, data->pcontext, &cache_status);
      if(cache_status == CACHE_INODE_STATE_ERROR)
        break;                  /* Get out of the loop */

      if(cache_status == CACHE_INODE_INVALID_ARGUMENT)
        {
          res_READ4.status = NFS4ERR_INVAL;
          return res_READ4.status;
        }

      if(pstate_iterate != NULL)
        {
          switch (pstate_iterate->state_type)
            {
            case CACHE_INODE_STATE_SHARE:
              if(pstate_found != pstate_iterate)
                {
                  if(pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_READ)
                    {
                      /* Writing to this file if prohibited, file is write-denied */
                      res_READ4.status = NFS4ERR_LOCKED;
                      return res_READ4.status;
                    }
                }
              break;
            }
        }
      pstate_previous_iterate = pstate_iterate;
    }
  while(pstate_iterate != NULL);

  /* Only files can be read */
  if(data->current_filetype != REGULAR_FILE)
    {
      /* If the source is no file, return EISDIR if it is a directory and EINAVL otherwise */
      if(data->current_filetype == DIR_BEGINNING
         || data->current_filetype == DIR_CONTINUE)
        res_READ4.status = NFS4ERR_ISDIR;
      else
        res_READ4.status = NFS4ERR_INVAL;

      return res_READ4.status;
    }

  /* Get the size and offset of the read operation */
  offset = arg_READ4.offset;
  size = arg_READ4.count;

  LogFullDebug(COMPONENT_NFS_V4, "   NFS4_OP_READ: offset = %llu  length = %llu\n", offset, size);

  if((data->pexport->options & EXPORT_OPTION_MAXOFFSETREAD) ==
     EXPORT_OPTION_MAXOFFSETREAD)
    if((fsal_off_t) (offset + size) > data->pexport->MaxOffsetRead)
      {
        res_READ4.status = NFS4ERR_DQUOT;
        return res_READ4.status;
      }

  /* Do not read more than FATTR4_MAXREAD */
  if((data->pexport->options & EXPORT_OPTION_MAXREAD == EXPORT_OPTION_MAXREAD) &&
     size > data->pexport->MaxRead)
    {
      /* the client asked for too much data, 
       * this should normally not happen because 
       * client will get FATTR4_MAXREAD value at mount time */
      size = data->pexport->MaxRead;
    }

  /* If size == 0 , no I/O is to be made and everything is alright */
  if(size == 0)
    {
      res_READ4.READ4res_u.resok4.eof = FALSE;  /* end of file was not reached because READ occured, and a size = 0 can not lead to eof */
      res_READ4.READ4res_u.resok4.data.data_len = 0;
      res_READ4.READ4res_u.resok4.data.data_val = NULL;

      res_READ4.status = NFS4_OK;
      return res_READ4.status;
    }

  if((data->pexport->options & EXPORT_OPTION_USE_DATACACHE) &&
     (entry->object.file.pentry_content == NULL))
    {
      /* Entry is not in datacache, but should be in, cache it .
       * Several threads may call this function at the first time and a race condition can occur here
       * in order to avoid this, cache_inode_add_data_cache is "mutex protected" 
       * The first call will create the file content cache entry, the further will return
       * with error CACHE_INODE_CACHE_CONTENT_EXISTS which is not a pathological thing here */

      datapol.UseMaxCacheSize = data->pexport->options & EXPORT_OPTION_MAXCACHESIZE;
      datapol.MaxCacheSize = data->pexport->MaxCacheSize;

      /* Status is set in last argument */
      cache_inode_add_data_cache(entry, data->ht, data->pclient, data->pcontext,
                                 &cache_status);

      if((cache_status != CACHE_INODE_SUCCESS) &&
         (cache_content_cache_behaviour(entry,
                                        &datapol,
                                        (cache_content_client_t *) (data->pclient->
                                                                    pcontent_client),
                                        &content_status) == CACHE_CONTENT_FULLY_CACHED)
         && (cache_status != CACHE_INODE_CACHE_CONTENT_EXISTS))
        {
          res_READ4.status = NFS4ERR_SERVERFAULT;
          return res_READ4.status;
        }

    }

  /* Some work is to be done */
  if((bufferdata = (char *)Mem_Alloc(size)) == NULL)
    {
      res_READ4.status = NFS4ERR_SERVERFAULT;
      return res_READ4.status;
    }
  memset((char *)bufferdata, 0, size);

  seek_descriptor.whence = FSAL_SEEK_SET;
  seek_descriptor.offset = offset;

  if(cache_inode_rdwr(entry,
                      CACHE_CONTENT_READ,
                      &seek_descriptor,
                      size,
                      &read_size,
                      &attr,
                      bufferdata,
                      &eof_met,
                      data->ht,
                      data->pclient,
                      data->pcontext, TRUE, &cache_status) != CACHE_INODE_SUCCESS)
    {
      res_READ4.status = nfs4_Errno(cache_status);
      return res_READ4.status;
    }

  /* What is the filesize ? */
  if((offset + read_size) > attr.filesize)
    res_READ4.READ4res_u.resok4.eof = TRUE;

  res_READ4.READ4res_u.resok4.data.data_len = read_size;
  res_READ4.READ4res_u.resok4.data.data_val = bufferdata;

  LogFullDebug(COMPONENT_NFS_V4,"   NFS4_OP_READ: offset = %llu  read length = %llu eof=%u\n", offset, read_size,
         eof_met);

  /* Is EOF met or not ? */
  if(eof_met == TRUE)
    res_READ4.READ4res_u.resok4.eof = TRUE;
  else
    res_READ4.READ4res_u.resok4.eof = FALSE;

  /* Say it is ok */
  res_READ4.status = NFS4_OK;

  return res_READ4.status;
}                               /* nfs41_op_read */
Пример #4
0
cache_inode_status_t
cache_inode_commit(cache_entry_t * pentry,
                   uint64_t offset,
                   fsal_size_t count,
                   fsal_attrib_list_t * pfsal_attr,
                   hash_table_t * ht,
                   cache_inode_client_t * pclient,
                   fsal_op_context_t * pcontext,
                   uint64_t typeofcommit,
                   cache_inode_status_t * pstatus)
{
    cache_inode_status_t status;
    fsal_seek_t seek_descriptor;
    fsal_size_t size_io_done;
    fsal_boolean_t eof;
    cache_inode_unstable_data_t *udata;
    fsal_status_t fsal_status;

    /* Do not use this function is Data Cache is used */
    if(pentry->object.file.pentry_content != NULL)
     {
            *pstatus = CACHE_INODE_SUCCESS;
            return *pstatus;
     }

    /* If we aren't using the Ganesha write buffer, then we're using the filesystem
     * write buffer so execute a normal fsal_sync() call. */
    if (typeofcommit == FSAL_UNSAFE_WRITE_TO_FS_BUFFER)
    {

      P_w(&pentry->lock);

      /* Can't sync a file descriptor if it's currently closed. */
      if(cache_inode_open(pentry,
                          pclient,
                          FSAL_O_WRONLY, pcontext, pstatus) != CACHE_INODE_SUCCESS)
        {

          V_w(&pentry->lock);
          
          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_COMMIT] += 1;
          
          return *pstatus;
        }

#ifdef _USE_MFSL      
      fsal_status = MFSL_sync(&(pentry->object.file.open_fd.mfsl_fd), NULL); 
#else
      fsal_status = FSAL_sync(&(pentry->object.file.open_fd.fd));
#endif
      if(FSAL_IS_ERROR(fsal_status))
      {
        LogMajor(COMPONENT_CACHE_INODE,
                 "cache_inode_rdwr: fsal_sync() failed: fsal_status.major = %d",
                 fsal_status.major);

      /* Close the fd that we just opened before the FSAL_sync(). We are already
       * replying with an error. No need to catch an additional error form 
       * a close? */
         cache_inode_close(pentry, pclient, &status);

        V_w(&pentry->lock);

        /* stats */
        pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_COMMIT] += 1;

        *pstatus = CACHE_INODE_FSAL_ERROR;
        return *pstatus;
      }
      *pstatus = CACHE_INODE_SUCCESS;

      /* Close the fd that we just opened before the FSAL_sync() */
      if(cache_inode_close(pentry, pclient, pstatus) != CACHE_INODE_SUCCESS)
        {
          LogEvent(COMPONENT_CACHE_INODE,
                   "cache_inode_rdwr: cache_inode_close = %d",
                   *pstatus);
          
          V_w(&pentry->lock);
          
          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_COMMIT] += 1;
          
          return *pstatus;
        }

      V_w(&pentry->lock);      
      return *pstatus;
    }

    /* Ok, it looks like we're using the Ganesha write buffer. This means we
     * will either be writing to the buffer, or writing a stable write to the
     * file system if the buffer is already full. */

    udata = &pentry->object.file.unstable_data;
    if(udata->buffer == NULL)
        {
            *pstatus = CACHE_INODE_SUCCESS;
            return *pstatus;
        }
    if(count == 0 || count == 0xFFFFFFFFL)
        {
            /* Count = 0 means "flush all data to permanent storage */
            seek_descriptor.offset = udata->offset;
            seek_descriptor.whence = FSAL_SEEK_SET;

            status = cache_inode_rdwr(pentry,
                                      CACHE_INODE_WRITE,
                                      &seek_descriptor, udata->length,
                                      &size_io_done, pfsal_attr,
                                      udata->buffer, &eof, ht,
                                      pclient, pcontext, TRUE, pstatus);
            if (status != CACHE_INODE_SUCCESS)
                return *pstatus;

            P_w(&pentry->lock);

            Mem_Free(udata->buffer);
            udata->buffer = NULL;

            V_w(&pentry->lock);
        }
    else
        {
            if(offset < udata->offset)
                {
                    *pstatus = CACHE_INODE_INVALID_ARGUMENT;
                    return *pstatus;
                }

            seek_descriptor.offset = offset;
            seek_descriptor.whence = FSAL_SEEK_SET;

            return cache_inode_rdwr(pentry,
                                    CACHE_INODE_WRITE,
                                    &seek_descriptor,
                                    count,
                                    &size_io_done,
                                    pfsal_attr,
                                    (char *)(udata->buffer + offset - udata->offset),
                                    &eof, ht, pclient,
                                    pcontext, TRUE, pstatus);
    }
  /* Regulat exit */
  *pstatus = CACHE_INODE_SUCCESS;
  return *pstatus;
}
Пример #5
0
int _9p_write( _9p_request_data_t * preq9p,
               void  * pworker_data,
               u32 * plenout,
               char * preply)
{
    char * cursor = preq9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE ;
    nfs_worker_data_t * pwkrdata = (nfs_worker_data_t *)pworker_data ;

    int rc = 0 ;
    u32 err = 0 ;


    u16 * msgtag = NULL ;
    u32 * fid    = NULL ;
    u64 * offset = NULL ;
    u32 * count  = NULL ;

    u32 outcount = 0 ;

    _9p_fid_t * pfid = NULL ;

    fsal_seek_t seek_descriptor;
    fsal_size_t size;
    fsal_size_t written_size = 0;
    fsal_attrib_list_t attr;
    fsal_boolean_t eof_met;
    cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
    uint64_t stable_flag = FSAL_SAFE_WRITE_TO_FS;

    caddr_t databuffer = NULL ;

    /* Get data */
    _9p_getptr( cursor, msgtag, u16 ) ;
    _9p_getptr( cursor, fid,    u32 ) ;
    _9p_getptr( cursor, offset, u64 ) ;
    _9p_getptr( cursor, count,  u32 ) ;

    databuffer = cursor ;

    LogDebug( COMPONENT_9P, "TWRITE: tag=%u fid=%u offset=%llu count=%u",
              (u32)*msgtag, *fid, (unsigned long long)*offset, *count  ) ;

    if( *fid >= _9P_FID_PER_CONN )
    {
        err = ERANGE ;
        rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ;
        return rc ;
    }

    pfid = &preq9p->pconn->fids[*fid] ;

    /* Do the job */
    seek_descriptor.whence = FSAL_SEEK_SET ;
    seek_descriptor.offset = *offset;

    size = *count ;

    if(cache_inode_rdwr( pfid->pentry,
                         CACHE_INODE_WRITE,
                         &seek_descriptor,
                         size,
                         &written_size,
                         &attr,
                         databuffer,
                         &eof_met,
                         pwkrdata->ht,
                         &pwkrdata->cache_inode_client,
                         &pfid->fsal_op_context,
                         stable_flag,
                         &cache_status ) != CACHE_INODE_SUCCESS )
    {
        err = _9p_tools_errno( cache_status ) ; ;
        rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ;
        return rc ;
    }

    outcount = (u32)written_size ;

    /* Build the reply */
    _9p_setinitptr( cursor, preply, _9P_RWRITE ) ;
    _9p_setptr( cursor, msgtag, u16 ) ;

    _9p_setvalue( cursor, outcount, u32 ) ;

    _9p_setendptr( cursor, preply ) ;
    _9p_checkbound( cursor, preply, plenout ) ;

    LogDebug( COMPONENT_9P, "RWRITE: tag=%u fid=%u offset=%llu input count=%u output count=%u",
              (u32)*msgtag, *fid , (unsigned long long)*offset, *count, outcount ) ;

    return 1 ;
}
Пример #6
0
int _9p_write(struct _9p_request_data *req9p, u32 *plenout, char *preply)
{
	char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE;
	u16 *msgtag = NULL;
	u32 *fid = NULL;
	u64 *offset = NULL;
	u32 *count = NULL;

	u32 outcount = 0;

	struct _9p_fid *pfid = NULL;

	size_t size;
	size_t written_size = 0;
	bool eof_met;
	cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
	/* bool sync = true; */
	bool sync = false;

	char *databuffer = NULL;

	/* fsal_status_t fsal_status; */

	/* Get data */
	_9p_getptr(cursor, msgtag, u16);
	_9p_getptr(cursor, fid, u32);
	_9p_getptr(cursor, offset, u64);
	_9p_getptr(cursor, count, u32);

	databuffer = cursor;

	LogDebug(COMPONENT_9P, "TWRITE: tag=%u fid=%u offset=%llu count=%u",
		 (u32) *msgtag, *fid, (unsigned long long)*offset, *count);

	if (*fid >= _9P_FID_PER_CONN)
		return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply);

	pfid = req9p->pconn->fids[*fid];

	/* Make sure the requested amount of data respects negotiated msize */
	if (*count + _9P_ROOM_TWRITE > req9p->pconn->msize)
		return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply);

	/* Check that it is a valid fid */
	if (pfid == NULL || pfid->pentry == NULL) {
		LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid);
		return _9p_rerror(req9p, msgtag, EIO, plenout, preply);
	}

	_9p_init_opctx(pfid, req9p);

	if ((op_ctx->export_perms->options &
				 EXPORT_OPTION_WRITE_ACCESS) == 0)
		return _9p_rerror(req9p, msgtag, EROFS, plenout, preply);

	/* Do the job */
	size = *count;

	if (pfid->specdata.xattr.xattr_content != NULL) {
		memcpy(pfid->specdata.xattr.xattr_content + (*offset),
		       databuffer, size);
		pfid->specdata.xattr.xattr_offset += size;
		pfid->specdata.xattr.xattr_write = true;

		/* ADD CODE TO DETECT GAP */
#if 0
		fsal_status =
		    pfid->pentry->obj_handle->ops->setextattr_value_by_id(
			pfid->pentry->obj_handle,
			&pfid->op_context,
			pfid->specdata.xattr.xattr_id,
			xattrval, size + 1);

		if (FSAL_IS_ERROR(fsal_status))
			return _9p_rerror(req9p, msgtag,
					  _9p_tools_errno
					  (cache_inode_error_convert
					   (fsal_status)), plenout, preply);
#endif

		outcount = *count;
	} else {
		cache_status = cache_inode_rdwr(pfid->pentry, CACHE_INODE_WRITE,
						*offset, size, &written_size,
						databuffer, &eof_met, &sync,
						NULL);

		/* Get the handle, for stats */
		struct gsh_client *client = req9p->pconn->client;

		if (client == NULL) {
			LogDebug(COMPONENT_9P,
				 "Cannot get client block for 9P request");
		} else {
			op_ctx->client = client;

			server_stats_io_done(size,
					     written_size,
					     (cache_status ==
					      CACHE_INODE_SUCCESS) ?
					      true : false,
					     true);
		}

		if (cache_status != CACHE_INODE_SUCCESS)
			return _9p_rerror(req9p, msgtag,
					  _9p_tools_errno(cache_status),
					  plenout, preply);

		outcount = (u32) written_size;

	}

	/* Build the reply */
	_9p_setinitptr(cursor, preply, _9P_RWRITE);
	_9p_setptr(cursor, msgtag, u16);

	_9p_setvalue(cursor, outcount, u32);

	_9p_setendptr(cursor, preply);
	_9p_checkbound(cursor, preply, plenout);

	LogDebug(COMPONENT_9P,
		 "RWRITE: tag=%u fid=%u offset=%llu input count=%u output count=%u",
		 (u32) *msgtag, *fid, (unsigned long long)*offset, *count,
		 outcount);

/**
 * @todo write statistics accounting goes here
 * modeled on nfs I/O stats
 */
	return 1;
}
Пример #7
0
cache_inode_status_t
cache_inode_commit(cache_entry_t * pentry,
                   uint64_t offset,
                   fsal_size_t count,
                   fsal_attrib_list_t * pfsal_attr,
                   hash_table_t * ht,
                   cache_inode_client_t * pclient,
                   fsal_op_context_t * pcontext,
                   cache_inode_status_t * pstatus)
{
    cache_inode_status_t status;
    fsal_seek_t seek_descriptor;
    fsal_size_t size_io_done;
    fsal_boolean_t eof;
    cache_inode_unstable_data_t *udata;

    udata = &pentry->object.file.unstable_data;
    if(udata->buffer == NULL)
        {
            *pstatus = CACHE_INODE_SUCCESS;
            return *pstatus;
        }
    if(count == 0 || count == 0xFFFFFFFFL)
        {
            /* Count = 0 means "flush all data to permanent storage */
            seek_descriptor.offset = udata->offset;
            seek_descriptor.whence = FSAL_SEEK_SET;

            status = cache_inode_rdwr(pentry,
                                      CACHE_INODE_WRITE,
                                      &seek_descriptor, udata->length,
                                      &size_io_done, pfsal_attr,
                                      udata->buffer, &eof, ht,
                                      pclient, pcontext, TRUE, pstatus);
            if (status != CACHE_INODE_SUCCESS)
                return *pstatus;

            P_w(&pentry->lock);

            Mem_Free(udata->buffer);
            udata->buffer = NULL;

            V_w(&pentry->lock);
        }
    else
        {
            if(offset < udata->offset)
                {
                    *pstatus = CACHE_INODE_INVALID_ARGUMENT;
                    return *pstatus;
                }

            seek_descriptor.offset = offset;
            seek_descriptor.whence = FSAL_SEEK_SET;

            return cache_inode_rdwr(pentry,
                                    CACHE_INODE_WRITE,
                                    &seek_descriptor,
                                    count,
                                    &size_io_done,
                                    pfsal_attr,
                                    (char *)(udata->buffer + offset - udata->offset),
                                    &eof, ht, pclient,
                                    pcontext, TRUE, pstatus);
    }
  /* Regulat exit */
  *pstatus = CACHE_INODE_SUCCESS;
  return *pstatus;
}
Пример #8
0
int nfs_Write(nfs_arg_t * parg,
              exportlist_t * pexport,
              fsal_op_context_t * pcontext,
              cache_inode_client_t * pclient,
              hash_table_t * ht, struct svc_req *preq, nfs_res_t * pres)
{
  static char __attribute__ ((__unused__)) funcName[] = "nfs_Write";

  cache_entry_t *pentry;
  fsal_attrib_list_t attr;
  fsal_attrib_list_t pre_attr;
  fsal_attrib_list_t *ppre_attr;
  int rc;
  cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
  cache_content_status_t content_status;
  fsal_seek_t seek_descriptor;
  fsal_size_t size = 0;
  fsal_size_t written_size;
  fsal_off_t offset = 0;
  caddr_t data = NULL;
  enum stable_how stable;       /* NFS V3 storage stability, see RFC1813 page 50 */
  cache_inode_file_type_t filetype;
  fsal_boolean_t eof_met;
  uint64_t stable_flag = FSAL_SAFE_WRITE_TO_FS;

  if(isDebug(COMPONENT_NFSPROTO))
    {
      char str[LEN_FH_STR], *stables = "";

      switch (preq->rq_vers)
        {
        case NFS_V2:
          offset = parg->arg_write2.offset;
          size = parg->arg_write2.data.nfsdata2_len;
          stables = "FILE_SYNC";
          break;
        case NFS_V3:
          offset = parg->arg_write3.offset;
          size = parg->arg_write3.count;
          switch (parg->arg_write3.stable)
            {
              case UNSTABLE:  stables = "UNSTABLE"; break;
              case DATA_SYNC: stables = "DATA_SYNC"; break;
              case FILE_SYNC: stables = "FILE_SYNC"; break;
            }
        }

      nfs_FhandleToStr(preq->rq_vers,
                       &(parg->arg_write2.file),
                       &(parg->arg_write3.file),
                       NULL,
                       str);
      LogDebug(COMPONENT_NFSPROTO,
               "REQUEST PROCESSING: Calling nfs_Write handle: %s start: %llx len: %llx %s",
               str,
               (unsigned long long) offset,
               (unsigned long long) size,
               stables);
    }

  cache_content_policy_data_t datapol;

  datapol.UseMaxCacheSize = FALSE;

  if(preq->rq_vers == NFS_V3)
    {
      /* to avoid setting it on each error case */
      pres->res_write3.WRITE3res_u.resfail.file_wcc.before.attributes_follow = FALSE;
      pres->res_write3.WRITE3res_u.resfail.file_wcc.after.attributes_follow = FALSE;
      ppre_attr = NULL;
    }

  /* Convert file handle into a cache entry */
  if((pentry = nfs_FhandleToCache(preq->rq_vers,
                                  &(parg->arg_write2.file),
                                  &(parg->arg_write3.file),
                                  NULL,
                                  &(pres->res_attr2.status),
                                  &(pres->res_write3.status),
                                  NULL, &pre_attr, pcontext, pclient, ht, &rc)) == NULL)
    {
      /* Stale NFS FH ? */
      return rc;
    }

  if((preq->rq_vers == NFS_V3) && (nfs3_Is_Fh_Xattr(&(parg->arg_write3.file))))
    return nfs3_Write_Xattr(parg, pexport, pcontext, pclient, ht, preq, pres);

  /* get directory attributes before action (for V3 reply) */
  ppre_attr = &pre_attr;

  /* Extract the filetype */
  filetype = cache_inode_fsal_type_convert(pre_attr.type);

  /* Sanity check: write only a regular file */
  if(filetype != REGULAR_FILE)
    {
      switch (preq->rq_vers)
        {
        case NFS_V2:
          /*
           * In the RFC tell it not good but it does
           * not tell what to do ... 
           * We use NFSERR_ISDIR for lack of better
           */
          pres->res_attr2.status = NFSERR_ISDIR;
          break;

        case NFS_V3:
          if(filetype == DIR_BEGINNING || filetype == DIR_CONTINUE)
            pres->res_write3.status = NFS3ERR_ISDIR;
          else
            pres->res_write3.status = NFS3ERR_INVAL;
          break;
        }
      return NFS_REQ_OK;
    }

  /* For MDONLY export, reject write operation */
  /* Request of type MDONLY_RO were rejected at the nfs_rpc_dispatcher level */
  /* This is done by replying EDQUOT (this error is known for not disturbing the client's requests cache */
  if(pexport->access_type == ACCESSTYPE_MDONLY)
    {
      switch (preq->rq_vers)
        {
        case NFS_V2:
          pres->res_attr2.status = NFSERR_DQUOT;
          break;

        case NFS_V3:
          pres->res_write3.status = NFS3ERR_DQUOT;
          break;
        }

      nfs_SetFailedStatus(pcontext, pexport,
                          preq->rq_vers,
                          cache_status,
                          &pres->res_attr2.status,
                          &pres->res_write3.status,
                          NULL, NULL,
                          pentry,
                          ppre_attr,
                          &(pres->res_write3.WRITE3res_u.resfail.file_wcc),
                          NULL, NULL, NULL);

      return NFS_REQ_OK;
    }

  /* Extract the argument from the request */
  switch (preq->rq_vers)
    {
    case NFS_V2:
      if(ppre_attr && ppre_attr->filesize > NFS2_MAX_FILESIZE)
        {
          /*
           *  V2 clients don't understand filesizes >
           *  2GB, so we don't allow them to alter
           *  them in any way. BJP 6/26/2001
           */
          pres->res_attr2.status = NFSERR_FBIG;
          return NFS_REQ_OK;
        }

      offset = parg->arg_write2.offset; /* beginoffset is obsolete */
      size = parg->arg_write2.data.nfsdata2_len;        /* totalcount is obsolete  */
      data = parg->arg_write2.data.nfsdata2_val;
      stable = FILE_SYNC;
      if (pexport->use_commit == TRUE)
        stable_flag = FSAL_SAFE_WRITE_TO_FS;
      break;

    case NFS_V3:
      offset = parg->arg_write3.offset;
      size = parg->arg_write3.count;

      if(size > parg->arg_write3.data.data_len)
        {
          /* should never happen */
          pres->res_write3.status = NFS3ERR_INVAL;
          return NFS_REQ_OK;
        }

      if((pexport->use_commit == TRUE) &&
         (pexport->use_ganesha_write_buffer == FALSE) &&
         (parg->arg_write3.stable == UNSTABLE))
        {
          stable_flag = FSAL_UNSAFE_WRITE_TO_FS_BUFFER;
        }
      else if((pexport->use_commit == TRUE) &&
              (pexport->use_ganesha_write_buffer == TRUE) &&
              (parg->arg_write3.stable == UNSTABLE))
        {
          stable_flag = FSAL_UNSAFE_WRITE_TO_GANESHA_BUFFER;
        }
      else
        {
          stable_flag = FSAL_SAFE_WRITE_TO_FS;
        }
      data = parg->arg_write3.data.data_val;
      stable = parg->arg_write3.stable;
      break;
    }

  /* 
   * do not exceed maxium WRITE offset if set 
   */
  if((pexport->options & EXPORT_OPTION_MAXOFFSETWRITE) == EXPORT_OPTION_MAXOFFSETWRITE)
    {
      LogFullDebug(COMPONENT_NFSPROTO,
                   "-----> Write offset=%llu count=%llu MaxOffSet=%llu",
                   (unsigned long long) offset,
                   (unsigned long long) size,
                   (unsigned long long) pexport->MaxOffsetWrite);

      if((fsal_off_t) (offset + size) > pexport->MaxOffsetWrite)
        {
          LogEvent(COMPONENT_NFSPROTO,
                   "NFS WRITE: A client tryed to violate max file size %llu for exportid #%hu",
                   (unsigned long long) pexport->MaxOffsetWrite, pexport->id);

          switch (preq->rq_vers)
            {
            case NFS_V2:
              pres->res_attr2.status = NFSERR_DQUOT;
              break;

            case NFS_V3:
              pres->res_write3.status = NFS3ERR_INVAL;
              break;
            }

          nfs_SetFailedStatus(pcontext, pexport,
                              preq->rq_vers,
                              cache_status,
                              &pres->res_attr2.status,
                              &pres->res_write3.status,
                              NULL, NULL,
                              pentry,
                              ppre_attr,
                              &(pres->res_write3.WRITE3res_u.resfail.file_wcc),
                              NULL, NULL, NULL);

          return NFS_REQ_OK;
        }
    }

  /*
   * We should take care not to exceed FSINFO wtmax
   * field for the size 
   */
  if(((pexport->options & EXPORT_OPTION_MAXWRITE) == EXPORT_OPTION_MAXWRITE) &&
     size > pexport->MaxWrite)
    {
      /*
       * The client asked for too much data, we
       * must restrict him 
       */
      size = pexport->MaxWrite;
    }

  if(size == 0)
    {
      cache_status = CACHE_INODE_SUCCESS;
      written_size = 0;
    }
  else
    {
      /* An actual write is to be made, prepare it */

      /* If entry is not cached, cache it now */
      datapol.UseMaxCacheSize = pexport->options & EXPORT_OPTION_MAXCACHESIZE;
      datapol.MaxCacheSize = pexport->MaxCacheSize;

      if((pexport->options & EXPORT_OPTION_USE_DATACACHE) &&
         (cache_content_cache_behaviour(pentry,
                                        &datapol,
                                        (cache_content_client_t *)
                                        pclient->pcontent_client,
                                        &content_status) == CACHE_CONTENT_FULLY_CACHED)
         && (pentry->object.file.pentry_content == NULL))
        {
          /* Entry is not in datacache, but should be in, cache it .
           * Several threads may call this function at the first time and a race condition can occur here
           * in order to avoid this, cache_inode_add_data_cache is "mutex protected" 
           * The first call will create the file content cache entry, the further will return
           * with error CACHE_INODE_CACHE_CONTENT_EXISTS which is not a pathological thing here */

          /* Status is set in last argument */
          cache_inode_add_data_cache(pentry, ht, pclient, pcontext, &cache_status);
          if((cache_status != CACHE_INODE_SUCCESS) &&
             (cache_status != CACHE_INODE_CACHE_CONTENT_EXISTS))
            {
              /* If we are here, there was an error */
              if(nfs_RetryableError(cache_status))
                {
                  return NFS_REQ_DROP;
                }

              nfs_SetFailedStatus(pcontext, pexport,
                                  preq->rq_vers,
                                  cache_status,
                                  &pres->res_attr2.status,
                                  &pres->res_write3.status,
                                  NULL, NULL,
                                  pentry,
                                  ppre_attr,
                                  &(pres->res_write3.WRITE3res_u.resfail.file_wcc),
                                  NULL, NULL, NULL);

              return NFS_REQ_OK;
            }
        }

      /* only FILE_SYNC mode is supported */
      /* Set up uio to define the transfer */
      seek_descriptor.whence = FSAL_SEEK_SET;
      seek_descriptor.offset = offset;

      if(cache_inode_rdwr(pentry,
                          CACHE_INODE_WRITE,
                          &seek_descriptor,
                          size,
                          &written_size,
                          &attr,
                          data,
                          &eof_met,
                          ht,
                          pclient,
                          pcontext, stable_flag, &cache_status) == CACHE_INODE_SUCCESS)
        {


          switch (preq->rq_vers)
            {
            case NFS_V2:
              nfs2_FSALattr_To_Fattr(pexport,
                                     &attr, &(pres->res_attr2.ATTR2res_u.attributes));

              pres->res_attr2.status = NFS_OK;
              break;

            case NFS_V3:

              /* Build Weak Cache Coherency data */
              nfs_SetWccData(pcontext,
                             pexport,
                             pentry,
                             ppre_attr,
                             &attr, &(pres->res_write3.WRITE3res_u.resok.file_wcc));

              /* Set the written size */
              pres->res_write3.WRITE3res_u.resok.count = written_size;

              /* How do we commit data ? */
              if(stable_flag == FSAL_SAFE_WRITE_TO_FS)
                {
                  pres->res_write3.WRITE3res_u.resok.committed = FILE_SYNC;
                }
              else
                {
                  pres->res_write3.WRITE3res_u.resok.committed = UNSTABLE;
                }

              /* Set the write verifier */
              memcpy(pres->res_write3.WRITE3res_u.resok.verf,
                     NFS3_write_verifier, sizeof(writeverf3));

              pres->res_write3.status = NFS3_OK;
              break;
            }

          return NFS_REQ_OK;
        }
    }

  LogFullDebug(COMPONENT_NFSPROTO,
               "---> failed write: cache_status=%d", cache_status);

  /* If we are here, there was an error */
  if(nfs_RetryableError(cache_status))
    {
      return NFS_REQ_DROP;
    }

  nfs_SetFailedStatus(pcontext, pexport,
                      preq->rq_vers,
                      cache_status,
                      &pres->res_attr2.status,
                      &pres->res_write3.status,
                      NULL, NULL,
                      pentry,
                      ppre_attr,
                      &(pres->res_write3.WRITE3res_u.resfail.file_wcc), NULL, NULL, NULL);

  return NFS_REQ_OK;
}                               /* nfs_Write.c */