diff --git a/README b/README index 5cd86167..fd2d07a0 100644 --- a/README +++ b/README @@ -34,7 +34,6 @@ TODOs: * Fragmentation is currently broken * All high level async functions (nfs_*async) should return a handle so that they can be cancelled. -* Make zero-copy work for NFS4 writes LIBNFS is a client library for accessing NFS shares over a network. diff --git a/include/nfsc/libnfs-raw.h b/include/nfsc/libnfs-raw.h index 10f7784e..e18946d4 100644 --- a/include/nfsc/libnfs-raw.h +++ b/include/nfsc/libnfs-raw.h @@ -2362,6 +2362,7 @@ rpc_nfs4_null_async(struct rpc_context *rpc, rpc_cb cb, /* * Call NFS4/COMPOUND + * * Function returns * pdu : The command was queued successfully. The callback will be invoked once * the command completes. @@ -2375,6 +2376,7 @@ rpc_nfs4_null_async(struct rpc_context *rpc, rpc_cb cb, * data is the error string. * RPC_STATUS_CANCEL : The command was cancelled. * data is NULL. + * This function can NOT be used for compounds that contain OP_READ or OP_WRITE. */ struct COMPOUND4args; EXTERN struct rpc_pdu * @@ -2397,6 +2399,7 @@ rpc_nfs4_compound_async(struct rpc_context *rpc, rpc_cb cb, * data is the error string. * RPC_STATUS_CANCEL : The command was cancelled. * data is NULL. + * This function can NOT be used for compounds that contain OP_READ or OP_WRITE. */ struct COMPOUND4args; EXTERN struct rpc_pdu * @@ -2421,12 +2424,41 @@ rpc_nfs4_compound_async2(struct rpc_context *rpc, rpc_cb cb, * data is the error string. * RPC_STATUS_CANCEL : The command was cancelled. * data is NULL. + * If the compound contains OP_READ you must use this function and not + * rpc_nfs4_compound_async() + * The OP_READ must be the last operation in the compound. */ EXTERN struct rpc_pdu * rpc_nfs4_read_async(struct rpc_context *rpc, rpc_cb cb, void *buf, size_t count, struct COMPOUND4args *args, void *private_data); + +/* + * Call NFS4/COMPOUND for write operations + * + * Function returns + * 0 : The command was queued successfully. The callback will be invoked once + * the command completes. + * <0 : An error occured when trying to queue the command. + * The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the server. + * data is COMPOUND4res *. + * RPC_STATUS_ERROR : The command failed with an error. + * data is the error string. + * RPC_STATUS_CANCEL : The command was cancelled. + * data is NULL. + * If the compound contains OP_WRITE you must use this function and not + * rpc_nfs4_compound_async() + * The OP_WRITE must be the last operation in the compound. + */ +EXTERN struct rpc_pdu * +rpc_nfs4_write_async(struct rpc_context *rpc, rpc_cb cb, + const void *buf, size_t count, + struct COMPOUND4args *args, + void *private_data); /* * Call /NULL diff --git a/lib/libnfs-win32.def b/lib/libnfs-win32.def index bcc93afb..700ba22a 100644 --- a/lib/libnfs-win32.def +++ b/lib/libnfs-win32.def @@ -215,6 +215,7 @@ rpc_nfs4_compound_async rpc_nfs4_compound_async2 rpc_nfs4_null_async rpc_nfs4_read_async +rpc_nfs4_write_async rpc_nlm4_null_async rpc_nlm4_test_async rpc_nlm4_lock_async diff --git a/lib/nfs_v4.c b/lib/nfs_v4.c index d3fa15b0..5a4e6cbf 100644 --- a/lib/nfs_v4.c +++ b/lib/nfs_v4.c @@ -3018,8 +3018,8 @@ nfs4_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, args.argarray.argarray_len = i; args.argarray.argarray_val = op; - if (rpc_nfs4_compound_async2(nfs->rpc, nfs4_pwrite_cb, &args, - data, count) == NULL) { + if (rpc_nfs4_write_async(nfs->rpc, nfs4_pwrite_cb, buf, count, + &args, data) == NULL) { nfs_set_error(nfs, "PWRITE " "failed: %s", rpc_get_error(nfs->rpc)); free_nfs4_cb_data(data); diff --git a/nfs/nfs.c b/nfs/nfs.c index f74a593e..b34183fb 100644 --- a/nfs/nfs.c +++ b/nfs/nfs.c @@ -35,8 +35,6 @@ #include "libnfs-private.h" #include "libnfs-raw-nfs.h" -uint32_t zero_padding; - char *nfsstat3_to_str(int error) { switch (error) { @@ -321,6 +319,7 @@ struct rpc_pdu *rpc_nfs3_write_async(struct rpc_context *rpc, rpc_cb cb, { struct rpc_pdu *pdu; int start; + static uint32_t zero_padding; pdu = rpc_allocate_pdu2(rpc, NFS_PROGRAM, NFS_V3, NFS3_WRITE, cb, private_data, (zdrproc_t)zdr_WRITE3res, sizeof(WRITE3res), 0); if (pdu == NULL) { diff --git a/nfs4/Makefile.am b/nfs4/Makefile.am index 8db51176..f7aa05f5 100644 --- a/nfs4/Makefile.am +++ b/nfs4/Makefile.am @@ -39,4 +39,5 @@ compile_rpc: -e "s/register int32_t \*buf;//" \ -e "s/int i;//" >> libnfs-raw-nfs4.c sed -z -i -e "s/zdr_READ4resok/zzdr_READ4resok/" libnfs-raw-nfs4.c + sed -z -i -e "s/zdr_WRITE4args/zzdr_WRITE4args/" libnfs-raw-nfs4.c cat libnfs-raw-nfs4.c.fragment >>libnfs-raw-nfs4.c diff --git a/nfs4/libnfs-raw-nfs4.c b/nfs4/libnfs-raw-nfs4.c index 9eac1039..5d47c201 100644 --- a/nfs4/libnfs-raw-nfs4.c +++ b/nfs4/libnfs-raw-nfs4.c @@ -2705,7 +2705,7 @@ zdr_stable_how4 (ZDR *zdrs, stable_how4 *objp) } uint32_t -zdr_WRITE4args (ZDR *zdrs, WRITE4args *objp) +zzdr_WRITE4args (ZDR *zdrs, WRITE4args *objp) { @@ -4595,3 +4595,14 @@ zdr_READ4resok (ZDR *zdrs, READ4resok *objp) return TRUE; } +uint32_t +zdr_WRITE4args (ZDR *zdrs, WRITE4args *objp) +{ + if (!zdr_stateid4 (zdrs, &objp->stateid)) + return FALSE; + if (!zdr_offset4 (zdrs, &objp->offset)) + return FALSE; + if (!zdr_stable_how4 (zdrs, &objp->stable)) + return FALSE; + return TRUE; +} diff --git a/nfs4/libnfs-raw-nfs4.c.fragment b/nfs4/libnfs-raw-nfs4.c.fragment index c07e1943..6893e592 100644 --- a/nfs4/libnfs-raw-nfs4.c.fragment +++ b/nfs4/libnfs-raw-nfs4.c.fragment @@ -13,3 +13,14 @@ zdr_READ4resok (ZDR *zdrs, READ4resok *objp) return TRUE; } +uint32_t +zdr_WRITE4args (ZDR *zdrs, WRITE4args *objp) +{ + if (!zdr_stateid4 (zdrs, &objp->stateid)) + return FALSE; + if (!zdr_offset4 (zdrs, &objp->offset)) + return FALSE; + if (!zdr_stable_how4 (zdrs, &objp->stable)) + return FALSE; + return TRUE; +} diff --git a/nfs4/nfs4.c b/nfs4/nfs4.c index cd530a34..4749097e 100644 --- a/nfs4/nfs4.c +++ b/nfs4/nfs4.c @@ -278,3 +278,72 @@ struct rpc_pdu *rpc_nfs4_read_async(struct rpc_context *rpc, rpc_cb cb, return pdu; } + +struct rpc_pdu *rpc_nfs4_write_async(struct rpc_context *rpc, rpc_cb cb, + const void *buf, size_t count, + struct COMPOUND4args *args, + void *private_data) +{ + struct rpc_pdu *pdu; + int start; + uint32_t len; + static uint32_t zero_padding; + + pdu = rpc_allocate_pdu2(rpc, NFS4_PROGRAM, NFS_V4, NFSPROC4_COMPOUND, + cb, private_data, (zdrproc_t)zdr_COMPOUND4res, + sizeof(COMPOUND4res), 0); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for " + "NFS4/COMPOUND call"); + return NULL; + } + + start = zdr_getpos(&pdu->zdr); + + if (zdr_COMPOUND4args(&pdu->zdr, args) == 0) { + rpc_set_error(rpc, "ZDR error: Failed to encode COMPOUND4args"); + rpc_free_pdu(rpc, pdu); + return NULL; + } + + /* Add an iovector for the COMPOUND4/.../WRITE4 header */ + if (rpc_add_iovector(rpc, &pdu->out, &pdu->outdata.data[start + 4], + zdr_getpos(&pdu->zdr) - start, NULL) < 0) { + rpc_free_pdu(rpc, pdu); + return NULL; + } + + /* Add an iovector for the length of the byte/array blob */ + start = zdr_getpos(&pdu->zdr); + len = count; + zdr_u_int(&pdu->zdr, &len); + if (rpc_add_iovector(rpc, &pdu->out, &pdu->outdata.data[start + 4], + 4, NULL) < 0) { + rpc_free_pdu(rpc, pdu); + return NULL; + } + + /* Add an iovector for the data itself */ + if (rpc_add_iovector(rpc, &pdu->out, buf, count, NULL) < 0) { + rpc_free_pdu(rpc, pdu); + return NULL; + } + + /* We may need to pad this to 4 byte boundary */ + if (count & 0x03) { + if (rpc_add_iovector(rpc, &pdu->out, (char *)&zero_padding, + 4 - count & 0x03, + NULL) < 0) { + rpc_free_pdu(rpc, pdu); + return NULL; + } + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for " + "NFS4/COMPOUND4 call"); + return NULL; + } + + return pdu; +}