X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=lib%2Flibnfs.c;h=30719ab562809cb59172cf5cd3b6b2434d0bffa3;hb=390ff38ad1689f0708cdb18811b3b7d5d616cba7;hp=2da52e4368abc2c95e27df905c9b03f62dd1f6ed;hpb=027a767eca52f74e0cdbcd60268472f2ae5514f9;p=deb_libnfs.git diff --git a/lib/libnfs.c b/lib/libnfs.c index 2da52e4..30719ab 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -69,13 +69,21 @@ #include #include #include "libnfs-zdr.h" +#include "slist.h" #include "libnfs.h" #include "libnfs-raw.h" #include "libnfs-raw-mount.h" #include "libnfs-raw-nfs.h" +#include "libnfs-raw-portmap.h" #include "libnfs-private.h" +#define MAX_DIR_CACHE 1024 + struct nfsdir { + struct nfs_fh3 fh; + fattr3 attr; + struct nfsdir *next; + struct nfsdirent *entries; struct nfsdirent *current; }; @@ -83,6 +91,7 @@ struct nfsdir { struct nfsfh { struct nfs_fh3 fh; int is_sync; + int is_append; uint64_t offset; }; @@ -94,6 +103,7 @@ struct nfs_context { uint64_t readmax; uint64_t writemax; char *cwd; + struct nfsdir *dircache; }; void nfs_free_nfsdir(struct nfsdir *nfsdir) @@ -106,11 +116,42 @@ void nfs_free_nfsdir(struct nfsdir *nfsdir) free(nfsdir->entries); nfsdir->entries = dirent; } + free(nfsdir->fh.data.data_val); free(nfsdir); } +static void nfs_dircache_add(struct nfs_context *nfs, struct nfsdir *nfsdir) +{ + int i; + LIBNFS_LIST_ADD(&nfs->dircache, nfsdir); + + for (nfsdir = nfs->dircache, i = 0; nfsdir; nfsdir = nfsdir->next, i++) { + if (i > MAX_DIR_CACHE) { + LIBNFS_LIST_REMOVE(&nfs->dircache, nfsdir); + nfs_free_nfsdir(nfsdir); + break; + } + } +} + +static struct nfsdir *nfs_dircache_find(struct nfs_context *nfs, struct nfs_fh3 *fh) +{ + struct nfsdir *nfsdir; + + for (nfsdir = nfs->dircache; nfsdir; nfsdir = nfsdir->next) { + if (nfsdir->fh.data.data_len == fh->data.data_len && + !memcmp(nfsdir->fh.data.data_val, fh->data.data_val, fh->data.data_len)) { + LIBNFS_LIST_REMOVE(&nfs->dircache, nfsdir); + return nfsdir; + } + } + + return NULL; +} + struct nfs_cb_data; -typedef int (*continue_func)(struct nfs_context *nfs, struct nfs_cb_data *data); +typedef int (*continue_func)(struct nfs_context *nfs, fattr3 *attr, + struct nfs_cb_data *data); struct nfs_cb_data { struct nfs_context *nfs; @@ -130,9 +171,11 @@ struct nfs_cb_data { /* for multi-read/write calls. */ int error; int cancel; + int oom; int num_calls; - uint64_t start_offset, max_offset; + uint64_t start_offset, max_offset, count; char *buffer; + size_t request_size; char *usrbuf; }; @@ -140,9 +183,10 @@ struct nfs_mcb_data { struct nfs_cb_data *data; uint64_t offset; uint64_t count; + int update_pos; }; -static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh); +static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr, struct nfs_cb_data *data, struct nfs_fh3 *fh); void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth) { @@ -373,6 +417,12 @@ void nfs_destroy_context(struct nfs_context *nfs) nfs->rootfh.data.data_val = NULL; } + while (nfs->dircache) { + struct nfsdir *nfsdir = nfs->dircache; + LIBNFS_LIST_REMOVE(&nfs->dircache, nfsdir); + nfs_free_nfsdir(nfsdir); + } + free(nfs); } @@ -392,6 +442,30 @@ void free_rpc_cb_data(struct rpc_cb_data *data) free(data); } +static void rpc_connect_program_5_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; + } + + data->cb(rpc, status, NULL, data->private_data); + free_rpc_cb_data(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; @@ -412,6 +486,25 @@ static void rpc_connect_program_4_cb(struct rpc_context *rpc, int status, void * return; } + switch (data->program) { + case MOUNT_PROGRAM: + if (rpc_mount3_null_async(rpc, rpc_connect_program_5_cb, + data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + return; + case NFS_PROGRAM: + if (rpc_nfs3_null_async(rpc, rpc_connect_program_5_cb, + data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + return; + } + data->cb(rpc, status, NULL, data->private_data); free_rpc_cb_data(data); } @@ -419,7 +512,9 @@ static void rpc_connect_program_4_cb(struct rpc_context *rpc, int status, void * 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; + struct pmap3_string_result *gar; + uint32_t rpc_port = 0; + unsigned char *ptr; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -434,7 +529,29 @@ static void rpc_connect_program_3_cb(struct rpc_context *rpc, int status, void * return; } - rpc_port = *(uint32_t *)command_data; + switch (rpc->s.ss_family) { + case AF_INET: + rpc_port = *(uint32_t *)command_data; + break; + case AF_INET6: + /* ouch. portmapper and ipv6 are not great */ + gar = command_data; + if (gar->addr == NULL) { + break; + } + ptr = strrchr(gar->addr, '.'); + if (ptr == NULL) { + break; + } + rpc_port = atoi(ptr + 1); + *ptr = 0; + ptr = strrchr(gar->addr, '.'); + if (ptr == NULL) { + break; + } + rpc_port += 256 * atoi(ptr + 1); + break; + } if (rpc_port == 0) { rpc_set_error(rpc, "RPC error. Program is not available on %s", data->server); data->cb(rpc, RPC_STATUS_ERROR, rpc_get_error(rpc), data->private_data); @@ -453,6 +570,7 @@ static void rpc_connect_program_3_cb(struct rpc_context *rpc, int status, void * 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; + struct pmap3_mapping map; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -467,10 +585,26 @@ static void rpc_connect_program_2_cb(struct rpc_context *rpc, int status, void * return; } - 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; + switch (rpc->s.ss_family) { + case AF_INET: + if (rpc_pmap2_getport_async(rpc, data->program, data->version, IPPROTO_TCP, rpc_connect_program_3_cb, private_data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; + case AF_INET6: + map.prog=data->program; + map.vers=data->version; + map.netid=""; + map.addr=""; + map.owner=""; + if (rpc_pmap3_getaddr_async(rpc, &map, rpc_connect_program_3_cb, private_data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; } } @@ -494,14 +628,25 @@ static void rpc_connect_program_1_cb(struct rpc_context *rpc, int status, void * 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; + switch (rpc->s.ss_family) { + case AF_INET: + if (rpc_pmap2_null_async(rpc, rpc_connect_program_2_cb, data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; + case AF_INET6: + if (rpc_pmap3_null_async(rpc, rpc_connect_program_2_cb, data) != 0) { + data->cb(rpc, status, command_data, data->private_data); + free_rpc_cb_data(data); + return; + } + break; } } -int rpc_connect_program_async(struct rpc_context *rpc, char *server, int program, int version, rpc_cb cb, void *private_data) +int rpc_connect_program_async(struct rpc_context *rpc, const char *server, int program, int version, rpc_cb cb, void *private_data) { struct rpc_cb_data *data; @@ -540,9 +685,6 @@ static void free_nfs_cb_data(struct nfs_cb_data *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; @@ -625,36 +767,6 @@ static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_da } } - -static void nfs_mount_7_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct nfs_cb_data *data = private_data; - struct nfs_context *nfs = data->nfs; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - - if (status == RPC_STATUS_ERROR) { - data->cb(-EFAULT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); - return; - } - - 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; - } -} - - static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { struct nfs_cb_data *data = private_data; @@ -693,11 +805,13 @@ static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_da memcpy(nfs->rootfh.data.data_val, res->mountres3_u.mountinfo.fhandle.fhandle3_val, nfs->rootfh.data.data_len); rpc_disconnect(rpc, "normal disconnect"); - if (rpc_connect_async(rpc, nfs->server, 2049, nfs_mount_7_cb, data) != 0) { + + if (rpc_connect_program_async(nfs->rpc, nfs->server, NFS_PROGRAM, NFS_V3, nfs_mount_8_cb, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); free_nfs_cb_data(data); return; } + /* NFS TCP connections we want to autoreconnect after sessions are torn down (due to inactivity or error) */ rpc_set_autoreconnect(rpc); } @@ -728,123 +842,6 @@ static void nfs_mount_5_cb(struct rpc_context *rpc, int status, void *command_da } } -static void nfs_mount_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct nfs_cb_data *data = private_data; - struct nfs_context *nfs = data->nfs; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - - if (status == RPC_STATUS_ERROR) { - data->cb(-EFAULT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); - return; - } - - 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; - } -} - -static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct nfs_cb_data *data = private_data; - struct nfs_context *nfs = data->nfs; - uint32_t mount_port; - - 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; - } - - mount_port = *(uint32_t *)command_data; - if (mount_port == 0) { - rpc_set_error(rpc, "RPC error. Mount program is not available on %s", nfs->server); - data->cb(-ENOENT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } - - rpc_disconnect(rpc, "normal disconnect"); - if (rpc_connect_async(rpc, nfs->server, mount_port, nfs_mount_4_cb, data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } -} - - -static void nfs_mount_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - 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); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); - return; - } - - if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, IPPROTO_TCP, nfs_mount_3_cb, private_data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } -} - -static void nfs_mount_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; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - - if (status == RPC_STATUS_ERROR) { - data->cb(-EFAULT, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); - free_nfs_cb_data(data); - return; - } - - if (rpc_pmap_null_async(rpc, nfs_mount_2_cb, data) != 0) { - data->cb(-ENOMEM, nfs, command_data, data->private_data); - free_nfs_cb_data(data); - return; - } -} - /* * Async call for mounting an nfs share and geting the root filehandle */ @@ -873,7 +870,7 @@ int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *exp data->cb = cb; data->private_data = private_data; - if (rpc_connect_async(nfs->rpc, server, 111, nfs_mount_1_cb, data) != 0) { + if (rpc_connect_program_async(nfs->rpc, server, MOUNT_PROGRAM, MOUNT_V3, nfs_mount_5_cb, data) != 0) { rpc_set_error(nfs->rpc, "Failed to start connection"); free_nfs_cb_data(data); return -1; @@ -893,6 +890,7 @@ static void nfs_lookup_path_1_cb(struct rpc_context *rpc, int status, void *comm struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; LOOKUP3res *res; + fattr3 *attr; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -915,15 +913,17 @@ static void nfs_lookup_path_1_cb(struct rpc_context *rpc, int status, void *comm return; } - if (nfs_lookup_path_async_internal(nfs, data, &res->LOOKUP3res_u.resok.object) != 0) { - rpc_set_error(nfs->rpc, "Failed to create lookup pdu"); - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); - free_nfs_cb_data(data); - return; - } + attr = res->LOOKUP3res_u.resok.obj_attributes.attributes_follow ? + &res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes : + NULL; + + /* This function will always invoke the callback and cleanup + * for failures. So no need to check the return value. + */ + nfs_lookup_path_async_internal(nfs, attr, data, &res->LOOKUP3res_u.resok.object); } -static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh) +static int nfs_lookup_path_async_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data, struct nfs_fh3 *fh) { char *path, *slash; LOOKUP3args args; @@ -962,11 +962,10 @@ static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb if (slash != NULL) { *slash = '/'; } - data->continue_cb(nfs, data); + data->continue_cb(nfs, attr, data); return 0; } - memset(&args, 0, sizeof(LOOKUP3args)); args.what.dir = *fh; args.what.name = path; @@ -1041,7 +1040,7 @@ static int nfs_normalize_path(struct nfs_context *nfs, char *path) /* /$ -> \0 */ len = strlen(path); - if (len >= 1) { + if (len > 1) { if (path[len - 1] == '/') { path[len - 1] = '\0'; len--; @@ -1083,9 +1082,45 @@ static int nfs_normalize_path(struct nfs_context *nfs, char *path) return 0; } +static void nfs_lookup_path_getattr_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + GETATTR3res *res; + fattr3 *attr; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: GETATTR of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + attr = &res->GETATTR3res_u.resok.obj_attributes; + /* This function will always invoke the callback and cleanup + * for failures. So no need to check the return value. + */ + nfs_lookup_path_async_internal(nfs, attr, data, &nfs->rootfh); +} + static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data, continue_func continue_cb, void *continue_data, void (*free_continue_data)(void *), int continue_int) { struct nfs_cb_data *data; + struct GETATTR3args args; if (path[0] == '\0') { path = "."; @@ -1131,18 +1166,27 @@ static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_c } data->path = data->saved_path; - - if (nfs_lookup_path_async_internal(nfs, data, &nfs->rootfh) != 0) { - /* return 0 here since the callback will be invoked if there is a failure */ + if (data->path[0]) { + /* This function will always invoke the callback and cleanup + * for failures. So no need to check the return value. + */ + nfs_lookup_path_async_internal(nfs, NULL, data, &nfs->rootfh); return 0; } + + /* We have a request for "", so just perform a GETATTR3 so we can + * return the attributes to the caller. + */ + memset(&args, 0, sizeof(GETATTR3args)); + args.object = nfs->rootfh; + if (rpc_nfs3_getattr_async(nfs->rpc, nfs_lookup_path_getattr_cb, &args, data) != 0) { + free_nfs_cb_data(data); + return -1; + } return 0; } - - - /* * Async stat() */ @@ -1204,7 +1248,7 @@ static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_stat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_stat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct GETATTR3args args; @@ -1284,7 +1328,7 @@ static void nfs_stat64_1_cb(struct rpc_context *rpc, int status, void *command_d free_nfs_cb_data(data); } -static int nfs_stat64_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_stat64_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct GETATTR3args args; @@ -1353,6 +1397,9 @@ static void nfs_open_trunc_cb(struct rpc_context *rpc, int status, void *command if (data->continue_int & O_SYNC) { nfsfh->is_sync = 1; } + if (data->continue_int & O_APPEND) { + nfsfh->is_append = 1; + } /* steal the filehandle */ nfsfh->fh = data->fh; @@ -1449,6 +1496,9 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, if (data->continue_int & O_SYNC) { nfsfh->is_sync = 1; } + if (data->continue_int & O_APPEND) { + nfsfh->is_append = 1; + } /* steal the filehandle */ nfsfh->fh = data->fh; @@ -1458,7 +1508,7 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, free_nfs_cb_data(data); } -static int nfs_open_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_open_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { int nfsmode = 0; ACCESS3args args; @@ -1502,7 +1552,7 @@ int nfs_open_async(struct nfs_context *nfs, const char *path, int flags, nfs_cb /* * Async chdir() */ -static int nfs_chdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_chdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { /* steal saved_path */ free(nfs->cwd); @@ -1529,38 +1579,6 @@ int nfs_chdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void * /* * Async pread() */ -static void nfs_pread_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; - READ3res *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: 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; - } - - 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); -} - static void nfs_fill_READ3args(READ3args *args, struct nfsfh *fh, uint64_t offset, uint64_t count) { memset(args, 0, sizeof(READ3args)); @@ -1589,54 +1607,96 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat data->cancel = 1; } - /* reassemble the data into the buffer */ if (status == RPC_STATUS_SUCCESS) { res = command_data; if (res->status != NFS3_OK) { rpc_set_error(nfs->rpc, "NFS: Read failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); data->error = 1; - } else { - if (res->READ3res_u.resok.count > 0) { - if (res->READ3res_u.resok.count <= mdata->count) { - 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 { + uint64_t count = res->READ3res_u.resok.count; + + if (mdata->update_pos) + data->nfsfh->offset += count; + + /* if we have more than one call or we have received a short read we need a reassembly buffer */ + if (data->num_calls || (count < mdata->count && !res->READ3res_u.resok.eof)) { + if (data->buffer == NULL) { + data->buffer = malloc(data->request_size); + if (data->buffer == NULL) { + rpc_set_error(nfs->rpc, "Out-Of-Memory: Failed to allocate reassembly buffer for %d bytes", (int)data->request_size); + data->oom = 1; + } + } + } + if (count > 0) { + if (count <= mdata->count) { + /* copy data into reassembly buffer if we have one */ + if (data->buffer != NULL) { + memcpy(&data->buffer[mdata->offset - data->start_offset], res->READ3res_u.resok.data.data_val, count); + } + if (data->max_offset < mdata->offset + count) { + data->max_offset = mdata->offset + count; } } else { rpc_set_error(nfs->rpc, "NFS: Read overflow. Server has sent more data than requested!"); data->error = 1; } } + /* check if we have received a short read */ + if (count < mdata->count && !res->READ3res_u.resok.eof) { + if (count == 0) { + rpc_set_error(nfs->rpc, "NFS: Read failed. No bytes read and not at EOF!"); + data->error = 1; + } else { + /* reissue reminder of this read request */ + READ3args args; + mdata->offset += count; + mdata->count -= count; + nfs_fill_READ3args(&args, data->nfsfh, mdata->offset, mdata->count); + if (rpc_nfs3_read_async(nfs->rpc, nfs_pread_mcb, &args, mdata) == 0) { + data->num_calls++; + return; + } else { + rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path); + data->oom = 1; + } + } + } } } + free(mdata); + if (data->num_calls > 0) { /* still waiting for more replies */ - 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) +static int nfs_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_data, int update_pos) { struct nfs_cb_data *data; @@ -1650,41 +1710,17 @@ 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)) { - READ3args args; - nfs_fill_READ3args(&args, nfsfh, offset, count); - - if (rpc_nfs3_read_async(nfs->rpc, nfs_pread_cb, &args, 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(count > 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. + /* 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; @@ -1696,40 +1732,51 @@ int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse mdata = malloc(sizeof(struct nfs_mcb_data)); if (mdata == NULL) { rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_mcb_data structure"); - if (data->num_calls == 0) + if (data->num_calls == 0) { free_nfs_cb_data(data); - return -1; + return -1; + } + data->oom = 1; + break; } memset(mdata, 0, sizeof(struct nfs_mcb_data)); mdata->data = data; mdata->offset = offset; mdata->count = readcount; + mdata->update_pos = update_pos; nfs_fill_READ3args(&args, nfsfh, offset, readcount); if (rpc_nfs3_read_async(nfs->rpc, nfs_pread_mcb, &args, mdata) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path); - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free(mdata); - if (data->num_calls == 0) + if (data->num_calls == 0) { free_nfs_cb_data(data); - return -1; + return -1; + } + data->oom = 1; + break; } count -= readcount; offset += readcount; data->num_calls++; - } + } while (count > 0); return 0; } +int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_data) +{ + return nfs_pread_async_internal(nfs, nfsfh, offset, count, cb, private_data, 0); +} + /* * Async read() */ int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, nfs_cb cb, void *private_data) { - return nfs_pread_async(nfs, nfsfh, nfsfh->offset, count, cb, private_data); + return nfs_pread_async_internal(nfs, nfsfh, nfsfh->offset, count, cb, private_data, 1); } @@ -1744,7 +1791,7 @@ static void nfs_fill_WRITE3args (WRITE3args *args, struct nfsfh *fh, uint64_t of args->file = fh->fh; args->offset = offset; args->count = count; - args->stable = fh->is_sync?FILE_SYNC:UNSTABLE; + args->stable = fh->is_sync ? FILE_SYNC : UNSTABLE; args->data.data_len = count; args->data.data_val = buf; } @@ -1775,15 +1822,21 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da rpc_set_error(nfs->rpc, "NFS: Write failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); data->error = 1; } else { - if (res->WRITE3res_u.resok.count < mdata->count) { - if (res->WRITE3res_u.resok.count == 0) { + uint64_t count = res->WRITE3res_u.resok.count; + + if (mdata->update_pos) + data->nfsfh->offset += count; + + if (count < mdata->count) { + if (count == 0) { rpc_set_error(nfs->rpc, "NFS: Write failed. No bytes written!"); data->error = 1; } else { /* reissue reminder of this write request */ WRITE3args args; - mdata->offset += res->WRITE3res_u.resok.count; - mdata->count -= res->WRITE3res_u.resok.count; + mdata->offset += count; + mdata->count -= 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) { @@ -1791,13 +1844,13 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da return; } else { rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE call for %s", data->path); - data->error = 1; + data->oom = 1; } } } - if (res->WRITE3res_u.resok.count > 0) { - if (data->max_offset < mdata->offset + res->WRITE3res_u.resok.count) { - data->max_offset = mdata->offset + res->WRITE3res_u.resok.count; + if (count > 0) { + if (data->max_offset < mdata->offset + count) { + data->max_offset = mdata->offset + count; } } } @@ -1809,7 +1862,11 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da /* still waiting for more replies */ return; } - + if (data->oom != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } if (data->error != 0) { data->cb(-EFAULT, nfs, command_data, data->private_data); free_nfs_cb_data(data); @@ -1821,14 +1878,13 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da return; } - data->nfsfh->offset = data->max_offset; data->cb(data->max_offset - data->start_offset, nfs, NULL, data->private_data); free_nfs_cb_data(data); } -int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data) +static int nfs_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data, int update_pos) { struct nfs_cb_data *data; @@ -1844,13 +1900,10 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs data->nfsfh = nfsfh; data->usrbuf = buf; - nfsfh->offset = offset; - /* 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. + /* 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; @@ -1868,25 +1921,30 @@ 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"); - if (data->num_calls == 0) + if (data->num_calls == 0) { free_nfs_cb_data(data); - return -1; + return -1; + } + data->oom = 1; + break; } memset(mdata, 0, sizeof(struct nfs_mcb_data)); mdata->data = data; mdata->offset = offset; mdata->count = writecount; + mdata->update_pos = update_pos; nfs_fill_WRITE3args(&args, nfsfh, offset, writecount, &buf[offset - data->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); - if (data->num_calls == 0) + if (data->num_calls == 0) { free_nfs_cb_data(data); - - return -1; + return -1; + } + data->oom = 1; + break; } count -= writecount; @@ -1897,12 +1955,79 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs return 0; } +int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data) +{ + return nfs_pwrite_async_internal(nfs, nfsfh, offset, count, buf, cb, private_data, 0); +} + /* * Async write() */ +static void nfs_write_append_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + GETATTR3res *res; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: GETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + if (nfs_pwrite_async_internal(nfs, data->nfsfh, res->GETATTR3res_u.resok.obj_attributes.size, data->count, data->usrbuf, data->cb, data->private_data, 1) != 0) { + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + free_nfs_cb_data(data); +} + int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, char *buf, nfs_cb cb, void *private_data) { - return nfs_pwrite_async(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data); + if (nfsfh->is_append) { + struct GETATTR3args args; + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + return -1; + } + memset(data, 0, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + data->nfsfh = nfsfh; + data->usrbuf = buf; + data->count = count; + + memset(&args, 0, sizeof(GETATTR3args)); + args.object = nfsfh->fh; + + if (rpc_nfs3_getattr_async(nfs->rpc, nfs_write_append_cb, &args, data) != 0) { + rpc_set_error(nfs->rpc, "out of memory: failed to send GETATTR"); + free_nfs_cb_data(data); + return -1; + } + return 0; + } + return nfs_pwrite_async_internal(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data, 1); } @@ -2091,7 +2216,7 @@ int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t l /* * Async truncate() */ -static int nfs_truncate_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_truncate_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { uint64_t offset = data->continue_int; struct nfsfh nfsfh; @@ -2162,7 +2287,7 @@ static void nfs_mkdir_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_mkdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_mkdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { char *str = data->continue_data; MKDIR3args args; @@ -2253,7 +2378,7 @@ static void nfs_rmdir_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_rmdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_rmdir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { char *str = data->continue_data; RMDIR3args args; @@ -2398,7 +2523,7 @@ static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_da return; } -static int nfs_creat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_creat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { char *str = data->continue_data; CREATE3args args; @@ -2489,7 +2614,7 @@ static void nfs_unlink_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_unlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_unlink_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { char *str = data->continue_data; struct REMOVE3args args; @@ -2588,7 +2713,7 @@ static void nfs_mknod_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_mknod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_mknod_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct mknod_cb_data *cb_data = data->continue_data; char *str = cb_data->path; @@ -2736,6 +2861,7 @@ static void nfs_opendir3_cb(struct rpc_context *rpc, int status, void *command_d nfsdirent->ctime.tv_usec = attributes->ctime.nseconds/1000; nfsdirent->uid = attributes->uid; nfsdirent->gid = attributes->gid; + nfsdirent->nlink = attributes->nlink; } } @@ -2842,49 +2968,54 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d return; } + if (res->READDIR3res_u.resok.dir_attributes.attributes_follow) + nfsdir->attr = res->READDIR3res_u.resok.dir_attributes.post_op_attr_u.attributes; + /* steal the dirhandle */ nfsdir->current = nfsdir->entries; - rdpe_cb_data = malloc(sizeof(struct rdpe_cb_data)); - rdpe_cb_data->getattrcount = 0; - rdpe_cb_data->status = RPC_STATUS_SUCCESS; - rdpe_cb_data->data = data; - for (nfsdirent = nfsdir->entries; nfsdirent; nfsdirent = nfsdirent->next) { - struct rdpe_lookup_cb_data *rdpe_lookup_cb_data; - LOOKUP3args args; - - rdpe_lookup_cb_data = malloc(sizeof(struct rdpe_lookup_cb_data)); - rdpe_lookup_cb_data->rdpe_cb_data = rdpe_cb_data; - rdpe_lookup_cb_data->nfsdirent = nfsdirent; - - memset(&args, 0, sizeof(LOOKUP3args)); - args.what.dir = data->fh; - args.what.name = nfsdirent->name; - - if (rpc_nfs3_lookup_async(nfs->rpc, nfs_opendir3_cb, &args, rdpe_lookup_cb_data) != 0) { - rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR LOOKUP call"); - - /* if we have already commands in flight, we cant just stop, we have to wait for the - * commands in flight to complete - */ - if (rdpe_cb_data->getattrcount > 0) { + if (nfsdir->entries) { + rdpe_cb_data = malloc(sizeof(struct rdpe_cb_data)); + rdpe_cb_data->getattrcount = 0; + rdpe_cb_data->status = RPC_STATUS_SUCCESS; + rdpe_cb_data->data = data; + for (nfsdirent = nfsdir->entries; nfsdirent; nfsdirent = nfsdirent->next) { + struct rdpe_lookup_cb_data *rdpe_lookup_cb_data; + LOOKUP3args args; + + rdpe_lookup_cb_data = malloc(sizeof(struct rdpe_lookup_cb_data)); + rdpe_lookup_cb_data->rdpe_cb_data = rdpe_cb_data; + rdpe_lookup_cb_data->nfsdirent = nfsdirent; + + memset(&args, 0, sizeof(LOOKUP3args)); + args.what.dir = data->fh; + args.what.name = nfsdirent->name; + + if (rpc_nfs3_lookup_async(nfs->rpc, nfs_opendir3_cb, &args, rdpe_lookup_cb_data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR LOOKUP call"); + + /* if we have already commands in flight, we cant just stop, we have to wait for the + * commands in flight to complete + */ + if (rdpe_cb_data->getattrcount > 0) { + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + rdpe_cb_data->status = RPC_STATUS_ERROR; + free(rdpe_lookup_cb_data); + return; + } + + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); nfs_free_nfsdir(nfsdir); data->continue_data = NULL; free_nfs_cb_data(data); - rdpe_cb_data->status = RPC_STATUS_ERROR; free(rdpe_lookup_cb_data); + free(rdpe_cb_data); return; } - - data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); - nfs_free_nfsdir(nfsdir); - data->continue_data = NULL; - free_nfs_cb_data(data); - free(rdpe_lookup_cb_data); - free(rdpe_cb_data); - return; + rdpe_cb_data->getattrcount++; } - rdpe_cb_data->getattrcount++; } } @@ -2971,6 +3102,7 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da nfsdirent->ctime.tv_usec = entry->name_attributes.post_op_attr_u.attributes.ctime.nseconds/1000; nfsdirent->uid = entry->name_attributes.post_op_attr_u.attributes.uid; nfsdirent->gid = entry->name_attributes.post_op_attr_u.attributes.gid; + nfsdirent->nlink = entry->name_attributes.post_op_attr_u.attributes.nlink; } nfsdirent->next = nfsdir->entries; @@ -3000,6 +3132,9 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da return; } + if (res->READDIRPLUS3res_u.resok.dir_attributes.attributes_follow) + nfsdir->attr = res->READDIRPLUS3res_u.resok.dir_attributes.post_op_attr_u.attributes; + /* steal the dirhandle */ data->continue_data = NULL; nfsdir->current = nfsdir->entries; @@ -3008,9 +3143,34 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da free_nfs_cb_data(data); } -static int nfs_opendir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_opendir_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { READDIRPLUS3args args; + struct nfsdir *nfsdir = data->continue_data;; + struct nfsdir *cached; + + cached = nfs_dircache_find(nfs, &data->fh); + if (cached) { + if (attr && attr->mtime.seconds == cached->attr.mtime.seconds) { + cached->current = cached->entries; + data->cb(0, nfs, cached, data->private_data); + free_nfs_cb_data(data); + return 0; + } else { + /* cache must be stale */ + nfs_free_nfsdir(cached); + } + } + + nfsdir->fh.data.data_len = data->fh.data.data_len; + nfsdir->fh.data.data_val = malloc(nfsdir->fh.data.data_len); + if (nfsdir->fh.data.data_val == NULL) { + rpc_set_error(nfs->rpc, "OOM when allocating fh for nfsdir"); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + memcpy(nfsdir->fh.data.data_val, data->fh.data.data_val, data->fh.data.data_len); args.dir = data->fh; args.cookie = 0; @@ -3060,9 +3220,9 @@ struct nfsdirent *nfs_readdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir /* * closedir() */ -void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) +void nfs_closedir(struct nfs_context *nfs, struct nfsdir *nfsdir) { - nfs_free_nfsdir(nfsdir); + nfs_dircache_add(nfs, nfsdir); } @@ -3083,7 +3243,7 @@ void nfs_getcwd(struct nfs_context *nfs, const char **cwd) struct lseek_cb_data { struct nfs_context *nfs; struct nfsfh *nfsfh; - uint64_t offset; + int64_t offset; nfs_cb cb; void *private_data; }; @@ -3093,6 +3253,7 @@ static void nfs_lseek_1_cb(struct rpc_context *rpc, int status, void *command_da GETATTR3res *res; struct lseek_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; + uint64_t size = 0; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -3115,24 +3276,41 @@ static void nfs_lseek_1_cb(struct rpc_context *rpc, int status, void *command_da return; } - data->nfsfh->offset = data->offset + res->GETATTR3res_u.resok.obj_attributes.size; - data->cb(0, nfs, &data->nfsfh->offset, data->private_data); + size = res->GETATTR3res_u.resok.obj_attributes.size; + + if (data->offset < 0 && + (uint64_t)(-data->offset) > size) { + data->cb(-EINVAL, nfs, &data->nfsfh->offset, data->private_data); + } else { + data->nfsfh->offset = data->offset + size; + data->cb(0, nfs, &data->nfsfh->offset, data->private_data); + } + free(data); } -int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, int whence, nfs_cb cb, void *private_data) +int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int64_t offset, int whence, nfs_cb cb, void *private_data) { struct lseek_cb_data *data; struct GETATTR3args args; if (whence == SEEK_SET) { - nfsfh->offset = offset; - cb(0, nfs, &nfsfh->offset, private_data); + if (offset < 0) { + cb(-EINVAL, nfs, &nfsfh->offset, private_data); + } else { + nfsfh->offset = offset; + cb(0, nfs, &nfsfh->offset, private_data); + } return 0; } if (whence == SEEK_CUR) { - nfsfh->offset += offset; - cb(0, nfs, &nfsfh->offset, private_data); + if (offset < 0 && + nfsfh->offset < (uint64_t)(-offset)) { + cb(-EINVAL, nfs, &nfsfh->offset, private_data); + } else { + nfsfh->offset += offset; + cb(0, nfs, &nfsfh->offset, private_data); + } return 0; } @@ -3211,7 +3389,7 @@ static void nfs_statvfs_1_cb(struct rpc_context *rpc, int status, void *command_ free_nfs_cb_data(data); } -static int nfs_statvfs_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_statvfs_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { FSSTAT3args args; @@ -3273,7 +3451,7 @@ static void nfs_readlink_1_cb(struct rpc_context *rpc, int status, void *command free_nfs_cb_data(data); } -static int nfs_readlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_readlink_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { READLINK3args args; @@ -3335,7 +3513,7 @@ static void nfs_chmod_cb(struct rpc_context *rpc, int status, void *command_data free_nfs_cb_data(data); } -static int nfs_chmod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_chmod_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { SETATTR3args args; @@ -3390,7 +3568,7 @@ int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs } memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len); - if (nfs_chmod_continue_internal(nfs, data) != 0) { + if (nfs_chmod_continue_internal(nfs, NULL, data) != 0) { return -1; } @@ -3438,7 +3616,7 @@ struct nfs_chown_data { gid_t gid; }; -static int nfs_chown_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_chown_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { SETATTR3args args; struct nfs_chown_data *chown_data = data->continue_data; @@ -3524,7 +3702,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) { + if (nfs_chown_continue_internal(nfs, NULL, data) != 0) { return -1; } @@ -3569,7 +3747,7 @@ static void nfs_utimes_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_utimes_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_utimes_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { SETATTR3args args; struct timeval *utimes_data = data->continue_data; @@ -3707,7 +3885,7 @@ static void nfs_access_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_access_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_access_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { int nfsmode = 0; ACCESS3args args; @@ -3804,7 +3982,7 @@ static void nfs_symlink_cb(struct rpc_context *rpc, int status, void *command_da free_nfs_cb_data(data); } -static int nfs_symlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_symlink_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_symlink_data *symlink_data = data->continue_data; SYMLINK3args args; @@ -3940,7 +4118,7 @@ static void nfs_rename_cb(struct rpc_context *rpc, int status, void *command_dat free_nfs_cb_data(data); } -static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_rename_continue_2_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_rename_data *rename_data = data->continue_data; RENAME3args args; @@ -3963,7 +4141,7 @@ static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb } -static int nfs_rename_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_rename_continue_1_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_rename_data *rename_data = data->continue_data; char* newpath = strdup(rename_data->newpath); @@ -4110,7 +4288,7 @@ static void nfs_link_cb(struct rpc_context *rpc, int status, void *command_data, free_nfs_cb_data(data); } -static int nfs_link_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_link_continue_2_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_link_data *link_data = data->continue_data; LINK3args args; @@ -4133,7 +4311,7 @@ static int nfs_link_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_d } -static int nfs_link_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +static int nfs_link_continue_1_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { struct nfs_link_data *link_data = data->continue_data; @@ -4319,91 +4497,6 @@ static void mount_export_4_cb(struct rpc_context *rpc, int status, void *command } } -static void mount_export_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct mount_cb_data *data = private_data; - uint32_t mount_port; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - if (status == RPC_STATUS_ERROR) { - data->cb(rpc, -EFAULT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(rpc, -EINTR, "Command was cancelled", data->private_data); - free_mount_cb_data(data); - return; - } - - mount_port = *(uint32_t *)command_data; - if (mount_port == 0) { - rpc_set_error(rpc, "RPC error. Mount program is not available"); - data->cb(rpc, -ENOENT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - - rpc_disconnect(rpc, "normal disconnect"); - if (rpc_connect_async(rpc, data->server, mount_port, mount_export_4_cb, data) != 0) { - data->cb(rpc, -ENOMEM, command_data, data->private_data); - free_mount_cb_data(data); - return; - } -} - -static void mount_export_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct mount_cb_data *data = private_data; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - if (status == RPC_STATUS_ERROR) { - data->cb(rpc, -EFAULT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(rpc, -EINTR, "Command was cancelled", data->private_data); - free_mount_cb_data(data); - return; - } - - if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, IPPROTO_TCP, mount_export_3_cb, private_data) != 0) { - data->cb(rpc, -ENOMEM, command_data, data->private_data); - free_mount_cb_data(data); - return; - } -} - -static void mount_export_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) -{ - struct mount_cb_data *data = private_data; - - assert(rpc->magic == RPC_CONTEXT_MAGIC); - - /* Dont want any more callbacks even if the socket is closed */ - rpc->connect_cb = NULL; - - if (status == RPC_STATUS_ERROR) { - data->cb(rpc, -EFAULT, command_data, data->private_data); - free_mount_cb_data(data); - return; - } - if (status == RPC_STATUS_CANCEL) { - data->cb(rpc, -EINTR, "Command was cancelled", data->private_data); - free_mount_cb_data(data); - return; - } - - if (rpc_pmap_null_async(rpc, mount_export_2_cb, data) != 0) { - data->cb(rpc, -ENOMEM, command_data, data->private_data); - free_mount_cb_data(data); - return; - } -} - int mount_getexports_async(struct rpc_context *rpc, const char *server, rpc_cb cb, void *private_data) { struct mount_cb_data *data; @@ -4422,7 +4515,8 @@ int mount_getexports_async(struct rpc_context *rpc, const char *server, rpc_cb c free_mount_cb_data(data); return -1; } - if (rpc_connect_async(rpc, data->server, 111, mount_export_1_cb, data) != 0) { + if (rpc_connect_program_async(rpc, data->server, MOUNT_PROGRAM, MOUNT_V3, mount_export_4_cb, data) != 0) { + rpc_set_error(rpc, "Failed to start connection"); free_mount_cb_data(data); return -1; }