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 */
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 */
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 */
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; }
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 ; }
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; }
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; }
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 */