+ data->cb(0, nfs, nfsfh, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_open_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data)
+{
+ int nfsmode = 0;
+ ACCESS3args args;
+
+ if (data->continue_int & O_WRONLY) {
+ nfsmode |= ACCESS3_MODIFY;
+ }
+ if (data->continue_int & O_RDWR) {
+ nfsmode |= ACCESS3_READ|ACCESS3_MODIFY;
+ }
+ if (!(data->continue_int & (O_WRONLY|O_RDWR))) {
+ nfsmode |= ACCESS3_READ;
+ }
+
+ memset(&args, 0, sizeof(ACCESS3args));
+ args.object = data->fh;
+ args.access = nfsmode;
+
+ if (rpc_nfs3_access_async(nfs->rpc, nfs_open_cb, &args, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS "
+ "call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc),
+ data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_open_async(struct nfs_context *nfs, const char *path, int flags, nfs_cb cb, void *private_data)
+{
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_open_continue_internal, NULL, NULL, flags) != 0) {
+ rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Async chdir()
+ */
+static int nfs_chdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data)
+{
+ /* steal saved_path */
+ free(nfs->cwd);
+ nfs->cwd = data->saved_path;
+ data->saved_path = NULL;
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+
+ return 0;
+}
+
+int nfs_chdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chdir_continue_internal, NULL, NULL, 0) != 0) {
+ rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Async pread()
+ */
+static void nfs_fill_READ3args(READ3args *args, struct nfsfh *fh, uint64_t offset, uint64_t count)
+{
+ memset(args, 0, sizeof(READ3args));
+ args->file = fh->fh;
+ args->offset = offset;
+ args->count = count;
+}
+
+static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+ struct nfs_mcb_data *mdata = private_data;
+ struct nfs_cb_data *data = mdata->data;
+ struct nfs_context *nfs = data->nfs;
+ READ3res *res;
+
+ 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: Read failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->error = 1;
+ } else {
+ uint64_t count = res->READ3res_u.resok.count;
+
+ if (mdata->update_pos)
+ data->nfsfh->offset += count;
+
+ /* if we have more than one call or we have received a short read we need a reassembly buffer */
+ if (data->num_calls || (count < mdata->count && !res->READ3res_u.resok.eof)) {
+ if (data->buffer == NULL) {
+ data->buffer = malloc(data->request_size);
+ if (data->buffer == NULL) {
+ rpc_set_error(nfs->rpc, "Out-Of-Memory: Failed to allocate reassembly buffer for %d bytes", (int)data->request_size);
+ data->oom = 1;
+ }
+ }
+ }
+ if (count > 0) {
+ if (count <= mdata->count) {
+ /* copy data into reassembly buffer if we have one */
+ if (data->buffer != NULL) {
+ memcpy(&data->buffer[mdata->offset - data->start_offset], res->READ3res_u.resok.data.data_val, count);
+ }
+ if (data->max_offset < mdata->offset + count) {
+ data->max_offset = mdata->offset + count;
+ }
+ } else {
+ rpc_set_error(nfs->rpc, "NFS: Read overflow. Server has sent more data than requested!");
+ data->error = 1;
+ }
+ }
+ /* check if we have received a short read */
+ if (count < mdata->count && !res->READ3res_u.resok.eof) {
+ if (count == 0) {
+ rpc_set_error(nfs->rpc, "NFS: Read failed. No bytes read and not at EOF!");
+ data->error = 1;
+ } else {
+ /* reissue reminder of this read request */
+ READ3args args;
+ mdata->offset += count;
+ mdata->count -= count;
+ nfs_fill_READ3args(&args, data->nfsfh, mdata->offset, mdata->count);
+ if (rpc_nfs3_read_async(nfs->rpc, nfs_pread_mcb, &args, mdata) == 0) {
+ data->num_calls++;
+ return;
+ } else {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path);
+ data->oom = 1;
+ }
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+
+ if (data->buffer) {
+ data->cb(data->max_offset - data->start_offset, nfs, data->buffer, data->private_data);
+ } else {
+ data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data);
+ }
+
+ free_nfs_cb_data(data);
+}
+
+static int nfs_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, 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->request_size = count;
+
+ assert(data->num_calls == 0);
+
+ /* chop requests into chunks of at most READMAX bytes if necessary.
+ * we send all reads in parallel so that performance is still good.
+ */
+ data->max_offset = offset;
+ data->start_offset = offset;
+
+ do {
+ uint64_t readcount = count;
+ struct nfs_mcb_data *mdata;
+ READ3args args;
+
+ if (readcount > nfs_get_readmax(nfs)) {
+ readcount = nfs_get_readmax(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 = readcount;
+ mdata->update_pos = update_pos;
+
+ nfs_fill_READ3args(&args, nfsfh, offset, readcount);