X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=lib%2Flibnfs.c;h=bb89d4a8c10bb34fd541801af242d7d3704ce1f6;hb=ee872606f1e484f684276ec595f1e39d1f95b9a9;hp=e2f96722b32e0f28d73007cb9f8f2466b2614254;hpb=fab61e3d0384e61553b39860b923d41bc0cf17b1;p=deb_libnfs.git diff --git a/lib/libnfs.c b/lib/libnfs.c index e2f9672..bb89d4a 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -17,26 +17,58 @@ /* * High level api to nfs filesystems */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef AROS +#include "aros_compat.h" +#endif + #ifdef WIN32 #include "win32_compat.h" -#define DllExport -#else -#include -#include +#endif + +#ifdef HAVE_UTIME_H #include -#include -#endif/*WIN32*/ +#endif + +#ifdef ANDROID +#define statvfs statfs +#endif #define _GNU_SOURCE +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_VFS_H +#include +#endif + +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif + #include #include #include #include +#include #include #include #include #include +#include "libnfs-zdr.h" #include "libnfs.h" #include "libnfs-raw.h" #include "libnfs-raw-mount.h" @@ -61,6 +93,7 @@ struct nfs_context { struct nfs_fh3 rootfh; uint64_t readmax; uint64_t writemax; + char *cwd; }; void nfs_free_nfsdir(struct nfsdir *nfsdir) @@ -97,9 +130,12 @@ struct nfs_cb_data { /* for multi-read/write calls. */ int error; int cancel; + int oom; int num_calls; uint64_t start_offset, max_offset; char *buffer; + size_t request_size; + char *usrbuf; }; struct nfs_mcb_data { @@ -110,8 +146,7 @@ struct nfs_mcb_data { static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh); - -void nfs_set_auth(struct nfs_context *nfs, AUTH *auth) +void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth) { rpc_set_auth(nfs->rpc, auth); } @@ -141,6 +176,159 @@ 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 (!strncmp(arg, "tcp-syncnt", 10)) { + rpc_set_tcp_syncnt(nfs_get_rpc_context(nfs), atoi(val)); + } else if (!strncmp(arg, "uid", 3)) { + rpc_set_uid(nfs_get_rpc_context(nfs), atoi(val)); + } else if (!strncmp(arg, "gid", 3)) { + rpc_set_gid(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; @@ -149,17 +337,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; } @@ -179,6 +365,11 @@ 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; @@ -187,27 +378,165 @@ void nfs_destroy_context(struct nfs_context *nfs) free(nfs); } -void free_nfs_cb_data(struct nfs_cb_data *data) +struct rpc_cb_data { + char *server; + uint32_t program; + uint32_t version; + + rpc_cb cb; + void *private_data; +}; + +void free_rpc_cb_data(struct rpc_cb_data *data) { - if (data->saved_path != NULL) { - free(data->saved_path); - data->saved_path = NULL; + free(data->server); + data->server = NULL; + free(data); +} + +static void rpc_connect_program_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_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(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); + return; } - if (data->continue_data != NULL) { - data->free_continue_data(data->continue_data); - data->continue_data = NULL; + data->cb(rpc, status, NULL, data->private_data); + free_rpc_cb_data(data); +} + +static void rpc_connect_program_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct rpc_cb_data *data = private_data; + uint32_t rpc_port; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (status == RPC_STATUS_ERROR) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); + return; + } + + rpc_port = *(uint32_t *)command_data; + 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 rpc_connect_program_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct rpc_cb_data *data = private_data; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (status == RPC_STATUS_ERROR) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); + return; } - if (data->fh.data.data_val != NULL) { - free(data->fh.data.data_val); - data->fh.data.data_val = NULL; + if (rpc_pmap_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; } +} + +static void rpc_connect_program_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_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 (data->buffer != NULL) { - free(data->buffer); - data->buffer = NULL; + if (status == RPC_STATUS_ERROR) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; } + if (status == RPC_STATUS_CANCEL) { + data->cb(rpc, status, "Command was cancelled", data->private_data); + free_rpc_cb_data(data); + return; + } + + if (rpc_pmap_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; + } +} + +int rpc_connect_program_async(struct rpc_context *rpc, 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; + } + memset(data, 0, sizeof(struct rpc_cb_data)); + data->server = strdup(server); + data->program = program; + data->version = version; + + 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; + } + 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); + } + + free(data->saved_path); + free(data->fh.data.data_val); + free(data->buffer); free(data); } @@ -216,11 +545,13 @@ void free_nfs_cb_data(struct nfs_cb_data *data) -static void nfs_mount_10_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_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; + 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); @@ -241,6 +572,9 @@ static void nfs_mount_9_cb(struct rpc_context *rpc, int status, void *command_da 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); if (status == RPC_STATUS_ERROR) { data->cb(-EFAULT, nfs, command_data, data->private_data); @@ -256,7 +590,10 @@ static void nfs_mount_9_cb(struct rpc_context *rpc, int status, void *command_da 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) { + 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; @@ -267,6 +604,9 @@ static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_da { struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; + struct FSINFO3args args; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status == RPC_STATUS_ERROR) { data->cb(-EFAULT, nfs, command_data, data->private_data); @@ -279,7 +619,8 @@ static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_da return; } - if (rpc_nfs_fsinfo_async(rpc, nfs_mount_9_cb, &nfs->rootfh, 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; @@ -292,6 +633,8 @@ static void nfs_mount_7_cb(struct rpc_context *rpc, int status, void *command_da 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; @@ -306,7 +649,7 @@ static void nfs_mount_7_cb(struct rpc_context *rpc, int status, void *command_da return; } - if (rpc_nfs_null_async(rpc, nfs_mount_8_cb, data) != 0) { + if (rpc_nfs3_null_async(rpc, nfs_mount_8_cb, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; @@ -320,6 +663,8 @@ static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_da struct nfs_context *nfs = data->nfs; mountres3 *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); @@ -365,6 +710,8 @@ static void nfs_mount_5_cb(struct rpc_context *rpc, int status, void *command_da struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; + 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); @@ -376,7 +723,7 @@ 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) { + 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; @@ -388,6 +735,8 @@ static void nfs_mount_4_cb(struct rpc_context *rpc, int status, void *command_da 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; @@ -402,7 +751,7 @@ 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) { + if (rpc_mount3_null_async(rpc, nfs_mount_5_cb, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; @@ -415,7 +764,9 @@ static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_da struct nfs_context *nfs = data->nfs; uint32_t mount_port; - if (status == RPC_STATUS_ERROR) { + 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; @@ -448,6 +799,8 @@ static void nfs_mount_2_cb(struct rpc_context *rpc, int status, void *command_da struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; + 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); @@ -471,6 +824,8 @@ static void nfs_mount_1_cb(struct rpc_context *rpc, int status, void *command_da 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; @@ -535,12 +890,14 @@ int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *exp * Functions to first look up a path, component by component, and then finally call a specific function once * the filehandle for the final component is found. */ -static void nfs_lookup_path_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_lookup_path_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; LOOKUP3res *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); @@ -570,17 +927,24 @@ static void nfs_lookup_path_1_cb(struct rpc_context *rpc _U_, int status, void * static int nfs_lookup_path_async_internal(struct nfs_context *nfs, 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++; @@ -597,31 +961,144 @@ 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); + if (slash != NULL) { + *slash = '/'; + } data->continue_cb(nfs, 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_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) +static int nfs_normalize_path(struct nfs_context *nfs, char *path) { - struct nfs_cb_data *data; + 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, "Pathname is not absulute %s", path); + 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 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; + + 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"); + 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)); @@ -632,12 +1109,29 @@ static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_c data->free_continue_data = free_continue_data; data->continue_int = continue_int; data->private_data = private_data; - data->saved_path = strdup(path); + 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 (nfs_lookup_path_async_internal(nfs, data, &nfs->rootfh) != 0) { @@ -654,12 +1148,18 @@ static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_c /* * Async stat() */ -static void nfs_stat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +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); @@ -697,7 +1197,7 @@ static void nfs_stat_1_cb(struct rpc_context *rpc _U_, int status, void *command #ifndef WIN32 st.st_blksize = 4096; st.st_blocks = res->GETATTR3res_u.resok.obj_attributes.size / 4096; -#endif//WIN32 +#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; @@ -708,7 +1208,12 @@ static void nfs_stat_1_cb(struct rpc_context *rpc _U_, int status, void *command static int nfs_stat_continue_internal(struct nfs_context *nfs, 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_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); @@ -728,13 +1233,138 @@ int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *p } +/* + * Async nfs_stat64() + */ +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 nfs_stat_64 st; + + 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.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.nfs_mode |= S_IFDIR ; + } + if (res->GETATTR3res_u.resok.obj_attributes.type == NF3REG) { + st.nfs_mode |= S_IFREG ; + } + 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_stat64_continue_internal(struct nfs_context *nfs, 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_stat64_1_cb, &args, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +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_stat64_continue_internal, NULL, NULL, 0) != 0) { + rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); + return -1; + } + + return 0; +} + +/* + * Async 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; + } + /* 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 _U_, int status, void *command_data, void *private_data) +static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { ACCESS3res *res; struct nfs_cb_data *data = private_data; @@ -742,6 +1372,8 @@ static void nfs_open_cb(struct rpc_context *rpc _U_, int status, void *command_d struct nfsfh *nfsfh; unsigned int nfsmode = 0; + 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); @@ -785,6 +1417,28 @@ static void nfs_open_cb(struct rpc_context *rpc _U_, int status, void *command_d 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"); @@ -799,8 +1453,7 @@ static void nfs_open_cb(struct rpc_context *rpc _U_, int status, void *command_d } /* 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); @@ -810,6 +1463,7 @@ static void nfs_open_cb(struct rpc_context *rpc _U_, int status, void *command_d static int nfs_open_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) { int nfsmode = 0; + ACCESS3args args; if (data->continue_int & O_WRONLY) { nfsmode |= ACCESS3_MODIFY; @@ -821,18 +1475,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; } @@ -841,49 +1501,53 @@ 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 _U_, int status, void *command_data, void *private_data) +static int nfs_chdir_continue_internal(struct nfs_context *nfs, 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; - 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; - } + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); - 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; + return 0; +} + +int nfs_chdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chdir_continue_internal, NULL, NULL, 0) != 0) { + rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); + return -1; } - 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; } -static void nfs_pread_mcb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) + +/* + * Async pread() + */ +static void nfs_fill_READ3args(READ3args *args, struct nfsfh *fh, uint64_t offset, uint64_t count) +{ + memset(args, 0, sizeof(READ3args)); + args->file = fh->fh; + args->offset = offset; + args->count = count; +} + +static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { struct nfs_mcb_data *mdata = private_data; struct nfs_cb_data *data = mdata->data; struct nfs_context *nfs = data->nfs; READ3res *res; + assert(rpc->magic == RPC_CONTEXT_MAGIC); + data->num_calls--; if (status == RPC_STATUS_ERROR) { @@ -895,46 +1559,89 @@ static void nfs_pread_mcb(struct rpc_context *rpc _U_, int status, void *command 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 we have more than one call or we have received a short read we need a reassembly buffer */ + if (data->num_calls || (res->READ3res_u.resok.count < mdata->count && !res->READ3res_u.resok.eof)) { + if (data->buffer == NULL) { + data->buffer = malloc(data->request_size); + if (data->buffer == NULL) { + rpc_set_error(nfs->rpc, "Out-Of-Memory: Failed to allocate reassembly buffer for %d bytes", (int)data->request_size); + data->oom = 1; + } + } + } if (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; + if (res->READ3res_u.resok.count <= mdata->count) { + /* copy data into reassembly buffer if we have one */ + if (data->buffer != NULL) { + memcpy(&data->buffer[mdata->offset - data->start_offset], res->READ3res_u.resok.data.data_val, res->READ3res_u.resok.count); + } + if (data->max_offset < mdata->offset + res->READ3res_u.resok.count) { + data->max_offset = mdata->offset + res->READ3res_u.resok.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 (res->READ3res_u.resok.count < mdata->count && !res->READ3res_u.resok.eof) { + if (res->READ3res_u.resok.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 += res->READ3res_u.resok.count; + mdata->count -= res->READ3res_u.resok.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) { + data->cb(data->max_offset - data->start_offset, nfs, data->buffer, data->private_data); + } else { + data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data); + } free_nfs_cb_data(data); - 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) @@ -951,37 +1658,22 @@ 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->request_size = count; nfsfh->offset = offset; - 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); - free_nfs_cb_data(data); - return -1; - } - return 0; - } + assert(data->num_calls == 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. + /* chop requests into chunks of at most READMAX bytes if necessary. + * we send all reads in parallel so that performance is still good. */ data->max_offset = offset; data->start_offset = offset; - 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; - } - - while (count > 0) { + do { uint64_t readcount = count; struct nfs_mcb_data *mdata; + READ3args args; if (readcount > nfs_get_readmax(nfs)) { readcount = nfs_get_readmax(nfs); @@ -990,23 +1682,35 @@ 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) { + + 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; } @@ -1024,43 +1728,27 @@ 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 _U_, 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; - - 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 _U_, int status, void *command_data, void *private_data) +static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { struct nfs_mcb_data *mdata = private_data; struct nfs_cb_data *data = mdata->data; struct nfs_context *nfs = data->nfs; WRITE3res *res; + assert(rpc->magic == RPC_CONTEXT_MAGIC); + data->num_calls--; if (status == RPC_STATUS_ERROR) { @@ -1078,30 +1766,53 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc _U_, int status, void *comman 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 < mdata->count) { + if (res->WRITE3res_u.resok.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 += res->WRITE3res_u.resok.count; + mdata->count -= res->WRITE3res_u.resok.count; + nfs_fill_WRITE3args(&args, data->nfsfh, mdata->offset, mdata->count, + &data->usrbuf[mdata->offset - data->start_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 (res->WRITE3res_u.resok.count > 0) { - if ((unsigned)data->max_offset < mdata->offset + res->WRITE3res_u.resok.count) { + if (data->max_offset < mdata->offset + res->WRITE3res_u.resok.count) { data->max_offset = mdata->offset + res->WRITE3res_u.resok.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; } @@ -1109,7 +1820,6 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc _U_, int status, void *comman data->cb(data->max_offset - data->start_offset, nfs, NULL, data->private_data); free_nfs_cb_data(data); - free(mdata); } @@ -1127,29 +1837,23 @@ 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; - 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); @@ -1158,24 +1862,35 @@ 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; - 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->start_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; } @@ -1194,7 +1909,7 @@ 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){ @@ -1217,6 +1932,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) { @@ -1228,7 +1944,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); @@ -1242,12 +1961,14 @@ int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, voi /* * Async fsync() */ -static void nfs_fsync_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_fsync_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; COMMIT3res *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); @@ -1274,6 +1995,7 @@ static void nfs_fsync_cb(struct rpc_context *rpc _U_, int status, void *command_ 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) { @@ -1285,7 +2007,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); @@ -1300,12 +2025,14 @@ int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, voi /* * Async ftruncate() */ -static void nfs_ftruncate_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_ftruncate_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; 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); @@ -1345,12 +2072,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); @@ -1368,8 +2094,7 @@ static int nfs_truncate_continue_internal(struct nfs_context *nfs, struct nfs_cb 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); @@ -1401,13 +2126,15 @@ int nfs_truncate_async(struct nfs_context *nfs, const char *path, uint64_t lengt /* * Async mkdir() */ -static void nfs_mkdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_mkdir_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { MKDIR3res *res; 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]; if (status == RPC_STATUS_ERROR) { @@ -1441,13 +2168,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); @@ -1469,6 +2195,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; } @@ -1490,13 +2217,15 @@ int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void * /* * Async rmdir() */ -static void nfs_rmdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_rmdir_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { RMDIR3res *res; 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]; if (status == RPC_STATUS_ERROR) { @@ -1525,10 +2254,13 @@ static void nfs_rmdir_cb(struct rpc_context *rpc _U_, int status, void *command_ static int nfs_rmdir_continue_internal(struct nfs_context *nfs, 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); @@ -1550,6 +2282,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; } @@ -1570,7 +2303,7 @@ int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void * /* * Async creat() */ -static void nfs_create_2_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_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; @@ -1578,6 +2311,8 @@ static void nfs_create_2_cb(struct rpc_context *rpc _U_, int status, void *comma struct nfsfh *nfsfh; char *str = data->continue_data; + 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); @@ -1607,10 +2342,10 @@ static void nfs_create_2_cb(struct rpc_context *rpc _U_, int status, void *comma } memset(nfsfh, 0, sizeof(struct nfsfh)); - /* steal the filehandle */ - nfsfh->fh.data.data_len = data->fh.data.data_len; - nfsfh->fh.data.data_val = data->fh.data.data_val; - data->fh.data.data_val = NULL; + /* 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); + memcpy(nfsfh->fh.data.data_val, res->LOOKUP3res_u.resok.object.data.data_val, nfsfh->fh.data.data_len); data->cb(0, nfs, nfsfh, data->private_data); free_nfs_cb_data(data); @@ -1618,12 +2353,15 @@ static void nfs_create_2_cb(struct rpc_context *rpc _U_, int status, void *comma -static void nfs_creat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_creat_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; + LOOKUP3args args; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); if (status == RPC_STATUS_ERROR) { data->cb(-EFAULT, nfs, command_data, data->private_data); @@ -1641,11 +2379,15 @@ static void nfs_creat_1_cb(struct rpc_context *rpc _U_, int status, void *comman 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); @@ -1661,16 +2403,14 @@ static int nfs_creat_continue_internal(struct nfs_context *nfs, struct nfs_cb_da 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.createhow3_u.obj_attributes.mode.set_it = 1; args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = data->continue_int; - if (rpc_nfs_create_async(nfs->rpc, nfs_creat_1_cb, &args, data) != 0) { + if (rpc_nfs3_create_async(nfs->rpc, nfs_creat_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); @@ -1693,11 +2433,12 @@ int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb ptr = strrchr(new_path, '/'); if (ptr == NULL) { rpc_set_error(nfs->rpc, "Invalid path %s", path); + free(new_path); return -1; } *ptr = 0; - /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */ + /* 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) { rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); return -1; @@ -1712,13 +2453,15 @@ int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb /* * Async unlink() */ -static void nfs_unlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_unlink_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { REMOVE3res *res; 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]; if (status == RPC_STATUS_ERROR) { @@ -1747,10 +2490,13 @@ static void nfs_unlink_cb(struct rpc_context *rpc _U_, int status, void *command static int nfs_unlink_continue_internal(struct nfs_context *nfs, 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); @@ -1772,6 +2518,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; } @@ -1805,13 +2552,15 @@ static void free_mknod_cb_data(void *ptr) free(data); } -static void nfs_mknod_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_mknod_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { MKNOD3res *res; 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]; if (status == RPC_STATUS_ERROR) { @@ -1841,10 +2590,44 @@ static int nfs_mknod_continue_internal(struct nfs_context *nfs, struct nfs_cb_da { 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; @@ -1866,13 +2649,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; @@ -1884,7 +2668,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; } @@ -1909,7 +2692,7 @@ struct rdpe_lookup_cb_data { }; /* Workaround for servers lacking READDIRPLUS, use READDIR instead and a GETATTR-loop */ -static void nfs_opendir3_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_opendir3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { LOOKUP3res *res = command_data; struct rdpe_lookup_cb_data *rdpe_lookup_cb_data = private_data; @@ -1919,19 +2702,22 @@ static void nfs_opendir3_cb(struct rpc_context *rpc _U_, int status, void *comma struct nfs_context *nfs = data->nfs; struct nfsdirent *nfsdirent = rdpe_lookup_cb_data->nfsdirent; + assert(rpc->magic == RPC_CONTEXT_MAGIC); + free(rdpe_lookup_cb_data); 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; @@ -1946,12 +2732,17 @@ static void nfs_opendir3_cb(struct rpc_context *rpc _U_, int status, void *comma 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; } } 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); @@ -1963,7 +2754,7 @@ static void nfs_opendir3_cb(struct rpc_context *rpc _U_, int status, void *comma } } -static void nfs_opendir2_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { READDIR3res *res = command_data; struct nfs_cb_data *data = private_data; @@ -1971,9 +2762,11 @@ static void nfs_opendir2_cb(struct rpc_context *rpc _U_, int status, void *comma 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) { data->cb(-EFAULT, nfs, command_data, data->private_data); nfs_free_nfsdir(nfsdir); @@ -2013,6 +2806,7 @@ static void nfs_opendir2_cb(struct rpc_context *rpc _U_, int status, void *comma 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); @@ -2028,7 +2822,14 @@ static void nfs_opendir2_cb(struct rpc_context *rpc _U_, int status, void *comma } 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); @@ -2048,18 +2849,26 @@ static void nfs_opendir2_cb(struct rpc_context *rpc _U_, int status, void *comma 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; - if (rpc_nfs_lookup_async(nfs->rpc, nfs_opendir3_cb, &data->fh, nfsdirent->name, rdpe_lookup_cb_data) != 0) { + 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; @@ -2077,21 +2886,26 @@ static void nfs_opendir2_cb(struct rpc_context *rpc _U_, int status, void *comma } } - -static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { READDIRPLUS3res *res = command_data; struct nfs_cb_data *data = private_data; 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; + + args.dir = data->fh; + args.cookie = cookie; + memset(&args.cookieverf, 0, sizeof(cookieverf3)); + args.count = 8192; - if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir2_cb, &data->fh, 0, (char *)&cv, 8192, data) != 0) { + 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); @@ -2135,6 +2949,7 @@ static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *comman 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); @@ -2152,6 +2967,8 @@ static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *comman 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->next = nfsdir->entries; @@ -2162,7 +2979,15 @@ static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *comman } 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); @@ -2183,10 +3008,14 @@ static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *comman static int nfs_opendir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) { - cookieverf3 cv; - - memset(cv, 0, sizeof(cookieverf3)); - if (rpc_nfs_readdirplus_async(nfs->rpc, nfs_opendir_cb, &data->fh, 0, (char *)&cv, 8192, data) != 0) { + READDIRPLUS3args args; + + 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); @@ -2226,15 +3055,24 @@ struct nfsdirent *nfs_readdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir } +/* + * closedir() + */ void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) { nfs_free_nfsdir(nfsdir); } - - - +/* + * getcwd() + */ +void nfs_getcwd(struct nfs_context *nfs, const char **cwd) +{ + if (cwd) { + *cwd = nfs->cwd; + } +} /* @@ -2248,12 +3086,14 @@ struct lseek_cb_data { void *private_data; }; -static void nfs_lseek_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_lseek_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { GETATTR3res *res; struct lseek_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; + assert(rpc->magic == RPC_CONTEXT_MAGIC); + if (status == RPC_STATUS_ERROR) { data->cb(-EFAULT, nfs, command_data, data->private_data); free(data); @@ -2281,6 +3121,7 @@ static void nfs_lseek_1_cb(struct rpc_context *rpc _U_, int status, void *comman int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_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; @@ -2305,7 +3146,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; @@ -2319,13 +3163,15 @@ int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse /* * Async statvfs() */ -static void nfs_statvfs_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_statvfs_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { FSSTAT3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; struct statvfs svfs; + 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); @@ -2352,10 +3198,12 @@ static void nfs_statvfs_1_cb(struct rpc_context *rpc _U_, int status, void *comm svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/4096; svfs.f_files = res->FSSTAT3res_u.resok.tfiles; svfs.f_ffree = res->FSSTAT3res_u.resok.ffiles; +#if !defined(ANDROID) svfs.f_favail = res->FSSTAT3res_u.resok.afiles; svfs.f_fsid = 0; svfs.f_flag = 0; svfs.f_namemax = 256; +#endif data->cb(0, nfs, &svfs, data->private_data); free_nfs_cb_data(data); @@ -2363,7 +3211,10 @@ static void nfs_statvfs_1_cb(struct rpc_context *rpc _U_, int status, void *comm static int nfs_statvfs_continue_internal(struct nfs_context *nfs, 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); @@ -2388,12 +3239,14 @@ int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void /* * Async readlink() */ -static void nfs_readlink_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_readlink_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { READLINK3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; + 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); @@ -2413,7 +3266,7 @@ static void nfs_readlink_1_cb(struct rpc_context *rpc _U_, int status, void *com return; } - + data->cb(0, nfs, res->READLINK3res_u.resok.data, data->private_data); free_nfs_cb_data(data); } @@ -2422,10 +3275,9 @@ static int nfs_readlink_continue_internal(struct nfs_context *nfs, struct nfs_cb { 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); @@ -2450,12 +3302,14 @@ int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, voi /* * Async chmod() */ -static void nfs_chmod_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_chmod_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; 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); @@ -2484,12 +3338,11 @@ static int nfs_chmod_continue_internal(struct nfs_context *nfs, struct nfs_cb_da 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); @@ -2536,7 +3389,6 @@ 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); return -1; } @@ -2548,12 +3400,14 @@ int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs /* * Async chown() */ -static void nfs_chown_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_chown_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; 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); @@ -2588,8 +3442,7 @@ static int nfs_chown_continue_internal(struct nfs_context *nfs, struct nfs_cb_da 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; @@ -2599,7 +3452,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); @@ -2648,10 +3501,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)); @@ -2659,6 +3512,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) { @@ -2668,9 +3522,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); return -1; } @@ -2684,12 +3536,14 @@ int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int /* * Async utimes() */ -static void nfs_utimes_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_utimes_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; 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); @@ -2719,8 +3573,7 @@ static int nfs_utimes_continue_internal(struct nfs_context *nfs, struct nfs_cb_d 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; @@ -2733,7 +3586,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); @@ -2794,19 +3647,18 @@ int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *t } - - - /* * Async access() */ -static void nfs_access_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_access_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { ACCESS3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; unsigned int nfsmode = 0; + 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); @@ -2856,6 +3708,7 @@ static void nfs_access_cb(struct rpc_context *rpc _U_, int status, void *command static int nfs_access_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) { int nfsmode = 0; + ACCESS3args args; if (data->continue_int & R_OK) { nfsmode |= ACCESS3_READ; @@ -2867,7 +3720,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); @@ -2913,13 +3770,15 @@ static void free_nfs_symlink_data(void *mem) free(data); } -static void nfs_symlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_symlink_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { SYMLINK3res *res; 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) { data->cb(-EFAULT, nfs, command_data, data->private_data); free_nfs_cb_data(data); @@ -2946,17 +3805,16 @@ static void nfs_symlink_cb(struct rpc_context *rpc _U_, int status, void *comman static int nfs_symlink_continue_internal(struct nfs_context *nfs, 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); @@ -3048,13 +3906,15 @@ static void free_nfs_rename_data(void *mem) free(data); } -static void nfs_rename_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_rename_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { RENAME3res *res; 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) { data->cb(-EFAULT, nfs, command_data, data->private_data); free_nfs_cb_data(data); @@ -3081,13 +3941,17 @@ static void nfs_rename_cb(struct rpc_context *rpc _U_, int status, void *command static int nfs_rename_continue_2_internal(struct nfs_context *nfs, 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); @@ -3100,20 +3964,28 @@ 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) { 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; } @@ -3204,13 +4076,15 @@ static void free_nfs_link_data(void *mem) free(data); } -static void nfs_link_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +static void nfs_link_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { LINK3res *res; 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) { data->cb(-EFAULT, nfs, command_data, data->private_data); free_nfs_cb_data(data); @@ -3237,13 +4111,17 @@ static void nfs_link_cb(struct rpc_context *rpc _U_, int status, void *command_d static int nfs_link_continue_2_internal(struct nfs_context *nfs, 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); @@ -3258,8 +4136,7 @@ static int nfs_link_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_d 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) { @@ -3341,11 +4218,19 @@ uint64_t nfs_get_readmax(struct nfs_context *nfs) */ uint64_t nfs_get_writemax(struct nfs_context *nfs) { - /* Some XDR 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_error(struct nfs_context *nfs, char *error_string, ...) @@ -3360,7 +4245,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); } @@ -3385,7 +4270,9 @@ static void mount_export_5_cb(struct rpc_context *rpc, int status, void *command { struct mount_cb_data *data = private_data; - if (status == RPC_STATUS_ERROR) { + 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; @@ -3407,10 +4294,12 @@ static void mount_export_4_cb(struct rpc_context *rpc, int status, void *command { 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) { + if (status == RPC_STATUS_ERROR) { data->cb(rpc, -EFAULT, command_data, data->private_data); free_mount_cb_data(data); return; @@ -3421,7 +4310,7 @@ static void mount_export_4_cb(struct rpc_context *rpc, int status, void *command return; } - if (rpc_mount_export_async(rpc, mount_export_5_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; @@ -3433,7 +4322,9 @@ static void mount_export_3_cb(struct rpc_context *rpc, int status, void *command struct mount_cb_data *data = private_data; uint32_t mount_port; - if (status == RPC_STATUS_ERROR) { + 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; @@ -3464,6 +4355,8 @@ static void mount_export_2_cb(struct rpc_context *rpc, int status, void *command { 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); @@ -3486,6 +4379,8 @@ static void mount_export_1_cb(struct rpc_context *rpc, int status, void *command { 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; @@ -3511,6 +4406,8 @@ int mount_getexports_async(struct rpc_context *rpc, const char *server, rpc_cb c { struct mount_cb_data *data; + assert(rpc->magic == RPC_CONTEXT_MAGIC); + data = malloc(sizeof(struct mount_cb_data)); if (data == NULL) { return -1; @@ -3522,7 +4419,7 @@ 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) { free_mount_cb_data(data); return -1; @@ -3533,6 +4430,7 @@ int mount_getexports_async(struct rpc_context *rpc, const char *server, rpc_cb c struct rpc_context *nfs_get_rpc_context(struct nfs_context *nfs) { + assert(nfs->rpc->magic == RPC_CONTEXT_MAGIC); return nfs->rpc; } @@ -3547,3 +4445,7 @@ const char *nfs_get_export(struct nfs_context *nfs) { const struct nfs_fh3 *nfs_get_rootfh(struct nfs_context *nfs) { return &nfs->rootfh; } + +struct nfs_fh3 *nfs_get_fh(struct nfsfh *nfsfh) { + return &nfsfh->fh; +}