+ assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+ data->num_calls--;
+
+ if (status == RPC_STATUS_ERROR) {
+ /* flag the failure but do not invoke callback until we have received all responses */
+ data->error = 1;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ /* flag the cancellation but do not invoke callback until we have received all responses */
+ data->cancel = 1;
+ }
+
+ if (status == RPC_STATUS_SUCCESS) {
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: Write failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->error = 1;
+ } else {
+ uint64_t count = res->WRITE3res_u.resok.count;
+
+ if (mdata->update_pos)
+ data->nfsfh->offset += count;
+
+ if (count < mdata->count) {
+ if (count == 0) {
+ rpc_set_error(nfs->rpc, "NFS: Write failed. No bytes written!");
+ data->error = 1;
+ } else {
+ /* reissue reminder of this write request */
+ WRITE3args args;
+ mdata->offset += count;
+ mdata->count -= count;
+
+ nfs_fill_WRITE3args(&args, data->nfsfh, mdata->offset, mdata->count,
+ &data->usrbuf[mdata->offset - data->offset]);
+ if (rpc_nfs3_write_async(nfs->rpc, nfs_pwrite_mcb, &args, mdata) == 0) {
+ data->num_calls++;
+ return;
+ } else {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE call for %s", data->path);
+ data->oom = 1;
+ }
+ }
+ }
+ if (count > 0) {
+ if (data->max_offset < mdata->offset + count) {
+ data->max_offset = mdata->offset + count;
+ }
+ }
+ }
+ }
+
+ free(mdata);
+
+ if (data->num_calls > 0) {
+ /* still waiting for more replies */
+ return;
+ }
+ if (data->oom != 0) {
+ data->cb(-ENOMEM, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (data->error != 0) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (data->cancel != 0) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(data->max_offset - data->offset, nfs, NULL, data->private_data);
+
+ free_nfs_cb_data(data);
+}
+
+
+static int nfs_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data, int update_pos)
+{
+ struct nfs_cb_data *data;
+
+ data = malloc(sizeof(struct nfs_cb_data));
+ if (data == NULL) {
+ rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
+ return -1;
+ }
+ memset(data, 0, sizeof(struct nfs_cb_data));
+ data->nfs = nfs;
+ data->cb = cb;
+ data->private_data = private_data;
+ data->nfsfh = nfsfh;
+ data->usrbuf = buf;
+
+ /* hello, clang-analyzer */
+ assert(data->num_calls == 0);
+
+ /* chop requests into chunks of at most WRITEMAX bytes if necessary.
+ * we send all writes in parallel so that performance is still good.
+ */
+ data->max_offset = offset;
+ data->offset = offset;
+
+ do {
+ uint64_t writecount = count;
+ struct nfs_mcb_data *mdata;
+ WRITE3args args;
+
+ if (writecount > nfs_get_writemax(nfs)) {
+ writecount = nfs_get_writemax(nfs);
+ }
+
+ mdata = malloc(sizeof(struct nfs_mcb_data));
+ if (mdata == NULL) {
+ rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_mcb_data structure");
+ if (data->num_calls == 0) {
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ data->oom = 1;
+ break;
+ }
+ memset(mdata, 0, sizeof(struct nfs_mcb_data));
+ mdata->data = data;
+ mdata->offset = offset;
+ mdata->count = writecount;
+ mdata->update_pos = update_pos;
+
+ nfs_fill_WRITE3args(&args, nfsfh, offset, writecount, &buf[offset - data->offset]);
+
+ if (rpc_nfs3_write_async(nfs->rpc, nfs_pwrite_mcb, &args, mdata) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE call for %s", data->path);
+ free(mdata);
+ if (data->num_calls == 0) {
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ data->oom = 1;
+ break;
+ }
+
+ count -= writecount;
+ offset += writecount;
+ data->num_calls++;
+ } while (count > 0);
+
+ return 0;
+}
+
+int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data)
+{
+ return nfs_pwrite_async_internal(nfs, nfsfh, offset, count, buf, cb, private_data, 0);
+}
+
+/*
+ * Async write()
+ */
+static void nfs_write_append_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ GETATTR3res *res;
+
+ assert(rpc->magic == RPC_CONTEXT_MAGIC);
+