+
+ 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->count);
+ if (data->buffer == NULL) {
+ rpc_set_error(nfs->rpc, "Out-Of-Memory: Failed to allocate reassembly buffer for %d bytes", (int)data->count);
+ 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->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) {
+ if (data->max_offset > data->org_offset + data->org_count) {
+ data->max_offset = data->org_offset + data->org_count;
+ }
+ data->cb(data->max_offset - data->org_offset, nfs, data->buffer + (data->org_offset - data->offset), data->private_data);
+ } else {
+ data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data);
+ }
+
+ data->nfsfh->ra.fh_offset = data->max_offset;
+ if (data->nfsfh->ra.cur_ra) {
+ free(data->nfsfh->ra.buf);
+ data->nfsfh->ra.buf = data->buffer;
+ data->nfsfh->ra.buf_offset = data->offset;
+ data->nfsfh->ra.buf_count = data->count;
+ data->nfsfh->ra.buf_ts = time(NULL);
+ data->buffer = NULL;
+ }
+ free_nfs_cb_data(data);
+}
+
+static void nfs_ra_invalidate(struct nfsfh *nfsfh) {
+ free(nfsfh->ra.buf);
+ nfsfh->ra.buf = NULL;
+ nfsfh->ra.buf_offset = 0;
+ nfsfh->ra.buf_count = 0;
+ nfsfh->ra.buf_ts = time(NULL);
+ nfsfh->ra.cur_ra = NFS_BLKSIZE;
+}
+
+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->org_offset = offset;
+ data->org_count = count;
+
+ assert(data->num_calls == 0);
+
+ if (nfs->rpc->readahead && time(NULL) - nfsfh->ra.buf_ts > NFS_RA_TIMEOUT) {
+ /* readahead cache timeout */
+ nfs_ra_invalidate(nfsfh);
+ }
+
+ if (nfs->rpc->readahead) {
+ if (offset >= nfsfh->ra.last_offset &&
+ offset - NFS_BLKSIZE <= nfsfh->ra.fh_offset + nfsfh->ra.cur_ra) {
+ if (nfs->rpc->readahead > nfsfh->ra.cur_ra) {
+ nfsfh->ra.cur_ra <<= 1;
+ }
+ } else {
+ nfsfh->ra.cur_ra = NFS_BLKSIZE;
+ }
+
+ nfsfh->ra.last_offset = offset;
+
+ if (nfsfh->ra.buf_offset <= offset &&
+ nfsfh->ra.buf_offset + nfsfh->ra.buf_count >= offset + count) {
+ /* serve request completely from cache */
+ data->buffer = malloc(count);
+ if (data->buffer == NULL) {
+ free_nfs_cb_data(data);
+ return -ENOMEM;
+ }
+ memcpy(data->buffer, nfsfh->ra.buf + (offset - nfsfh->ra.buf_offset), count);
+ data->cb(count, nfs, data->buffer, data->private_data);
+ nfsfh->ra.fh_offset = offset + count;
+ free_nfs_cb_data(data);
+ return 0;
+ }
+
+ /* align start offset to blocksize */
+ count += offset & (NFS_BLKSIZE - 1);
+ offset &= ~(NFS_BLKSIZE - 1);
+
+ /* align end offset to blocksize and add readahead */
+ count += nfsfh->ra.cur_ra - 1;
+ count &= ~(NFS_BLKSIZE - 1);
+
+ data->buffer = malloc(count);
+ if (data->buffer == NULL) {
+ free_nfs_cb_data(data);
+ return -ENOMEM;
+ }
+ data->offset = offset;
+ data->count = count;
+
+ if (nfsfh->ra.buf_count && nfsfh->ra.buf_offset <= offset &&
+ nfsfh->ra.buf_offset + nfsfh->ra.buf_count >= offset) {
+ /* serve request partially from cache */
+ size_t overlap = (nfsfh->ra.buf_offset + nfsfh->ra.buf_count) - offset;
+ if (overlap > count) count = overlap;
+ memcpy(data->buffer, nfsfh->ra.buf + (offset - nfsfh->ra.buf_offset), overlap);
+ offset += overlap;
+ count -= overlap;
+ }
+ } else {
+ data->offset = offset;
+ data->count = count;
+ }
+
+ data->max_offset = offset;
+
+ /* chop requests into chunks of at most READMAX bytes if necessary.
+ * we send all reads in parallel so that performance is still good.
+ */
+ 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);
+
+ if (rpc_nfs3_read_async(nfs->rpc, nfs_pread_mcb, &args, mdata) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path);
+ free(mdata);
+ if (data->num_calls == 0) {
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ data->oom = 1;
+ break;
+ }
+
+ count -= readcount;
+ offset += readcount;
+ data->num_calls++;
+ } while (count > 0);
+
+ return 0;
+}
+
+int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_data)
+{
+ return nfs_pread_async_internal(nfs, nfsfh, offset, count, cb, private_data, 0);
+}
+
+/*
+ * Async read()
+ */
+int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, nfs_cb cb, void *private_data)
+{
+ return nfs_pread_async_internal(nfs, nfsfh, nfsfh->offset, count, cb, private_data, 1);
+}
+
+
+
+/*
+ * Async pwrite()
+ */
+static void nfs_fill_WRITE3args (WRITE3args *args, struct nfsfh *fh, uint64_t offset, uint64_t count,
+ void *buf)
+{
+ memset(args, 0, sizeof(WRITE3args));
+ args->file = fh->fh;
+ args->offset = offset;
+ args->count = count;
+ args->stable = fh->is_sync ? FILE_SYNC : UNSTABLE;
+ args->data.data_len = count;
+ args->data.data_val = buf;
+}
+
+static void nfs_pwrite_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;
+ WRITE3res *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: 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);
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: GETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ if (nfs_pwrite_async_internal(nfs, data->nfsfh, res->GETATTR3res_u.resok.obj_attributes.size, data->count, data->usrbuf, data->cb, data->private_data, 1) != 0) {
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ free_nfs_cb_data(data);
+}
+
+int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, char *buf, nfs_cb cb, void *private_data)
+{
+ nfs_ra_invalidate(nfsfh);
+ if (nfsfh->is_append) {
+ struct GETATTR3args args;
+ 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;
+ data->count = count;
+
+ memset(&args, 0, sizeof(GETATTR3args));
+ args.object = nfsfh->fh;
+
+ if (rpc_nfs3_getattr_async(nfs->rpc, nfs_write_append_cb, &args, data) != 0) {
+ rpc_set_error(nfs->rpc, "out of memory: failed to send GETATTR");
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+ }
+ return nfs_pwrite_async_internal(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data, 1);
+}
+
+
+
+
+/*
+ * close
+ */
+
+int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
+{
+ free_nfsfh(nfsfh);
+ cb(0, nfs, NULL, private_data);
+ return 0;
+};
+
+
+
+
+
+/*
+ * Async fstat()
+ */
+int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
+{
+ struct nfs_cb_data *data;
+ struct GETATTR3args args;
+
+ 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;
+
+ memset(&args, 0, sizeof(GETATTR3args));
+ args.object = nfsfh->fh;
+
+ if (rpc_nfs3_getattr_async(nfs->rpc, nfs_stat_1_cb, &args, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR 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;
+}
+
+/*
+ * Async fstat64()
+ */
+int nfs_fstat64_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
+{
+ struct nfs_cb_data *data;
+ struct GETATTR3args args;
+
+ 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;
+
+ memset(&args, 0, sizeof(GETATTR3args));
+ args.object = nfsfh->fh;
+
+ if (rpc_nfs3_getattr_async(nfs->rpc, nfs_stat64_1_cb, &args, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }