X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=lib%2Flibnfs.c;h=11aade1e8e4f8fa98f8504a57e62ea04814d7fe1;hb=037a1061ddfa9ee5192daa4485794bee7657aa31;hp=f8b294a5589ebcffba9a0b8d9e7f345ab8788469;hpb=c5e346e15c043a6253cc5a838dd9c77febf624aa;p=deb_libnfs.git diff --git a/lib/libnfs.c b/lib/libnfs.c index f8b294a..11aade1 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -69,21 +69,41 @@ #include #include #include "libnfs-zdr.h" +#include "slist.h" #include "libnfs.h" #include "libnfs-raw.h" #include "libnfs-raw-mount.h" #include "libnfs-raw-nfs.h" +#include "libnfs-raw-portmap.h" #include "libnfs-private.h" +#define MAX_DIR_CACHE 128 + struct nfsdir { + struct nfs_fh3 fh; + fattr3 attr; + struct nfsdir *next; + struct nfsdirent *entries; struct nfsdirent *current; }; +struct nfs_readahead { + uint64_t fh_offset; + uint64_t last_offset; + uint64_t buf_offset; + uint64_t buf_count; + time_t buf_ts; + void *buf; + uint32_t cur_ra; +}; + struct nfsfh { struct nfs_fh3 fh; int is_sync; + int is_append; uint64_t offset; + struct nfs_readahead ra; }; struct nfs_context { @@ -93,6 +113,8 @@ struct nfs_context { struct nfs_fh3 rootfh; uint64_t readmax; uint64_t writemax; + char *cwd; + struct nfsdir *dircache; }; void nfs_free_nfsdir(struct nfsdir *nfsdir) @@ -105,11 +127,42 @@ void nfs_free_nfsdir(struct nfsdir *nfsdir) free(nfsdir->entries); nfsdir->entries = dirent; } + free(nfsdir->fh.data.data_val); free(nfsdir); } +static void nfs_dircache_add(struct nfs_context *nfs, struct nfsdir *nfsdir) +{ + int i; + LIBNFS_LIST_ADD(&nfs->dircache, nfsdir); + + for (nfsdir = nfs->dircache, i = 0; nfsdir; nfsdir = nfsdir->next, i++) { + if (i > MAX_DIR_CACHE) { + LIBNFS_LIST_REMOVE(&nfs->dircache, nfsdir); + nfs_free_nfsdir(nfsdir); + break; + } + } +} + +static struct nfsdir *nfs_dircache_find(struct nfs_context *nfs, struct nfs_fh3 *fh) +{ + struct nfsdir *nfsdir; + + for (nfsdir = nfs->dircache; nfsdir; nfsdir = nfsdir->next) { + if (nfsdir->fh.data.data_len == fh->data.data_len && + !memcmp(nfsdir->fh.data.data_val, fh->data.data_val, fh->data.data_len)) { + LIBNFS_LIST_REMOVE(&nfs->dircache, nfsdir); + return nfsdir; + } + } + + return NULL; +} + struct nfs_cb_data; -typedef int (*continue_func)(struct nfs_context *nfs, struct nfs_cb_data *data); +typedef int (*continue_func)(struct nfs_context *nfs, fattr3 *attr, + struct nfs_cb_data *data); struct nfs_cb_data { struct nfs_context *nfs; @@ -129,19 +182,21 @@ struct nfs_cb_data { /* for multi-read/write calls. */ int error; int cancel; + int oom; int num_calls; - uint64_t start_offset, max_offset; + uint64_t offset, count, max_offset, org_offset, org_count; char *buffer; + char *usrbuf; }; struct nfs_mcb_data { struct nfs_cb_data *data; uint64_t offset; uint64_t count; + int update_pos; }; -static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh); - +static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr, struct nfs_cb_data *data, struct nfs_fh3 *fh); void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth) { @@ -173,6 +228,161 @@ char *nfs_get_error(struct nfs_context *nfs) return rpc_get_error(nfs->rpc); }; +static int nfs_set_context_args(struct nfs_context *nfs, char *arg, char *val) +{ + if (!strcmp(arg, "tcp-syncnt")) { + rpc_set_tcp_syncnt(nfs_get_rpc_context(nfs), atoi(val)); + } else if (!strcmp(arg, "uid")) { + rpc_set_uid(nfs_get_rpc_context(nfs), atoi(val)); + } else if (!strcmp(arg, "gid")) { + rpc_set_gid(nfs_get_rpc_context(nfs), atoi(val)); + } else if (!strcmp(arg, "readahaed")) { + rpc_set_readahead(nfs_get_rpc_context(nfs), atoi(val)); + } + return 0; +} + +static struct nfs_url *nfs_parse_url(struct nfs_context *nfs, const char *url, int dir, int incomplete) +{ + struct nfs_url *urls; + char *strp, *flagsp, *strp2; + + if (strncmp(url, "nfs://", 6)) { + rpc_set_error(nfs->rpc, "Invalid URL specified"); + return NULL; + } + + urls = malloc(sizeof(struct nfs_url)); + if (urls == NULL) { + rpc_set_error(nfs->rpc, "Out of memory"); + return NULL; + } + + memset(urls, 0x00, sizeof(struct nfs_url)); + urls->server = strdup(url + 6); + if (urls->server == NULL) { + nfs_destroy_url(urls); + rpc_set_error(nfs->rpc, "Out of memory"); + return NULL; + } + + if (urls->server[0] == '/' || urls->server[0] == '\0' || + urls->server[0] == '?') { + if (incomplete) { + flagsp = strchr(urls->server, '?'); + goto flags; + } + nfs_destroy_url(urls); + rpc_set_error(nfs->rpc, "Invalid server string"); + return NULL; + } + + strp = strchr(urls->server, '/'); + if (strp == NULL) { + if (incomplete) { + flagsp = strchr(urls->server, '?'); + goto flags; + } + nfs_destroy_url(urls); + rpc_set_error(nfs->rpc, "Incomplete or invalid URL specified."); + return NULL; + } + + urls->path = strdup(strp); + if (urls->path == NULL) { + nfs_destroy_url(urls); + rpc_set_error(nfs->rpc, "Out of memory"); + return NULL; + } + *strp = 0; + + if (dir) { + flagsp = strchr(urls->path, '?'); + goto flags; + } + + strp = strrchr(urls->path, '/'); + if (strp == NULL) { + if (incomplete) { + flagsp = strchr(urls->path, '?'); + goto flags; + } + nfs_destroy_url(urls); + rpc_set_error(nfs->rpc, "Incomplete or invalid URL specified."); + return NULL; + } + urls->file = strdup(strp); + if (urls->path == NULL) { + nfs_destroy_url(urls); + rpc_set_error(nfs->rpc, "Out of memory"); + return NULL; + } + *strp = 0; + flagsp = strchr(urls->file, '?'); + +flags: + if (flagsp) { + *flagsp = 0; + } + + if (urls->file && !strlen(urls->file)) { + free(urls->file); + urls->file = NULL; + if (!incomplete) { + nfs_destroy_url(urls); + rpc_set_error(nfs->rpc, "Incomplete or invalid URL specified."); + return NULL; + } + } + + while (flagsp != NULL && *(flagsp+1) != 0) { + strp = flagsp + 1; + flagsp = strchr(strp, '&'); + if (flagsp) { + *flagsp = 0; + } + strp2 = strchr(strp, '='); + if (strp2) { + *strp2 = 0; + strp2++; + nfs_set_context_args(nfs, strp, strp2); + } + } + + if (urls->server && strlen(urls->server) <= 1) { + free(urls->server); + urls->server = NULL; + } + + return urls; +} + +struct nfs_url *nfs_parse_url_full(struct nfs_context *nfs, const char *url) +{ + return nfs_parse_url(nfs, url, 0, 0); +} + +struct nfs_url *nfs_parse_url_dir(struct nfs_context *nfs, const char *url) +{ + return nfs_parse_url(nfs, url, 1, 0); +} + +struct nfs_url *nfs_parse_url_incomplete(struct nfs_context *nfs, const char *url) +{ + return nfs_parse_url(nfs, url, 0, 1); +} + + +void nfs_destroy_url(struct nfs_url *url) +{ + if (url) { + free(url->server); + free(url->path); + free(url->file); + } + free(url); +} + struct nfs_context *nfs_init_context(void) { struct nfs_context *nfs; @@ -181,17 +391,15 @@ struct nfs_context *nfs_init_context(void) if (nfs == NULL) { return NULL; } + memset(nfs, 0, sizeof(struct nfs_context)); + nfs->rpc = rpc_init_context(); if (nfs->rpc == NULL) { free(nfs); return NULL; } - nfs->server = NULL; - nfs->export = NULL; - - nfs->rootfh.data.data_len = 0; - nfs->rootfh.data.data_val = NULL; + nfs->cwd = strdup("/"); return nfs; } @@ -211,198 +419,285 @@ void nfs_destroy_context(struct nfs_context *nfs) nfs->export = NULL; } + if (nfs->cwd) { + free(nfs->cwd); + nfs->cwd = NULL; + } + if (nfs->rootfh.data.data_val != NULL) { free(nfs->rootfh.data.data_val); nfs->rootfh.data.data_val = NULL; } - free(nfs); -} - -void free_nfs_cb_data(struct nfs_cb_data *data) -{ - if (data->saved_path != NULL) { - free(data->saved_path); - data->saved_path = NULL; + while (nfs->dircache) { + struct nfsdir *nfsdir = nfs->dircache; + LIBNFS_LIST_REMOVE(&nfs->dircache, nfsdir); + nfs_free_nfsdir(nfsdir); } - if (data->continue_data != NULL) { - data->free_continue_data(data->continue_data); - data->continue_data = NULL; - } + free(nfs); +} - if (data->fh.data.data_val != NULL) { - free(data->fh.data.data_val); - data->fh.data.data_val = NULL; - } +struct rpc_cb_data { + char *server; + uint32_t program; + uint32_t version; - if (data->buffer != NULL) { - free(data->buffer); - data->buffer = NULL; - } + rpc_cb cb; + void *private_data; +}; +void free_rpc_cb_data(struct rpc_cb_data *data) +{ + free(data->server); + data->server = NULL; free(data); } - - - - -static void nfs_mount_10_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void rpc_connect_program_5_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; + struct rpc_cb_data *data = private_data; assert(rpc->magic == RPC_CONTEXT_MAGIC); + /* Dont want any more callbacks even if the socket is closed */ + rpc->connect_cb = NULL; + if (status == RPC_STATUS_ERROR) { - data->cb(-EFAULT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); return; } if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); return; } - data->cb(0, nfs, NULL, data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, NULL, data->private_data); + free_rpc_cb_data(data); } -static void nfs_mount_9_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void rpc_connect_program_4_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; - FSINFO3res *res = command_data; + struct rpc_cb_data *data = private_data; assert(rpc->magic == RPC_CONTEXT_MAGIC); + /* Dont want any more callbacks even if the socket is closed */ + rpc->connect_cb = NULL; + if (status == RPC_STATUS_ERROR) { - data->cb(-EFAULT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); return; } if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); return; } - nfs->readmax = res->FSINFO3res_u.resok.rtmax; - nfs->writemax = res->FSINFO3res_u.resok.wtmax; - - if (rpc_nfs_getattr_async(rpc, nfs_mount_10_cb, &nfs->rootfh, data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); + switch (data->program) { + case MOUNT_PROGRAM: + if (rpc_mount3_null_async(rpc, rpc_connect_program_5_cb, + data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + return; + case NFS_PROGRAM: + if (rpc_nfs3_null_async(rpc, rpc_connect_program_5_cb, + data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } return; } + + data->cb(rpc, status, NULL, data->private_data); + free_rpc_cb_data(data); } -static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void rpc_connect_program_3_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; + struct rpc_cb_data *data = private_data; + struct pmap3_string_result *gar; + uint32_t rpc_port = 0; + unsigned char *ptr; 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); + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); return; } if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); return; } - if (rpc_nfs_fsinfo_async(rpc, nfs_mount_9_cb, &nfs->rootfh, data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); + switch (rpc->s.ss_family) { + case AF_INET: + rpc_port = *(uint32_t *)command_data; + break; + case AF_INET6: + /* ouch. portmapper and ipv6 are not great */ + gar = command_data; + if (gar->addr == NULL) { + break; + } + ptr = strrchr(gar->addr, '.'); + if (ptr == NULL) { + break; + } + rpc_port = atoi(ptr + 1); + *ptr = 0; + ptr = strrchr(gar->addr, '.'); + if (ptr == NULL) { + break; + } + rpc_port += 256 * atoi(ptr + 1); + break; + } + if (rpc_port == 0) { + rpc_set_error(rpc, "RPC error. Program is not available on %s", data->server); + data->cb(rpc, RPC_STATUS_ERROR, rpc_get_error(rpc), data->private_data); + free_rpc_cb_data(data); return; } -} + rpc_disconnect(rpc, "normal disconnect"); + if (rpc_connect_async(rpc, data->server, rpc_port, rpc_connect_program_4_cb, data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } +} -static void nfs_mount_7_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void rpc_connect_program_2_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; + struct rpc_cb_data *data = private_data; + struct pmap3_mapping map; assert(rpc->magic == RPC_CONTEXT_MAGIC); - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - if (status == RPC_STATUS_ERROR) { - data->cb(-EFAULT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); return; } if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); return; } - if (rpc_nfs_null_async(rpc, nfs_mount_8_cb, data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; + switch (rpc->s.ss_family) { + case AF_INET: + if (rpc_pmap2_getport_async(rpc, data->program, data->version, IPPROTO_TCP, rpc_connect_program_3_cb, private_data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; + case AF_INET6: + map.prog=data->program; + map.vers=data->version; + map.netid=""; + map.addr=""; + map.owner=""; + if (rpc_pmap3_getaddr_async(rpc, &map, rpc_connect_program_3_cb, private_data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; } } - -static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void rpc_connect_program_1_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; - mountres3 *res; + struct rpc_cb_data *data = private_data; assert(rpc->magic == RPC_CONTEXT_MAGIC); + /* Dont want any more callbacks even if the socket is closed */ + rpc->connect_cb = NULL; + if (status == RPC_STATUS_ERROR) { - data->cb(-EFAULT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); return; } if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); return; } - res = command_data; - if (res->fhs_status != MNT3_OK) { - rpc_set_error(rpc, "RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status)); - data->cb(mountstat3_to_errno(res->fhs_status), nfs, rpc_get_error(rpc), data->private_data); - free_nfs_cb_data(data); - return; + switch (rpc->s.ss_family) { + case AF_INET: + if (rpc_pmap2_null_async(rpc, rpc_connect_program_2_cb, data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; + case AF_INET6: + if (rpc_pmap3_null_async(rpc, rpc_connect_program_2_cb, data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; } +} - nfs->rootfh.data.data_len = res->mountres3_u.mountinfo.fhandle.fhandle3_len; - nfs->rootfh.data.data_val = malloc(nfs->rootfh.data.data_len); - if (nfs->rootfh.data.data_val == NULL) { - rpc_set_error(rpc, "Out of memory. Could not allocate memory to store root filehandle"); - data->cb(-ENOMEM, nfs, rpc_get_error(rpc), data->private_data); - free_nfs_cb_data(data); - return; +int rpc_connect_program_async(struct rpc_context *rpc, const char *server, int program, int version, rpc_cb cb, void *private_data) +{ + struct rpc_cb_data *data; + + data = malloc(sizeof(struct rpc_cb_data)); + if (data == NULL) { + return -1; } - memcpy(nfs->rootfh.data.data_val, res->mountres3_u.mountinfo.fhandle.fhandle3_val, nfs->rootfh.data.data_len); + memset(data, 0, sizeof(struct rpc_cb_data)); + data->server = strdup(server); + data->program = program; + data->version = version; - rpc_disconnect(rpc, "normal disconnect"); - if (rpc_connect_async(rpc, nfs->server, 2049, nfs_mount_7_cb, data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; + data->cb = cb; + data->private_data = private_data; + + if (rpc_connect_async(rpc, server, 111, rpc_connect_program_1_cb, data) != 0) { + rpc_set_error(rpc, "Failed to start connection"); + free_rpc_cb_data(data); + return -1; } - /* NFS TCP connections we want to autoreconnect after sessions are torn down (due to inactivity or error) */ - rpc_set_autoreconnect(rpc); + return 0; } +static void free_nfs_cb_data(struct nfs_cb_data *data) +{ + if (data->continue_data != NULL) { + assert(data->free_continue_data); + data->free_continue_data(data->continue_data); + } -static void nfs_mount_5_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) + free(data->saved_path); + free(data->fh.data.data_val); + free(data->buffer); + + free(data); +} + + +static void nfs_mount_10_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; @@ -420,23 +715,19 @@ static void nfs_mount_5_cb(struct rpc_context *rpc, int status, void *command_da return; } - if (rpc_mount_mnt_async(rpc, nfs_mount_6_cb, nfs->export, data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); } -static void nfs_mount_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void nfs_mount_9_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; + FSINFO3res *res = command_data; + struct GETATTR3args args; assert(rpc->magic == RPC_CONTEXT_MAGIC); - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - if (status == RPC_STATUS_ERROR) { data->cb(-EFAULT, nfs, command_data, data->private_data); free_nfs_cb_data(data); @@ -448,22 +739,28 @@ static void nfs_mount_4_cb(struct rpc_context *rpc, int status, void *command_da return; } - if (rpc_mount_null_async(rpc, nfs_mount_5_cb, data) != 0) { + nfs->readmax = res->FSINFO3res_u.resok.rtmax; + nfs->writemax = res->FSINFO3res_u.resok.wtmax; + + memset(&args, 0, sizeof(GETATTR3args)); + args.object = nfs->rootfh; + + if (rpc_nfs3_getattr_async(rpc, nfs_mount_10_cb, &args, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; } } -static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void nfs_mount_8_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; - uint32_t mount_port; + struct FSINFO3args args; assert(rpc->magic == RPC_CONTEXT_MAGIC); - if (status == RPC_STATUS_ERROR) { + if (status == RPC_STATUS_ERROR) { data->cb(-EFAULT, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; @@ -474,27 +771,19 @@ static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_da return; } - mount_port = *(uint32_t *)command_data; - if (mount_port == 0) { - rpc_set_error(rpc, "RPC error. Mount program is not available on %s", nfs->server); - data->cb(-ENOENT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } - - rpc_disconnect(rpc, "normal disconnect"); - if (rpc_connect_async(rpc, nfs->server, mount_port, nfs_mount_4_cb, data) != 0) { + args.fsroot = nfs->rootfh; + if (rpc_nfs3_fsinfo_async(rpc, nfs_mount_9_cb, &args, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; } } - -static void nfs_mount_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void nfs_mount_6_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; + mountres3 *res; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -509,23 +798,44 @@ static void nfs_mount_2_cb(struct rpc_context *rpc, int status, void *command_da return; } - if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, IPPROTO_TCP, nfs_mount_3_cb, private_data) != 0) { + res = command_data; + if (res->fhs_status != MNT3_OK) { + rpc_set_error(rpc, "RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status)); + data->cb(mountstat3_to_errno(res->fhs_status), nfs, rpc_get_error(rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + nfs->rootfh.data.data_len = res->mountres3_u.mountinfo.fhandle.fhandle3_len; + nfs->rootfh.data.data_val = malloc(nfs->rootfh.data.data_len); + if (nfs->rootfh.data.data_val == NULL) { + rpc_set_error(rpc, "Out of memory. Could not allocate memory to store root filehandle"); + data->cb(-ENOMEM, nfs, rpc_get_error(rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + memcpy(nfs->rootfh.data.data_val, res->mountres3_u.mountinfo.fhandle.fhandle3_val, nfs->rootfh.data.data_len); + + rpc_disconnect(rpc, "normal disconnect"); + + if (rpc_connect_program_async(nfs->rpc, nfs->server, NFS_PROGRAM, NFS_V3, nfs_mount_8_cb, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; } + + /* NFS TCP connections we want to autoreconnect after sessions are torn down (due to inactivity or error) */ + rpc_set_autoreconnect(rpc); } -static void nfs_mount_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) + +static void nfs_mount_5_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; assert(rpc->magic == RPC_CONTEXT_MAGIC); - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - if (status == RPC_STATUS_ERROR) { data->cb(-EFAULT, nfs, command_data, data->private_data); free_nfs_cb_data(data); @@ -537,7 +847,7 @@ static void nfs_mount_1_cb(struct rpc_context *rpc, int status, void *command_da return; } - if (rpc_pmap_null_async(rpc, nfs_mount_2_cb, data) != 0) { + if (rpc_mount3_mnt_async(rpc, nfs_mount_6_cb, nfs->export, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; @@ -572,7 +882,7 @@ int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *exp data->cb = cb; data->private_data = private_data; - if (rpc_connect_async(nfs->rpc, server, 111, nfs_mount_1_cb, data) != 0) { + if (rpc_connect_program_async(nfs->rpc, server, MOUNT_PROGRAM, MOUNT_V3, nfs_mount_5_cb, data) != 0) { rpc_set_error(nfs->rpc, "Failed to start connection"); free_nfs_cb_data(data); return -1; @@ -592,6 +902,7 @@ static void nfs_lookup_path_1_cb(struct rpc_context *rpc, int status, void *comm struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; LOOKUP3res *res; + fattr3 *attr; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -614,27 +925,36 @@ static void nfs_lookup_path_1_cb(struct rpc_context *rpc, int status, void *comm return; } - if (nfs_lookup_path_async_internal(nfs, data, &res->LOOKUP3res_u.resok.object) != 0) { - rpc_set_error(nfs->rpc, "Failed to create lookup pdu"); - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); - free_nfs_cb_data(data); - return; - } + attr = res->LOOKUP3res_u.resok.obj_attributes.attributes_follow ? + &res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes : + NULL; + + /* This function will always invoke the callback and cleanup + * for failures. So no need to check the return value. + */ + nfs_lookup_path_async_internal(nfs, attr, data, &res->LOOKUP3res_u.resok.object); } -static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh) +static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data, struct nfs_fh3 *fh) { - char *path, *str; + char *path, *slash; + LOOKUP3args args; while (*data->path == '/') { data->path++; } path = data->path; - str = strchr(path, '/'); - if (str != NULL) { - *str = 0; - data->path = str+1; + slash = strchr(path, '/'); + if (slash != NULL) { + /* Clear slash so that path is a zero terminated string for + * the current path component. Set it back to '/' again later + * when we are finished referencing this component so that + * data->saved_path will still point to the full + * normalized path. + */ + *slash = 0; + data->path = slash+1; } else { while (*data->path != 0) { data->path++; @@ -651,69 +971,331 @@ static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb return -1; } memcpy(data->fh.data.data_val, fh->data.data_val, data->fh.data.data_len); - data->continue_cb(nfs, data); + if (slash != NULL) { + *slash = '/'; + } + data->continue_cb(nfs, attr, data); return 0; } - if (rpc_nfs_lookup_async(nfs->rpc, nfs_lookup_path_1_cb, fh, path, data) != 0) { + memset(&args, 0, sizeof(LOOKUP3args)); + args.what.dir = *fh; + args.what.name = path; + + if (rpc_nfs3_lookup_async(nfs->rpc, nfs_lookup_path_1_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); return -1; } + if (slash != NULL) { + *slash = '/'; + } + return 0; +} + +static int nfs_normalize_path(struct nfs_context *nfs, char *path) +{ + char *str; + int len; + + /* // -> / */ + while ((str = strstr(path, "//"))) { + while(*str) { + *str = *(str + 1); + str++; + } + } + + /* /./ -> / */ + while ((str = strstr(path, "/./"))) { + while(*(str + 1)) { + *str = *(str + 2); + str++; + } + } + + /* ^/../ -> error */ + if (!strncmp(path, "/../", 4)) { + rpc_set_error(nfs->rpc, + "Absolute path starts with '/../' " + "during normalization"); + return -1; + } + + /* ^[^/] -> error */ + if (path[0] != '/') { + rpc_set_error(nfs->rpc, + "Absolute path does not start with '/'"); + return -1; + } + + /* /string/../ -> / */ + while ((str = strstr(path, "/../"))) { + char *tmp; + + if (!strncmp(path, "/../", 4)) { + rpc_set_error(nfs->rpc, + "Absolute path starts with '/../' " + "during normalization"); + return -1; + } + + tmp = str - 1; + while (*tmp != '/') { + tmp--; + } + str += 3; + while((*(tmp++) = *(str++)) != '\0') + ; + } + + /* /$ -> \0 */ + len = strlen(path); + if (len > 1) { + if (path[len - 1] == '/') { + path[len - 1] = '\0'; + len--; + } + } + if (path[0] == '\0') { + rpc_set_error(nfs->rpc, + "Absolute path became '' " + "during normalization"); + return -1; + } + + /* /.$ -> \0 */ + if (len >= 2) { + if (!strcmp(&path[len - 2], "/.")) { + path[len - 2] = '\0'; + len -= 2; + } + } + + /* ^/..$ -> error */ + if (!strcmp(path, "/..")) { + rpc_set_error(nfs->rpc, + "Absolute path is '/..' " + "during normalization"); + return -1; + } + + /* /string/..$ -> / */ + if (len >= 3) { + if (!strcmp(&path[len - 3], "/..")) { + char *tmp = &path[len - 3]; + while (*--tmp != '/') + ; + *tmp = '\0'; + } + } + + return 0; +} + +static void nfs_lookup_path_getattr_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; + fattr3 *attr; + + 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 of %s failed with %s(%d)", data->saved_path, 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; + } + + attr = &res->GETATTR3res_u.resok.obj_attributes; + /* This function will always invoke the callback and cleanup + * for failures. So no need to check the return value. + */ + nfs_lookup_path_async_internal(nfs, attr, data, &nfs->rootfh); +} + +static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data, continue_func continue_cb, void *continue_data, void (*free_continue_data)(void *), int continue_int) +{ + struct nfs_cb_data *data; + struct GETATTR3args args; + + if (path[0] == '\0') { + path = "."; + } + + 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"); + if (free_continue_data) + free_continue_data(continue_data); + return -1; + } + memset(data, 0, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->continue_cb = continue_cb; + data->continue_data = continue_data; + data->free_continue_data = free_continue_data; + data->continue_int = continue_int; + data->private_data = private_data; + if (path[0] == '/') { + data->saved_path = strdup(path); + } else { + data->saved_path = malloc(strlen(path) + strlen(nfs->cwd) + 2); + if (data->saved_path == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to " + "malloc path string"); + free_nfs_cb_data(data); + return -1; + } + sprintf(data->saved_path, "%s/%s", nfs->cwd, path); + } + + if (data->saved_path == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to copy path string"); + free_nfs_cb_data(data); + return -1; + } + if (nfs_normalize_path(nfs, data->saved_path) != 0) { + free_nfs_cb_data(data); + return -1; + } + + data->path = data->saved_path; + if (data->path[0]) { + /* This function will always invoke the callback and cleanup + * for failures. So no need to check the return value. + */ + nfs_lookup_path_async_internal(nfs, NULL, data, &nfs->rootfh); + return 0; + } + + /* We have a request for "", so just perform a GETATTR3 so we can + * return the attributes to the caller. + */ + memset(&args, 0, sizeof(GETATTR3args)); + args.object = nfs->rootfh; + if (rpc_nfs3_getattr_async(nfs->rpc, nfs_lookup_path_getattr_cb, &args, data) != 0) { + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + +/* + * Async stat() + */ +static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + GETATTR3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; +#ifdef WIN32 + struct __stat64 st; +#else + struct stat st; +#endif + + 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 of %s failed with %s(%d)", data->saved_path, 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; + } + + st.st_dev = -1; + st.st_ino = res->GETATTR3res_u.resok.obj_attributes.fileid; + st.st_mode = res->GETATTR3res_u.resok.obj_attributes.mode; + if (res->GETATTR3res_u.resok.obj_attributes.type == NF3DIR) { + st.st_mode |= S_IFDIR ; + } + if (res->GETATTR3res_u.resok.obj_attributes.type == NF3REG) { + st.st_mode |= S_IFREG ; + } + st.st_nlink = res->GETATTR3res_u.resok.obj_attributes.nlink; + st.st_uid = res->GETATTR3res_u.resok.obj_attributes.uid; + st.st_gid = res->GETATTR3res_u.resok.obj_attributes.gid; + st.st_rdev = 0; + st.st_size = res->GETATTR3res_u.resok.obj_attributes.size; +#ifndef WIN32 + st.st_blksize = NFS_BLKSIZE; + st.st_blocks = res->GETATTR3res_u.resok.obj_attributes.size / NFS_BLKSIZE; +#endif//WIN32 + st.st_atime = res->GETATTR3res_u.resok.obj_attributes.atime.seconds; + st.st_mtime = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds; + st.st_ctime = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds; + + data->cb(0, nfs, &st, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_stat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) +{ + struct GETATTR3args args; + + memset(&args, 0, sizeof(GETATTR3args)); + args.object = data->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; } -static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data, continue_func continue_cb, void *continue_data, void (*free_continue_data)(void *), int continue_int) +int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { - struct nfs_cb_data *data; - - if (path[0] != 0 && path[0] != '/') { - rpc_set_error(nfs->rpc, "Pathname is not absolute %s", path); - return -1; - } - - 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->continue_cb = continue_cb; - data->continue_data = continue_data; - data->free_continue_data = free_continue_data; - data->continue_int = continue_int; - data->private_data = private_data; - data->saved_path = strdup(path); - if (data->saved_path == NULL) { - rpc_set_error(nfs->rpc, "out of memory: failed to copy path string"); - free_nfs_cb_data(data); + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_stat_continue_internal, NULL, NULL, 0) != 0) { + rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); return -1; } - data->path = data->saved_path; - if (nfs_lookup_path_async_internal(nfs, data, &nfs->rootfh) != 0) { - /* return 0 here since the callback will be invoked if there is a failure */ - return 0; - } return 0; } - - - /* - * Async stat() + * Async nfs_stat64() */ -static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void nfs_stat64_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { GETATTR3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; - struct stat st; + struct nfs_stat_64 st; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -736,35 +1318,36 @@ static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_dat return; } - st.st_dev = -1; - st.st_ino = res->GETATTR3res_u.resok.obj_attributes.fileid; - st.st_mode = res->GETATTR3res_u.resok.obj_attributes.mode; + st.nfs_dev = -1; + st.nfs_ino = res->GETATTR3res_u.resok.obj_attributes.fileid; + st.nfs_mode = res->GETATTR3res_u.resok.obj_attributes.mode; if (res->GETATTR3res_u.resok.obj_attributes.type == NF3DIR) { - st.st_mode |= S_IFDIR ; + st.nfs_mode |= S_IFDIR ; } if (res->GETATTR3res_u.resok.obj_attributes.type == NF3REG) { - st.st_mode |= S_IFREG ; + st.nfs_mode |= S_IFREG ; } - st.st_nlink = res->GETATTR3res_u.resok.obj_attributes.nlink; - st.st_uid = res->GETATTR3res_u.resok.obj_attributes.uid; - st.st_gid = res->GETATTR3res_u.resok.obj_attributes.gid; - st.st_rdev = 0; - st.st_size = res->GETATTR3res_u.resok.obj_attributes.size; -#ifndef WIN32 - st.st_blksize = 4096; - st.st_blocks = res->GETATTR3res_u.resok.obj_attributes.size / 4096; -#endif//WIN32 - st.st_atime = res->GETATTR3res_u.resok.obj_attributes.atime.seconds; - st.st_mtime = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds; - st.st_ctime = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds; + st.nfs_nlink = res->GETATTR3res_u.resok.obj_attributes.nlink; + st.nfs_uid = res->GETATTR3res_u.resok.obj_attributes.uid; + st.nfs_gid = res->GETATTR3res_u.resok.obj_attributes.gid; + st.nfs_rdev = 0; + st.nfs_size = res->GETATTR3res_u.resok.obj_attributes.size; + st.nfs_atime = res->GETATTR3res_u.resok.obj_attributes.atime.seconds; + st.nfs_mtime = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds; + st.nfs_ctime = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds; data->cb(0, nfs, &st, data->private_data); free_nfs_cb_data(data); } -static int nfs_stat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_stat64_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { - if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &data->fh, data) != 0) { + struct GETATTR3args args; + + memset(&args, 0, sizeof(GETATTR3args)); + args.object = data->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); @@ -773,9 +1356,9 @@ static int nfs_stat_continue_internal(struct nfs_context *nfs, struct nfs_cb_dat return 0; } -int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +int nfs_stat64_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) { - if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_stat_continue_internal, NULL, NULL, 0) != 0) { + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_stat64_continue_internal, NULL, NULL, 0) != 0) { rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); return -1; } @@ -783,13 +1366,61 @@ int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *p return 0; } +/* + * Async open() + */ +static void nfs_open_trunc_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; + struct nfsfh *nfsfh; + SETATTR3res *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: Setattr 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; + } + nfsfh = malloc(sizeof(struct nfsfh)); + if (nfsfh == NULL) { + rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure"); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + memset(nfsfh, 0, sizeof(struct nfsfh)); + if (data->continue_int & O_SYNC) { + nfsfh->is_sync = 1; + } + if (data->continue_int & O_APPEND) { + nfsfh->is_append = 1; + } + /* steal the filehandle */ + nfsfh->fh = data->fh; + data->fh.data.data_val = NULL; + + data->cb(0, nfs, nfsfh, data->private_data); + free_nfs_cb_data(data); +} -/* - * Async open() - */ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { ACCESS3res *res; @@ -843,6 +1474,28 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, return; } + /* Try to truncate it if we were requested to */ + if ((data->continue_int & O_TRUNC) && + (data->continue_int & (O_RDWR|O_WRONLY))) { + SETATTR3args args; + + memset(&args, 0, sizeof(SETATTR3args)); + args.object = data->fh; + args.new_attributes.size.set_it = 1; + args.new_attributes.size.set_size3_u.size = 0; + + if (rpc_nfs3_setattr_async(nfs->rpc, nfs_open_trunc_cb, &args, + data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send " + "SETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), + data->private_data); + free_nfs_cb_data(data); + return; + } + return; + } + nfsfh = malloc(sizeof(struct nfsfh)); if (nfsfh == NULL) { rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure"); @@ -855,19 +1508,22 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, if (data->continue_int & O_SYNC) { nfsfh->is_sync = 1; } + if (data->continue_int & O_APPEND) { + nfsfh->is_append = 1; + } /* steal the filehandle */ - nfsfh->fh.data.data_len = data->fh.data.data_len; - nfsfh->fh.data.data_val = data->fh.data.data_val; + nfsfh->fh = data->fh; data->fh.data.data_val = NULL; data->cb(0, nfs, nfsfh, data->private_data); free_nfs_cb_data(data); } -static int nfs_open_continue_internal(struct nfs_context *nfs, struct 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; @@ -879,18 +1535,24 @@ static int nfs_open_continue_internal(struct nfs_context *nfs, struct nfs_cb_dat nfsmode |= ACCESS3_READ; } - if (rpc_nfs_access_async(nfs->rpc, nfs_open_cb, &data->fh, nfsmode, 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); + 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 mode, nfs_cb cb, void *private_data) +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, mode) != 0) { + 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; } @@ -899,42 +1561,42 @@ int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb c } - - - /* - * Async pread() + * Async chdir() */ -static void nfs_pread_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static int nfs_chdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { - struct nfs_cb_data *data = private_data; - struct nfs_context *nfs = data->nfs; - READ3res *res; + /* steal saved_path */ + free(nfs->cwd); + nfs->cwd = data->saved_path; + data->saved_path = NULL; - assert(rpc->magic == RPC_CONTEXT_MAGIC); + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); - 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; - } + return 0; +} - 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->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); - free_nfs_cb_data(data); - return; +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; } - data->nfsfh->offset += res->READ3res_u.resok.count; - data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data); - free_nfs_cb_data(data); + 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) @@ -957,49 +1619,117 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat data->cancel = 1; } - /* reassemble the data into the buffer */ 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 { - if (res->READ3res_u.resok.count > 0) { - memcpy(&data->buffer[mdata->offset - data->start_offset], res->READ3res_u.resok.data.data_val, res->READ3res_u.resok.count); - if ((unsigned)data->max_offset < mdata->offset + res->READ3res_u.resok.count) { - data->max_offset = mdata->offset + res->READ3res_u.resok.count; + } 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 */ - free(mdata); 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); - free(mdata); return; } if (data->cancel != 0) { data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); free_nfs_cb_data(data); - free(mdata); return; } - data->nfsfh->offset = data->max_offset; - data->cb(data->max_offset - data->start_offset, nfs, data->buffer, data->private_data); + 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); - free(mdata); } -int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_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; @@ -1013,37 +1743,82 @@ int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse data->cb = cb; data->private_data = private_data; data->nfsfh = nfsfh; + data->org_offset = offset; + data->org_count = count; - nfsfh->offset = offset; + assert(data->num_calls == 0); - if (count <= nfs_get_readmax(nfs)) { - if (rpc_nfs_read_async(nfs->rpc, nfs_pread_cb, &nfsfh->fh, offset, count, data) != 0) { - rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path); - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + 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 -1; + return 0; } - return 0; - } - /* trying to read more than maximum server read size, we has to chop it up into smaller - * reads and collect into a reassembly buffer. - * we send all reads in parallell so that performance is still good. - */ - data->max_offset = offset; - data->start_offset = offset; + /* align start offset to blocksize */ + count += offset & (NFS_BLKSIZE - 1); + offset &= ~(NFS_BLKSIZE - 1); - data->buffer = malloc(count); - if (data->buffer == NULL) { - rpc_set_error(nfs->rpc, "Out-Of-Memory: Failed to allocate reassembly buffer for %d bytes", (int)count); - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); - free_nfs_cb_data(data); - return -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; } - while (count > 0) { + 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); @@ -1052,33 +1827,51 @@ int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse 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"); - return -1; + 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; - if (rpc_nfs_read_async(nfs->rpc, nfs_pread_mcb, &nfsfh->fh, offset, readcount, mdata) != 0) { + 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); - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free(mdata); - return -1; + 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(nfs, nfsfh, nfsfh->offset, count, cb, private_data); + return nfs_pread_async_internal(nfs, nfsfh, nfsfh->offset, count, cb, private_data, 1); } @@ -1086,36 +1879,16 @@ int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, /* * Async pwrite() */ -static void nfs_pwrite_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void nfs_fill_WRITE3args (WRITE3args *args, struct nfsfh *fh, uint64_t offset, uint64_t count, + void *buf) { - struct nfs_cb_data *data = private_data; - struct nfs_context *nfs = data->nfs; - WRITE3res *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: Write 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; - } - - data->nfsfh->offset += res->WRITE3res_u.resok.count; - data->cb(res->WRITE3res_u.resok.count, nfs, NULL, data->private_data); - free_nfs_cb_data(data); + 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) @@ -1144,42 +1917,69 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da 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 { - if (res->WRITE3res_u.resok.count > 0) { - if ((unsigned)data->max_offset < mdata->offset + res->WRITE3res_u.resok.count) { - data->max_offset = mdata->offset + res->WRITE3res_u.resok.count; + 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 */ - free(mdata); 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); - free(mdata); return; } if (data->cancel != 0) { data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); free_nfs_cb_data(data); - free(mdata); return; } - data->nfsfh->offset = data->max_offset; - data->cb(data->max_offset - data->start_offset, nfs, NULL, data->private_data); + data->cb(data->max_offset - data->offset, nfs, NULL, data->private_data); free_nfs_cb_data(data); - free(mdata); } -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) +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; @@ -1193,29 +1993,21 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs data->cb = cb; data->private_data = private_data; data->nfsfh = nfsfh; + data->usrbuf = buf; - nfsfh->offset = offset; - - if (count <= nfs_get_writemax(nfs)) { - if (rpc_nfs_write_async(nfs->rpc, nfs_pwrite_cb, &nfsfh->fh, buf, offset, count, nfsfh->is_sync?FILE_SYNC:UNSTABLE, data) != 0) { - rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE 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; - } + /* hello, clang-analyzer */ + assert(data->num_calls == 0); - /* trying to write more than maximum server write size, we has to chop it up into smaller - * chunks. - * we send all writes in parallell so that performance is still good. + /* 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->start_offset = offset; + data->offset = offset; - while (count > 0) { + do { uint64_t writecount = count; struct nfs_mcb_data *mdata; + WRITE3args args; if (writecount > nfs_get_writemax(nfs)) { writecount = nfs_get_writemax(nfs); @@ -1224,34 +2016,114 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs 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"); - return -1; + 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; - if (rpc_nfs_write_async(nfs->rpc, nfs_pwrite_mcb, &nfsfh->fh, &buf[offset - data->start_offset], offset, writecount, nfsfh->is_sync?FILE_SYNC:UNSTABLE, mdata) != 0) { + 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); - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free(mdata); - return -1; + 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) { - return nfs_pwrite_async(nfs, nfsfh, nfsfh->offset, count, buf, cb, 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); } @@ -1260,13 +2132,14 @@ int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count /* * close */ - + int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) { if (nfsfh->fh.data.data_val != NULL){ free(nfsfh->fh.data.data_val); nfsfh->fh.data.data_val = NULL; } + free(nfsfh->ra.buf); free(nfsfh); cb(0, nfs, NULL, private_data); @@ -1283,6 +2156,7 @@ int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, voi 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) { @@ -1294,7 +2168,10 @@ int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, voi data->cb = cb; data->private_data = private_data; - if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &nfsfh->fh, data) != 0) { + 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); @@ -1342,6 +2219,7 @@ static void nfs_fsync_cb(struct rpc_context *rpc, int status, void *command_data int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) { struct nfs_cb_data *data; + struct COMMIT3args args; data = malloc(sizeof(struct nfs_cb_data)); if (data == NULL) { @@ -1353,7 +2231,10 @@ int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, voi data->cb = cb; data->private_data = private_data; - if (rpc_nfs_commit_async(nfs->rpc, nfs_fsync_cb, &nfsfh->fh, data) != 0) { + args.file = nfsfh->fh; + args.offset = 0; + args.count = 0; + if (rpc_nfs3_commit_async(nfs->rpc, nfs_fsync_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send COMMIT call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -1415,12 +2296,11 @@ int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t l data->private_data = private_data; memset(&args, 0, sizeof(SETATTR3args)); - args.object.data.data_len = nfsfh->fh.data.data_len; - args.object.data.data_val = nfsfh->fh.data.data_val; + args.object = nfsfh->fh; args.new_attributes.size.set_it = 1; args.new_attributes.size.set_size3_u.size = length; - if (rpc_nfs_setattr_async(nfs->rpc, nfs_ftruncate_cb, &args, data) != 0) { + if (rpc_nfs3_setattr_async(nfs->rpc, nfs_ftruncate_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -1433,13 +2313,12 @@ int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t l /* * Async truncate() */ -static int nfs_truncate_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_truncate_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { uint64_t offset = data->continue_int; struct nfsfh nfsfh; - nfsfh.fh.data.data_val = data->fh.data.data_val; - nfsfh.fh.data.data_len = data->fh.data.data_len; + nfsfh.fh = data->fh; if (nfs_ftruncate_async(nfs, &nfsfh, offset, data->cb, data->private_data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); @@ -1477,7 +2356,7 @@ static void nfs_mkdir_cb(struct rpc_context *rpc, int status, void *command_data struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; char *str = data->continue_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); str = &str[strlen(str) + 1]; @@ -1505,7 +2384,7 @@ static void nfs_mkdir_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_mkdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_mkdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { char *str = data->continue_data; MKDIR3args args; @@ -1513,13 +2392,12 @@ static int nfs_mkdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_da str = &str[strlen(str) + 1]; memset(&args, 0, sizeof(MKDIR3args)); - args.where.dir.data.data_len = data->fh.data.data_len; - args.where.dir.data.data_val = data->fh.data.data_val; + args.where.dir = data->fh; args.where.name = str; args.attributes.mode.set_it = 1; args.attributes.mode.set_mode3_u.mode = 0755; - if (rpc_nfs_mkdir_async(nfs->rpc, nfs_mkdir_cb, &args, data) != 0) { + if (rpc_nfs3_mkdir_async(nfs->rpc, nfs_mkdir_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send MKDIR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -1541,6 +2419,7 @@ int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void * ptr = strrchr(new_path, '/'); if (ptr == NULL) { + free(new_path); rpc_set_error(nfs->rpc, "Invalid path %s", path); return -1; } @@ -1568,7 +2447,7 @@ static void nfs_rmdir_cb(struct rpc_context *rpc, int status, void *command_data struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; char *str = data->continue_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); str = &str[strlen(str) + 1]; @@ -1596,13 +2475,16 @@ static void nfs_rmdir_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_rmdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_rmdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { char *str = data->continue_data; - + RMDIR3args args; + str = &str[strlen(str) + 1]; - if (rpc_nfs_rmdir_async(nfs->rpc, nfs_rmdir_cb, &data->fh, str, data) != 0) { + args.object.dir = data->fh; + args.object.name = str; + if (rpc_nfs3_rmdir_async(nfs->rpc, nfs_rmdir_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send RMDIR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -1624,6 +2506,7 @@ int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void * ptr = strrchr(new_path, '/'); if (ptr == NULL) { + free(new_path); rpc_set_error(nfs->rpc, "Invalid path %s", path); return -1; } @@ -1644,13 +2527,28 @@ int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void * /* * Async creat() */ +struct create_cb_data { + char *path; + int flags; + int mode; +}; + +static void free_create_cb_data(void *ptr) +{ + struct create_cb_data *data = ptr; + + free(data->path); + free(data); +} + static void nfs_create_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { LOOKUP3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; struct nfsfh *nfsfh; - char *str = data->continue_data; + struct create_cb_data *cb_data = data->continue_data; + char *str = cb_data->path; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -1683,6 +2581,13 @@ static void nfs_create_2_cb(struct rpc_context *rpc, int status, void *command_d } memset(nfsfh, 0, sizeof(struct nfsfh)); + if (cb_data->flags & O_SYNC) { + nfsfh->is_sync = 1; + } + if (cb_data->flags & O_APPEND) { + nfsfh->is_append = 1; + } + /* copy the filehandle */ nfsfh->fh.data.data_len = res->LOOKUP3res_u.resok.object.data.data_len; nfsfh->fh.data.data_val = malloc(nfsfh->fh.data.data_len); @@ -1694,12 +2599,14 @@ static void nfs_create_2_cb(struct rpc_context *rpc, int status, void *command_d -static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void nfs_create_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { CREATE3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; - char *str = data->continue_data; + struct create_cb_data *cb_data = data->continue_data; + char *str = cb_data->path; + LOOKUP3args args; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -1719,11 +2626,15 @@ static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_da if (res->status != NFS3_OK) { rpc_set_error(nfs->rpc, "NFS: CREATE of %s/%s failed with %s(%d)", data->saved_path, str, 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 (rpc_nfs_lookup_async(nfs->rpc, nfs_create_2_cb, &data->fh, str, data) != 0) { + memset(&args, 0, sizeof(LOOKUP3args)); + args.what.dir = data->fh; + args.what.name = str; + + if (rpc_nfs3_lookup_async(nfs->rpc, nfs_create_2_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s/%s", data->saved_path, str); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -1732,22 +2643,22 @@ static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_da return; } -static int nfs_creat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_create_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { - char *str = data->continue_data; + struct create_cb_data *cb_data = data->continue_data; + char *str = cb_data->path; CREATE3args args; str = &str[strlen(str) + 1]; memset(&args, 0, sizeof(CREATE3args)); - args.where.dir.data.data_len = data->fh.data.data_len; - args.where.dir.data.data_val = data->fh.data.data_val; + args.where.dir = data->fh; args.where.name = str; - args.how.mode = UNCHECKED; + args.how.mode = (cb_data->flags & O_EXCL) ? GUARDED : UNCHECKED; args.how.createhow3_u.obj_attributes.mode.set_it = 1; - args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = data->continue_int; + args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = cb_data->mode; - if (rpc_nfs_create_async(nfs->rpc, nfs_creat_1_cb, &args, data) != 0) { + if (rpc_nfs3_create_async(nfs->rpc, nfs_create_1_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send CREATE call for %s/%s", data->path, str); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -1756,26 +2667,37 @@ static int nfs_creat_continue_internal(struct nfs_context *nfs, struct nfs_cb_da return 0; } -int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +int nfs_create_async(struct nfs_context *nfs, const char *path, int flags, int mode, nfs_cb cb, void *private_data) { - char *new_path; + struct create_cb_data *cb_data; char *ptr; - new_path = strdup(path); - if (new_path == NULL) { + cb_data = malloc(sizeof(struct create_cb_data)); + if (cb_data == NULL) { + rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for cb data"); + return -1; + } + + cb_data->path = strdup(path); + if (cb_data->path == NULL) { rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for path"); + free(cb_data); return -1; } - ptr = strrchr(new_path, '/'); + ptr = strrchr(cb_data->path, '/'); if (ptr == NULL) { rpc_set_error(nfs->rpc, "Invalid path %s", path); + free_create_cb_data(cb_data); return -1; } *ptr = 0; + cb_data->flags = flags; + cb_data->mode = mode; + /* new_path now points to the parent directory, and beyond the nul terminator is the new directory to create */ - if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_creat_continue_internal, new_path, free, mode) != 0) { + if (nfs_lookuppath_async(nfs, cb_data->path, cb, private_data, nfs_create_continue_internal, cb_data, free_create_cb_data, 0) != 0) { rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); return -1; } @@ -1783,6 +2705,11 @@ int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb return 0; } +int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +{ + return nfs_create_async(nfs, path, 0, mode, cb, private_data); +} + @@ -1795,7 +2722,7 @@ static void nfs_unlink_cb(struct rpc_context *rpc, int status, void *command_dat struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; char *str = data->continue_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); str = &str[strlen(str) + 1]; @@ -1823,13 +2750,16 @@ static void nfs_unlink_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_unlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_unlink_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { char *str = data->continue_data; - + struct REMOVE3args args; + str = &str[strlen(str) + 1]; - if (rpc_nfs_remove_async(nfs->rpc, nfs_unlink_cb, &data->fh, str, data) != 0) { + args.object.dir = data->fh; + args.object.name = str; + if (rpc_nfs3_remove_async(nfs->rpc, nfs_unlink_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send REMOVE call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -1851,6 +2781,7 @@ int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void ptr = strrchr(new_path, '/'); if (ptr == NULL) { + free(new_path); rpc_set_error(nfs->rpc, "Invalid path %s", path); return -1; } @@ -1890,7 +2821,7 @@ static void nfs_mknod_cb(struct rpc_context *rpc, int status, void *command_data struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; char *str = data->continue_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); str = &str[strlen(str) + 1]; @@ -1918,14 +2849,48 @@ static void nfs_mknod_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_mknod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_mknod_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct mknod_cb_data *cb_data = data->continue_data; char *str = cb_data->path; - + MKNOD3args args; + str = &str[strlen(str) + 1]; - if (rpc_nfs_mknod_async(nfs->rpc, nfs_mknod_cb, &data->fh, str, cb_data->mode, cb_data->major, cb_data->minor, data) != 0) { + args.where.dir = data->fh; + args.where.name = str; + switch (cb_data->mode & S_IFMT) { + case S_IFCHR: + args.what.type = NF3CHR; + args.what.mknoddata3_u.chr_device.dev_attributes.mode.set_it = 1; + args.what.mknoddata3_u.chr_device.dev_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH); + args.what.mknoddata3_u.chr_device.spec.specdata1 = cb_data->major; + args.what.mknoddata3_u.chr_device.spec.specdata2 = cb_data->minor; + break; + case S_IFBLK: + args.what.type = NF3BLK; + args.what.mknoddata3_u.blk_device.dev_attributes.mode.set_it = 1; + args.what.mknoddata3_u.blk_device.dev_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH); + args.what.mknoddata3_u.blk_device.spec.specdata1 = cb_data->major; + args.what.mknoddata3_u.blk_device.spec.specdata2 = cb_data->minor; + case S_IFSOCK: + args.what.type = NF3SOCK; + args.what.mknoddata3_u.sock_attributes.mode.set_it = 1; + args.what.mknoddata3_u.sock_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH); + break; + case S_IFIFO: + args.what.type = NF3FIFO; + args.what.mknoddata3_u.pipe_attributes.mode.set_it = 1; + args.what.mknoddata3_u.pipe_attributes.mode.set_mode3_u.mode = cb_data->mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH); + break; + default: + rpc_set_error(nfs->rpc, "Invalid file type for NFS3/MKNOD call"); + data->cb(-EINVAL, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + + if (rpc_nfs3_mknod_async(nfs->rpc, nfs_mknod_cb, &args, data) != 0) { data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); return -1; @@ -1947,13 +2912,14 @@ int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev cb_data->path = strdup(path); if (cb_data->path == NULL) { rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for path"); - free(cb_data); + free(cb_data); return -1; } ptr = strrchr(cb_data->path, '/'); if (ptr == NULL) { rpc_set_error(nfs->rpc, "Invalid path %s", path); + free_mknod_cb_data(cb_data); return -1; } *ptr = 0; @@ -1965,7 +2931,6 @@ int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev /* data->path now points to the parent directory, and beyond the nul terminateor is the new directory to create */ if (nfs_lookuppath_async(nfs, cb_data->path, cb, private_data, nfs_mknod_continue_internal, cb_data, free_mknod_cb_data, 0) != 0) { rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); - free_mknod_cb_data(cb_data); return -1; } @@ -2007,14 +2972,15 @@ static void nfs_opendir3_cb(struct rpc_context *rpc, int status, void *command_d rdpe_cb_data->getattrcount--; if (status == RPC_STATUS_ERROR) { + rpc_set_error(nfs->rpc, "LOOKUP during READDIRPLUS emulation " + "failed with RPC_STATUS_ERROR"); rdpe_cb_data->status = RPC_STATUS_ERROR; } if (status == RPC_STATUS_CANCEL) { + rpc_set_error(nfs->rpc, "LOOKUP during READDIRPLUS emulation " + "failed with RPC_STATUS_CANCEL"); rdpe_cb_data->status = RPC_STATUS_CANCEL; } - if (status == RPC_STATUS_SUCCESS && res->status != NFS3_OK) { - rdpe_cb_data->status = RPC_STATUS_ERROR; - } if (status == RPC_STATUS_SUCCESS && res->status == NFS3_OK) { if (res->LOOKUP3res_u.resok.obj_attributes.attributes_follow) { fattr3 *attributes = &res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes; @@ -2029,12 +2995,18 @@ static void nfs_opendir3_cb(struct rpc_context *rpc, int status, void *command_d nfsdirent->mtime.tv_usec = attributes->mtime.nseconds/1000; nfsdirent->ctime.tv_sec = attributes->ctime.seconds; nfsdirent->ctime.tv_usec = attributes->ctime.nseconds/1000; + nfsdirent->uid = attributes->uid; + nfsdirent->gid = attributes->gid; + nfsdirent->nlink = attributes->nlink; } } if (rdpe_cb_data->getattrcount == 0) { if (rdpe_cb_data->status != RPC_STATUS_SUCCESS) { - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + rpc_set_error(nfs->rpc, "READDIRPLUS emulation " + "failed: %s", rpc_get_error(rpc)); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), + data->private_data); nfs_free_nfsdir(nfsdir); } else { data->cb(0, nfs, nfsdir, data->private_data); @@ -2054,9 +3026,9 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d struct nfsdir *nfsdir = data->continue_data; struct nfsdirent *nfsdirent; struct entry3 *entry; - uint64_t cookie; + uint64_t cookie = 0; struct rdpe_cb_data *rdpe_cb_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status == RPC_STATUS_ERROR) { @@ -2098,6 +3070,7 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d nfsdirent->name = strdup(entry->name); if (nfsdirent->name == NULL) { data->cb(-ENOMEM, nfs, "Failed to allocate dirent->name", data->private_data); + free(nfsdirent); nfs_free_nfsdir(nfsdir); data->continue_data = NULL; free_nfs_cb_data(data); @@ -2113,7 +3086,14 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d } if (res->READDIR3res_u.resok.reply.eof == 0) { - if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir2_cb, &data->fh, cookie, res->READDIR3res_u.resok.cookieverf, 8192, data) != 0) { + READDIR3args args; + + args.dir = data->fh; + args.cookie = cookie; + memcpy(&args.cookieverf, res->READDIR3res_u.resok.cookieverf, sizeof(cookieverf3)); + args.count = 8192; + + if (rpc_nfs3_readdir_async(nfs->rpc, nfs_opendir2_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); nfs_free_nfsdir(nfsdir); @@ -2124,45 +3104,57 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d return; } + if (res->READDIR3res_u.resok.dir_attributes.attributes_follow) + nfsdir->attr = res->READDIR3res_u.resok.dir_attributes.post_op_attr_u.attributes; + /* steal the dirhandle */ nfsdir->current = nfsdir->entries; - rdpe_cb_data = malloc(sizeof(struct rdpe_cb_data)); - rdpe_cb_data->getattrcount = 0; - rdpe_cb_data->status = RPC_STATUS_SUCCESS; - rdpe_cb_data->data = data; - for (nfsdirent = nfsdir->entries; nfsdirent; nfsdirent = nfsdirent->next) { - struct rdpe_lookup_cb_data *rdpe_lookup_cb_data; - - rdpe_lookup_cb_data = malloc(sizeof(struct rdpe_lookup_cb_data)); - rdpe_lookup_cb_data->rdpe_cb_data = rdpe_cb_data; - rdpe_lookup_cb_data->nfsdirent = nfsdirent; - - if (rpc_nfs_lookup_async(nfs->rpc, nfs_opendir3_cb, &data->fh, nfsdirent->name, rdpe_lookup_cb_data) != 0) { - rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR LOOKUP call"); - - /* if we have already commands in flight, we cant just stop, we have to wait for the - * commands in flight to complete - */ - if (rdpe_cb_data->getattrcount > 0) { - rdpe_cb_data->status = RPC_STATUS_ERROR; + if (nfsdir->entries) { + rdpe_cb_data = malloc(sizeof(struct rdpe_cb_data)); + rdpe_cb_data->getattrcount = 0; + rdpe_cb_data->status = RPC_STATUS_SUCCESS; + rdpe_cb_data->data = data; + for (nfsdirent = nfsdir->entries; nfsdirent; nfsdirent = nfsdirent->next) { + struct rdpe_lookup_cb_data *rdpe_lookup_cb_data; + LOOKUP3args args; + + rdpe_lookup_cb_data = malloc(sizeof(struct rdpe_lookup_cb_data)); + rdpe_lookup_cb_data->rdpe_cb_data = rdpe_cb_data; + rdpe_lookup_cb_data->nfsdirent = nfsdirent; + + memset(&args, 0, sizeof(LOOKUP3args)); + args.what.dir = data->fh; + args.what.name = nfsdirent->name; + + if (rpc_nfs3_lookup_async(nfs->rpc, nfs_opendir3_cb, &args, rdpe_lookup_cb_data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR LOOKUP call"); + + /* if we have already commands in flight, we cant just stop, we have to wait for the + * commands in flight to complete + */ + if (rdpe_cb_data->getattrcount > 0) { + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + rdpe_cb_data->status = RPC_STATUS_ERROR; + free(rdpe_lookup_cb_data); + return; + } + + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); free(rdpe_lookup_cb_data); + free(rdpe_cb_data); return; } - - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); - nfs_free_nfsdir(nfsdir); - data->continue_data = NULL; - free_nfs_cb_data(data); - free(rdpe_lookup_cb_data); - free(rdpe_cb_data); - return; + rdpe_cb_data->getattrcount++; } - rdpe_cb_data->getattrcount++; } } - static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { READDIRPLUS3res *res = command_data; @@ -2170,14 +3162,19 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da struct nfs_context *nfs = data->nfs; struct nfsdir *nfsdir = data->continue_data; struct entryplus3 *entry; - uint64_t cookie; - + uint64_t cookie = 0; + assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status == RPC_STATUS_ERROR || (status == RPC_STATUS_SUCCESS && res->status == NFS3ERR_NOTSUPP) ){ - cookieverf3 cv; + READDIR3args args; - if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir2_cb, &data->fh, 0, (char *)&cv, 8192, data) != 0) { + args.dir = data->fh; + args.cookie = cookie; + memset(&args.cookieverf, 0, sizeof(cookieverf3)); + args.count = 8192; + + if (rpc_nfs3_readdir_async(nfs->rpc, nfs_opendir2_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); nfs_free_nfsdir(nfsdir); @@ -2221,6 +3218,7 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da nfsdirent->name = strdup(entry->name); if (nfsdirent->name == NULL) { data->cb(-ENOMEM, nfs, "Failed to allocate dirent->name", data->private_data); + free(nfsdirent); nfs_free_nfsdir(nfsdir); data->continue_data = NULL; free_nfs_cb_data(data); @@ -2238,6 +3236,9 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da nfsdirent->mtime.tv_usec = entry->name_attributes.post_op_attr_u.attributes.mtime.nseconds/1000; nfsdirent->ctime.tv_sec = entry->name_attributes.post_op_attr_u.attributes.ctime.seconds; nfsdirent->ctime.tv_usec = entry->name_attributes.post_op_attr_u.attributes.ctime.nseconds/1000; + nfsdirent->uid = entry->name_attributes.post_op_attr_u.attributes.uid; + nfsdirent->gid = entry->name_attributes.post_op_attr_u.attributes.gid; + nfsdirent->nlink = entry->name_attributes.post_op_attr_u.attributes.nlink; } nfsdirent->next = nfsdir->entries; @@ -2248,7 +3249,15 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da } if (res->READDIRPLUS3res_u.resok.reply.eof == 0) { - if (rpc_nfs_readdirplus_async(nfs->rpc, nfs_opendir_cb, &data->fh, cookie, res->READDIRPLUS3res_u.resok.cookieverf, 8192, data) != 0) { + READDIRPLUS3args args; + + args.dir = data->fh; + args.cookie = cookie; + memcpy(&args.cookieverf, res->READDIRPLUS3res_u.resok.cookieverf, sizeof(cookieverf3)); + args.dircount = 8192; + args.maxcount = 8192; + + if (rpc_nfs3_readdirplus_async(nfs->rpc, nfs_opendir_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIRPLUS call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); nfs_free_nfsdir(nfsdir); @@ -2259,6 +3268,9 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da return; } + if (res->READDIRPLUS3res_u.resok.dir_attributes.attributes_follow) + nfsdir->attr = res->READDIRPLUS3res_u.resok.dir_attributes.post_op_attr_u.attributes; + /* steal the dirhandle */ data->continue_data = NULL; nfsdir->current = nfsdir->entries; @@ -2267,12 +3279,41 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da free_nfs_cb_data(data); } -static int nfs_opendir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_opendir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { - cookieverf3 cv; + READDIRPLUS3args args; + struct nfsdir *nfsdir = data->continue_data;; + struct nfsdir *cached; + + cached = nfs_dircache_find(nfs, &data->fh); + if (cached) { + if (attr && attr->mtime.seconds == cached->attr.mtime.seconds) { + cached->current = cached->entries; + data->cb(0, nfs, cached, data->private_data); + free_nfs_cb_data(data); + return 0; + } else { + /* cache must be stale */ + nfs_free_nfsdir(cached); + } + } + + nfsdir->fh.data.data_len = data->fh.data.data_len; + nfsdir->fh.data.data_val = malloc(nfsdir->fh.data.data_len); + if (nfsdir->fh.data.data_val == NULL) { + rpc_set_error(nfs->rpc, "OOM when allocating fh for nfsdir"); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + memcpy(nfsdir->fh.data.data_val, data->fh.data.data_val, data->fh.data.data_len); - memset(cv, 0, sizeof(cookieverf3)); - if (rpc_nfs_readdirplus_async(nfs->rpc, nfs_opendir_cb, &data->fh, 0, (char *)&cv, 8192, data) != 0) { + args.dir = data->fh; + args.cookie = 0; + memset(&args.cookieverf, 0, sizeof(cookieverf3)); + args.dircount = 8192; + args.maxcount = 8192; + if (rpc_nfs3_readdirplus_async(nfs->rpc, nfs_opendir_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIRPLUS call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -2312,15 +3353,24 @@ struct nfsdirent *nfs_readdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir } -void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) +/* + * closedir() + */ +void nfs_closedir(struct nfs_context *nfs, struct nfsdir *nfsdir) { - nfs_free_nfsdir(nfsdir); + nfs_dircache_add(nfs, nfsdir); } - - - +/* + * getcwd() + */ +void nfs_getcwd(struct nfs_context *nfs, const char **cwd) +{ + if (cwd) { + *cwd = nfs->cwd; + } +} /* @@ -2329,7 +3379,7 @@ void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) struct lseek_cb_data { struct nfs_context *nfs; struct nfsfh *nfsfh; - uint64_t offset; + int64_t offset; nfs_cb cb; void *private_data; }; @@ -2339,6 +3389,7 @@ static void nfs_lseek_1_cb(struct rpc_context *rpc, int status, void *command_da GETATTR3res *res; struct lseek_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; + uint64_t size = 0; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -2361,23 +3412,41 @@ static void nfs_lseek_1_cb(struct rpc_context *rpc, int status, void *command_da return; } - data->nfsfh->offset = data->offset + res->GETATTR3res_u.resok.obj_attributes.size; - data->cb(0, nfs, &data->nfsfh->offset, data->private_data); + size = res->GETATTR3res_u.resok.obj_attributes.size; + + if (data->offset < 0 && + (uint64_t)(-data->offset) > size) { + data->cb(-EINVAL, nfs, &data->nfsfh->offset, data->private_data); + } else { + data->nfsfh->offset = data->offset + size; + data->cb(0, nfs, &data->nfsfh->offset, data->private_data); + } + free(data); } -int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, int whence, nfs_cb cb, void *private_data) +int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int64_t offset, int whence, nfs_cb cb, void *private_data) { struct lseek_cb_data *data; + struct GETATTR3args args; if (whence == SEEK_SET) { - nfsfh->offset = offset; - cb(0, nfs, &nfsfh->offset, private_data); + if (offset < 0) { + cb(-EINVAL, nfs, &nfsfh->offset, private_data); + } else { + nfsfh->offset = offset; + cb(0, nfs, &nfsfh->offset, private_data); + } return 0; } if (whence == SEEK_CUR) { - nfsfh->offset += offset; - cb(0, nfs, &nfsfh->offset, private_data); + if (offset < 0 && + nfsfh->offset < (uint64_t)(-offset)) { + cb(-EINVAL, nfs, &nfsfh->offset, private_data); + } else { + nfsfh->offset += offset; + cb(0, nfs, &nfsfh->offset, private_data); + } return 0; } @@ -2393,7 +3462,10 @@ int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse data->cb = cb; data->private_data = private_data; - if (rpc_nfs_getattr_async(nfs->rpc, nfs_lseek_1_cb, &nfsfh->fh, data) != 0) { + memset(&args, 0, sizeof(GETATTR3args)); + args.object = nfsfh->fh; + + if (rpc_nfs3_getattr_async(nfs->rpc, nfs_lseek_1_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send LSEEK GETATTR call"); free(data); return -1; @@ -2435,11 +3507,11 @@ static void nfs_statvfs_1_cb(struct rpc_context *rpc, int status, void *command_ return; } - svfs.f_bsize = 4096; - svfs.f_frsize = 4096; - svfs.f_blocks = res->FSSTAT3res_u.resok.tbytes/4096; - svfs.f_bfree = res->FSSTAT3res_u.resok.fbytes/4096; - svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/4096; + svfs.f_bsize = NFS_BLKSIZE; + svfs.f_frsize = NFS_BLKSIZE; + svfs.f_blocks = res->FSSTAT3res_u.resok.tbytes/NFS_BLKSIZE; + svfs.f_bfree = res->FSSTAT3res_u.resok.fbytes/NFS_BLKSIZE; + svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/NFS_BLKSIZE; svfs.f_files = res->FSSTAT3res_u.resok.tfiles; svfs.f_ffree = res->FSSTAT3res_u.resok.ffiles; #if !defined(ANDROID) @@ -2453,9 +3525,12 @@ static void nfs_statvfs_1_cb(struct rpc_context *rpc, int status, void *command_ free_nfs_cb_data(data); } -static int nfs_statvfs_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_statvfs_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { - if (rpc_nfs_fsstat_async(nfs->rpc, nfs_statvfs_1_cb, &data->fh, data) != 0) { + FSSTAT3args args; + + args.fsroot = data->fh; + if (rpc_nfs3_fsstat_async(nfs->rpc, nfs_statvfs_1_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send FSSTAT call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -2507,19 +3582,18 @@ static void nfs_readlink_1_cb(struct rpc_context *rpc, int status, void *command return; } - + data->cb(0, nfs, res->READLINK3res_u.resok.data, data->private_data); free_nfs_cb_data(data); } -static int nfs_readlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_readlink_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { READLINK3args args; - args.symlink.data.data_len = data->fh.data.data_len; - args.symlink.data.data_val = data->fh.data.data_val; + args.symlink = data->fh; - if (rpc_nfs_readlink_async(nfs->rpc, nfs_readlink_1_cb, &args, data) != 0) { + if (rpc_nfs3_readlink_async(nfs->rpc, nfs_readlink_1_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send READLINK call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -2575,17 +3649,16 @@ static void nfs_chmod_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_chmod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_chmod_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { SETATTR3args args; memset(&args, 0, sizeof(SETATTR3args)); - args.object.data.data_len = data->fh.data.data_len; - args.object.data.data_val = data->fh.data.data_val; + args.object = data->fh; args.new_attributes.mode.set_it = 1; args.new_attributes.mode.set_mode3_u.mode = data->continue_int; - if (rpc_nfs_setattr_async(nfs->rpc, nfs_chmod_cb, &args, data) != 0) { + if (rpc_nfs3_setattr_async(nfs->rpc, nfs_chmod_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -2631,8 +3704,7 @@ int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs } memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len); - if (nfs_chmod_continue_internal(nfs, data) != 0) { - free_nfs_cb_data(data); + if (nfs_chmod_continue_internal(nfs, NULL, data) != 0) { return -1; } @@ -2680,14 +3752,13 @@ struct nfs_chown_data { gid_t gid; }; -static int nfs_chown_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_chown_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { SETATTR3args args; struct nfs_chown_data *chown_data = data->continue_data; memset(&args, 0, sizeof(SETATTR3args)); - args.object.data.data_len = data->fh.data.data_len; - args.object.data.data_val = data->fh.data.data_val; + args.object = data->fh; if (chown_data->uid != (uid_t)-1) { args.new_attributes.uid.set_it = 1; args.new_attributes.uid.set_uid3_u.uid = chown_data->uid; @@ -2697,7 +3768,7 @@ static int nfs_chown_continue_internal(struct nfs_context *nfs, struct nfs_cb_da args.new_attributes.gid.set_gid3_u.gid = chown_data->gid; } - if (rpc_nfs_setattr_async(nfs->rpc, nfs_chown_cb, &args, data) != 0) { + if (rpc_nfs3_setattr_async(nfs->rpc, nfs_chown_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -2746,10 +3817,10 @@ int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int chown_data->uid = uid; chown_data->gid = gid; - data = malloc(sizeof(struct nfs_cb_data)); if (data == NULL) { rpc_set_error(nfs->rpc, "out of memory. failed to allocate memory for fchown data"); + free(chown_data); return -1; } memset(data, 0, sizeof(struct nfs_cb_data)); @@ -2757,6 +3828,7 @@ int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int data->cb = cb; data->private_data = private_data; data->continue_data = chown_data; + data->free_continue_data = free; data->fh.data.data_len = nfsfh->fh.data.data_len; data->fh.data.data_val = malloc(data->fh.data.data_len); if (data->fh.data.data_val == NULL) { @@ -2766,9 +3838,7 @@ int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int } memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len); - - if (nfs_chown_continue_internal(nfs, data) != 0) { - free_nfs_cb_data(data); + if (nfs_chown_continue_internal(nfs, NULL, data) != 0) { return -1; } @@ -2813,14 +3883,13 @@ static void nfs_utimes_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_utimes_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_utimes_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { SETATTR3args args; struct timeval *utimes_data = data->continue_data; memset(&args, 0, sizeof(SETATTR3args)); - args.object.data.data_len = data->fh.data.data_len; - args.object.data.data_val = data->fh.data.data_val; + args.object = data->fh; if (utimes_data != NULL) { args.new_attributes.atime.set_it = SET_TO_CLIENT_TIME; args.new_attributes.atime.set_atime_u.atime.seconds = utimes_data[0].tv_sec; @@ -2833,7 +3902,7 @@ static int nfs_utimes_continue_internal(struct nfs_context *nfs, struct nfs_cb_d args.new_attributes.mtime.set_it = SET_TO_SERVER_TIME; } - if (rpc_nfs_setattr_async(nfs->rpc, nfs_utimes_cb, &args, data) != 0) { + if (rpc_nfs3_setattr_async(nfs->rpc, nfs_utimes_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -2952,9 +4021,10 @@ static void nfs_access_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_access_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_access_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { int nfsmode = 0; + ACCESS3args args; if (data->continue_int & R_OK) { nfsmode |= ACCESS3_READ; @@ -2966,7 +4036,11 @@ static int nfs_access_continue_internal(struct nfs_context *nfs, struct nfs_cb_d nfsmode |= ACCESS3_EXECUTE; } - if (rpc_nfs_access_async(nfs->rpc, nfs_access_cb, &data->fh, nfsmode, data) != 0) { + memset(&args, 0, sizeof(ACCESS3args)); + args.object = data->fh; + args.access = nfsmode; + + if (rpc_nfs3_access_async(nfs->rpc, nfs_access_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); @@ -3018,7 +4092,7 @@ static void nfs_symlink_cb(struct rpc_context *rpc, int status, void *command_da struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; struct nfs_symlink_data *symlink_data = data->continue_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status == RPC_STATUS_ERROR) { @@ -3044,20 +4118,19 @@ static void nfs_symlink_cb(struct rpc_context *rpc, int status, void *command_da free_nfs_cb_data(data); } -static int nfs_symlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_symlink_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_symlink_data *symlink_data = data->continue_data; - SYMLINK3args sa; + SYMLINK3args args; - memset(&sa, 0, sizeof(SYMLINK3args)); - sa.where.dir.data.data_len = data->fh.data.data_len; - sa.where.dir.data.data_val = data->fh.data.data_val; - sa.where.name = symlink_data->newpathobject; - sa.symlink.symlink_attributes.mode.set_it = 1; - sa.symlink.symlink_attributes.mode.set_mode3_u.mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH; - sa.symlink.symlink_data = symlink_data->oldpath; + memset(&args, 0, sizeof(SYMLINK3args)); + args.where.dir = data->fh; + args.where.name = symlink_data->newpathobject; + args.symlink.symlink_attributes.mode.set_it = 1; + args.symlink.symlink_attributes.mode.set_mode3_u.mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH; + args.symlink.symlink_data = symlink_data->oldpath; - if (rpc_nfs_symlink_async(nfs->rpc, nfs_symlink_cb, &sa, data) != 0) { + if (rpc_nfs3_symlink_async(nfs->rpc, nfs_symlink_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send SYMLINK call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -3155,7 +4228,7 @@ static void nfs_rename_cb(struct rpc_context *rpc, int status, void *command_dat struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; struct nfs_rename_data *rename_data = data->continue_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status == RPC_STATUS_ERROR) { @@ -3181,16 +4254,20 @@ static void nfs_rename_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_rename_continue_2_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_rename_data *rename_data = data->continue_data; + RENAME3args args; /* steal the filehandle */ - rename_data->newdir.data.data_len = data->fh.data.data_len; - rename_data->newdir.data.data_val = data->fh.data.data_val; + rename_data->newdir = data->fh; data->fh.data.data_val = NULL; - if (rpc_nfs_rename_async(nfs->rpc, nfs_rename_cb, &rename_data->olddir, rename_data->oldobject, &rename_data->newdir, rename_data->newobject, data) != 0) { + args.from.dir = rename_data->olddir; + args.from.name = rename_data->oldobject; + args.to.dir = rename_data->newdir; + args.to.name = rename_data->newobject; + if (rpc_nfs3_rename_async(nfs->rpc, nfs_rename_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send RENAME call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -3200,23 +4277,31 @@ static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb } -static int nfs_rename_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_rename_continue_1_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_rename_data *rename_data = data->continue_data; + char* newpath = strdup(rename_data->newpath); + if (!newpath) { + rpc_set_error(nfs->rpc, "Out of memory. Could not allocate memory to store target path for rename"); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } /* steal the filehandle */ - rename_data->olddir.data.data_len = data->fh.data.data_len; - rename_data->olddir.data.data_val = data->fh.data.data_val; + rename_data->olddir = data->fh; data->fh.data.data_val = NULL; if (nfs_lookuppath_async(nfs, rename_data->newpath, data->cb, data->private_data, nfs_rename_continue_2_internal, rename_data, free_nfs_rename_data, 0) != 0) { - rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", rename_data->newpath); + rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", newpath); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); + free(newpath); return -1; } data->continue_data = NULL; free_nfs_cb_data(data); + free(newpath); return 0; } @@ -3313,7 +4398,7 @@ static void nfs_link_cb(struct rpc_context *rpc, int status, void *command_data, struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; struct nfs_link_data *link_data = data->continue_data; - + assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status == RPC_STATUS_ERROR) { @@ -3339,16 +4424,20 @@ static void nfs_link_cb(struct rpc_context *rpc, int status, void *command_data, free_nfs_cb_data(data); } -static int nfs_link_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_link_continue_2_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_link_data *link_data = data->continue_data; + LINK3args args; /* steal the filehandle */ - link_data->newdir.data.data_len = data->fh.data.data_len; - link_data->newdir.data.data_val = data->fh.data.data_val; + link_data->newdir = data->fh; data->fh.data.data_val = NULL; - if (rpc_nfs_link_async(nfs->rpc, nfs_link_cb, &link_data->oldfh, &link_data->newdir, link_data->newobject, data) != 0) { + memset(&args, 0, sizeof(LINK3args)); + args.file = link_data->oldfh; + args.link.dir = link_data->newdir; + args.link.name = link_data->newobject; + if (rpc_nfs3_link_async(nfs->rpc, nfs_link_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send LINK call for %s", data->path); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -3358,13 +4447,12 @@ static int nfs_link_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_d } -static int nfs_link_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_link_continue_1_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_link_data *link_data = data->continue_data; /* steal the filehandle */ - link_data->oldfh.data.data_len = data->fh.data.data_len; - link_data->oldfh.data.data_val = data->fh.data.data_val; + link_data->oldfh = data->fh; data->fh.data.data_val = NULL; if (nfs_lookuppath_async(nfs, link_data->newpath, data->cb, data->private_data, nfs_link_continue_2_internal, link_data, free_nfs_link_data, 0) != 0) { @@ -3446,11 +4534,23 @@ uint64_t nfs_get_readmax(struct nfs_context *nfs) */ uint64_t nfs_get_writemax(struct nfs_context *nfs) { - /* Some ZDR libraries can not marshall PDUs bigger than this */ - if (nfs->writemax < 32768) { - return nfs->writemax; - } - return 32768; + return nfs->writemax; +} + +void nfs_set_tcp_syncnt(struct nfs_context *nfs, int v) { + rpc_set_tcp_syncnt(nfs->rpc, v); +} + +void nfs_set_uid(struct nfs_context *nfs, int uid) { + rpc_set_uid(nfs->rpc, uid); +} + +void nfs_set_gid(struct nfs_context *nfs, int gid) { + rpc_set_gid(nfs->rpc, gid); +} + +void nfs_set_readahead(struct nfs_context *nfs, uint32_t v) { + rpc_set_readahead(nfs->rpc, v); } void nfs_set_error(struct nfs_context *nfs, char *error_string, ...) @@ -3465,7 +4565,7 @@ void nfs_set_error(struct nfs_context *nfs, char *error_string, ...) free(nfs->rpc->error_string); } nfs->rpc->error_string = str; - va_end(ap); + va_end(ap); } @@ -3519,91 +4619,6 @@ static void mount_export_4_cb(struct rpc_context *rpc, int status, void *command /* Dont want any more callbacks even if the socket is closed */ rpc->connect_cb = NULL; - if (status == RPC_STATUS_ERROR) { - data->cb(rpc, -EFAULT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(rpc, -EINTR, "Command was cancelled", data->private_data); - free_mount_cb_data(data); - return; - } - - if (rpc_mount_export_async(rpc, mount_export_5_cb, data) != 0) { - data->cb(rpc, -ENOMEM, command_data, data->private_data); - free_mount_cb_data(data); - return; - } -} - -static void mount_export_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct mount_cb_data *data = private_data; - uint32_t mount_port; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - if (status == RPC_STATUS_ERROR) { - data->cb(rpc, -EFAULT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(rpc, -EINTR, "Command was cancelled", data->private_data); - free_mount_cb_data(data); - return; - } - - mount_port = *(uint32_t *)command_data; - if (mount_port == 0) { - rpc_set_error(rpc, "RPC error. Mount program is not available"); - data->cb(rpc, -ENOENT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - - rpc_disconnect(rpc, "normal disconnect"); - if (rpc_connect_async(rpc, data->server, mount_port, mount_export_4_cb, data) != 0) { - data->cb(rpc, -ENOMEM, command_data, data->private_data); - free_mount_cb_data(data); - return; - } -} - -static void mount_export_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct mount_cb_data *data = private_data; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - if (status == RPC_STATUS_ERROR) { - data->cb(rpc, -EFAULT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(rpc, -EINTR, "Command was cancelled", data->private_data); - free_mount_cb_data(data); - return; - } - - if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, IPPROTO_TCP, mount_export_3_cb, private_data) != 0) { - data->cb(rpc, -ENOMEM, command_data, data->private_data); - free_mount_cb_data(data); - return; - } -} - -static void mount_export_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct mount_cb_data *data = private_data; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - if (status == RPC_STATUS_ERROR) { data->cb(rpc, -EFAULT, command_data, data->private_data); free_mount_cb_data(data); @@ -3615,7 +4630,7 @@ static void mount_export_1_cb(struct rpc_context *rpc, int status, void *command return; } - if (rpc_pmap_null_async(rpc, mount_export_2_cb, data) != 0) { + if (rpc_mount3_export_async(rpc, mount_export_5_cb, data) != 0) { data->cb(rpc, -ENOMEM, command_data, data->private_data); free_mount_cb_data(data); return; @@ -3639,8 +4654,9 @@ int mount_getexports_async(struct rpc_context *rpc, const char *server, rpc_cb c if (data->server == NULL) { free_mount_cb_data(data); return -1; - } - if (rpc_connect_async(rpc, data->server, 111, mount_export_1_cb, data) != 0) { + } + if (rpc_connect_program_async(rpc, data->server, MOUNT_PROGRAM, MOUNT_V3, mount_export_4_cb, data) != 0) { + rpc_set_error(rpc, "Failed to start connection"); free_mount_cb_data(data); return -1; }