diff --git a/include/libnfs-private.h b/include/libnfs-private.h index 2ec48c2..6af8f46 100644 --- a/include/libnfs-private.h +++ b/include/libnfs-private.h @@ -82,6 +82,8 @@ struct rpc_queue { #define HASHES 1024 #define NFS_RA_TIMEOUT 5 #define NFS_MAX_XFER_SIZE (1024 * 1024) +#define ZDR_ENCODE_OVERHEAD 1024 +#define ZDR_ENCODEBUF_MINSIZE 4096 struct rpc_context { uint32_t magic; @@ -97,10 +99,6 @@ struct rpc_context { struct AUTH *auth; uint32_t xid; - /* buffer used for encoding RPC PDU */ - char *encodebuf; - int encodebuflen; - struct rpc_queue outqueue; struct sockaddr_storage udp_src; struct rpc_queue waitpdu[HASHES]; @@ -156,6 +154,7 @@ void rpc_return_to_queue(struct rpc_queue *q, struct rpc_pdu *pdu); unsigned int rpc_hash_xid(uint32_t xid); struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, zdrproc_t zdr_decode_fn, int zdr_bufsize); +struct rpc_pdu *rpc_allocate_pdu2(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, zdrproc_t zdr_decode_fn, int zdr_bufsize, size_t alloc_hint); void rpc_free_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu); int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu); uint32_t rpc_get_pdu_size(char *buf); diff --git a/include/nfsc/libnfs-raw.h b/include/nfsc/libnfs-raw.h index ce88dd2..563bced 100644 --- a/include/nfsc/libnfs-raw.h +++ b/include/nfsc/libnfs-raw.h @@ -31,7 +31,7 @@ extern "C" { struct rpc_data { int size; - unsigned char *data; + char *data; }; struct rpc_context; diff --git a/lib/init.c b/lib/init.c index 9f9931a..5fe7688 100644 --- a/lib/init.c +++ b/lib/init.c @@ -62,17 +62,8 @@ struct rpc_context *rpc_init_context(void) rpc->magic = RPC_CONTEXT_MAGIC; - /* Allow NFS_MAX_XFER_SIZE of data (for writes) and some */ - rpc->encodebuflen = NFS_MAX_XFER_SIZE + 4096; - rpc->encodebuf = malloc(rpc->encodebuflen); - if (rpc->encodebuf == NULL) { - free(rpc); - return NULL; - } - rpc->auth = authunix_create_default(); if (rpc->auth == NULL) { - free(rpc->encodebuf); free(rpc); return NULL; } @@ -126,15 +117,17 @@ void rpc_set_pagecache_ttl(struct rpc_context *rpc, uint32_t v) { void rpc_set_readahead(struct rpc_context *rpc, uint32_t v) { assert(rpc->magic == RPC_CONTEXT_MAGIC); + uint32_t min_pagecache; if (v) { v = MAX(NFS_BLKSIZE, round_to_power_of_two(v)); } RPC_LOG(rpc, 2, "readahead set to %d byte", v); rpc->readahead = v; - if (v) { + min_pagecache = (2 * v) / NFS_BLKSIZE; + if (rpc->pagecache < min_pagecache) { /* current pagecache implementation needs a pagecache bigger * than the readahead size to avoid collisions */ - rpc_set_pagecache(rpc, (2 * v) / NFS_BLKSIZE); + rpc_set_pagecache(rpc, min_pagecache); } } @@ -314,11 +307,6 @@ void rpc_destroy_context(struct rpc_context *rpc) close(rpc->fd); } - if (rpc->encodebuf != NULL) { - free(rpc->encodebuf); - rpc->encodebuf = NULL; - } - if (rpc->error_string != NULL) { free(rpc->error_string); rpc->error_string = NULL; diff --git a/lib/libnfs-sync.c b/lib/libnfs-sync.c index eba6121..2adc8d6 100644 --- a/lib/libnfs-sync.c +++ b/lib/libnfs-sync.c @@ -1707,39 +1707,39 @@ static int send_nfsd_probes(struct rpc_context *rpc, struct ifconf *ifc, struct assert(rpc->magic == RPC_CONTEXT_MAGIC); for (ptr =(char *)(ifc->ifc_buf); ptr < (char *)(ifc->ifc_buf) + ifc->ifc_len; ) { - struct ifreq *ifr; + struct ifreq ifr; char bcdd[16]; - ifr = (struct ifreq *)ptr; + memcpy(&ifr, ptr, sizeof(struct ifreq)); #ifdef HAVE_SOCKADDR_LEN - if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)) { - ptr += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; + if (ifr.ifr_addr.sa_len > sizeof(struct sockaddr)) { + ptr += sizeof(ifr.ifr_name) + ifr.ifr_addr.sa_len; } else { - ptr += sizeof(ifr->ifr_name) + sizeof(struct sockaddr); + ptr += sizeof(ifr.ifr_name) + sizeof(struct sockaddr); } #else ptr += sizeof(struct ifreq); #endif - if (ifr->ifr_addr.sa_family != AF_INET) { + if (ifr.ifr_addr.sa_family != AF_INET) { continue; } - if (ioctl(rpc_get_fd(rpc), SIOCGIFFLAGS, ifr) < 0) { + if (ioctl(rpc_get_fd(rpc), SIOCGIFFLAGS, &ifr) < 0) { return -1; } - if (!(ifr->ifr_flags & IFF_UP)) { + if (!(ifr.ifr_flags & IFF_UP)) { continue; } - if (ifr->ifr_flags & IFF_LOOPBACK) { + if (ifr.ifr_flags & IFF_LOOPBACK) { continue; } - if (!(ifr->ifr_flags & IFF_BROADCAST)) { + if (!(ifr.ifr_flags & IFF_BROADCAST)) { continue; } - if (ioctl(rpc_get_fd(rpc), SIOCGIFBRDADDR, ifr) < 0) { + if (ioctl(rpc_get_fd(rpc), SIOCGIFBRDADDR, &ifr) < 0) { continue; } - if (getnameinfo(&ifr->ifr_broadaddr, sizeof(struct sockaddr_in), &bcdd[0], sizeof(bcdd), NULL, 0, NI_NUMERICHOST) < 0) { + if (getnameinfo(&ifr.ifr_broadaddr, sizeof(struct sockaddr_in), &bcdd[0], sizeof(bcdd), NULL, 0, NI_NUMERICHOST) < 0) { continue; } if (rpc_set_udp_destination(rpc, bcdd, 111, 1) < 0) { diff --git a/lib/libnfs-zdr.c b/lib/libnfs-zdr.c index ea08b41..8ec3755 100644 --- a/lib/libnfs-zdr.c +++ b/lib/libnfs-zdr.c @@ -108,15 +108,13 @@ bool_t libnfs_zdr_u_int(ZDR *zdrs, uint32_t *u) switch (zdrs->x_op) { case ZDR_ENCODE: - *(uint32_t *)&zdrs->buf[zdrs->pos] = htonl(*u); + *(uint32_t *)(void *)&zdrs->buf[zdrs->pos] = htonl(*u); zdrs->pos += 4; return TRUE; - break; case ZDR_DECODE: - *u = ntohl(*(uint32_t *)&zdrs->buf[zdrs->pos]); + *u = ntohl(*(uint32_t *)(void *)&zdrs->buf[zdrs->pos]); zdrs->pos += 4; return TRUE; - break; } return FALSE; @@ -135,20 +133,18 @@ bool_t libnfs_zdr_uint64_t(ZDR *zdrs, uint64_t *u) switch (zdrs->x_op) { case ZDR_ENCODE: - *(uint32_t *)&zdrs->buf[zdrs->pos] = htonl((*u >> 32)); + *(uint32_t *)(void *)&zdrs->buf[zdrs->pos] = htonl((*u >> 32)); zdrs->pos += 4; - *(uint32_t *)&zdrs->buf[zdrs->pos] = htonl((*u & 0xffffffff)); + *(uint32_t *)(void *)&zdrs->buf[zdrs->pos] = htonl((*u & 0xffffffff)); zdrs->pos += 4; return TRUE; - break; case ZDR_DECODE: - *u = ntohl(*(uint32_t *)&zdrs->buf[zdrs->pos]); + *u = ntohl(*(uint32_t *)(void *)&zdrs->buf[zdrs->pos]); zdrs->pos += 4; *u <<= 32; - *u |= (uint32_t)ntohl(*(uint32_t *)&zdrs->buf[zdrs->pos]); + *u |= (uint32_t)ntohl(*(uint32_t *)(void *)&zdrs->buf[zdrs->pos]); zdrs->pos += 4; return TRUE; - break; } return FALSE; @@ -559,13 +555,13 @@ struct AUTH *libnfs_authunix_create(const char *host, uint32_t uid, uint32_t gid auth->ah_cred.oa_base = malloc(size); memset(auth->ah_cred.oa_base, 0x00, size); - buf = (uint32_t *)auth->ah_cred.oa_base; + buf = (uint32_t *)(void *)auth->ah_cred.oa_base; idx = 0; buf[idx++] = htonl(time(NULL)); buf[idx++] = htonl(strlen(host)); memcpy(&buf[2], host, strlen(host)); - idx += (strlen(host) + 3) >> 2; + idx += (strlen(host) + 3) >> 2; buf[idx++] = htonl(uid); buf[idx++] = htonl(gid); buf[idx++] = htonl(len); diff --git a/lib/libnfs.c b/lib/libnfs.c index 191e92f..fd8af10 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -204,7 +204,7 @@ static void nfs_dircache_drop(struct nfs_context *nfs, struct nfs_fh3 *fh) } static uint32_t nfs_pagecache_hash(struct nfs_pagecache *pagecache, uint64_t offset) { - return (2654435761 * (1 + ((uint32_t)(offset) / NFS_BLKSIZE))) & (pagecache->num_entries - 1); + return (2654435761UL * (1 + ((uint32_t)(offset) / NFS_BLKSIZE))) & (pagecache->num_entries - 1); } void nfs_pagecache_invalidate(struct nfs_context *nfs, struct nfsfh *nfsfh) { @@ -215,7 +215,7 @@ void nfs_pagecache_invalidate(struct nfs_context *nfs, struct nfsfh *nfsfh) { } void nfs_pagecache_put(struct nfs_pagecache *pagecache, uint64_t offset, char *buf, int len) { - time_t ts = time(NULL); + time_t ts = pagecache->ttl ? time(NULL) : 1; if (!pagecache->num_entries) return; while (len > 0) { uint64_t page_offset = offset & ~(NFS_BLKSIZE - 1); @@ -274,6 +274,7 @@ struct nfs_cb_data { int num_calls; uint64_t offset, count, max_offset, org_offset, org_count; char *buffer; + int not_my_buffer; char *usrbuf; int update_pos; }; @@ -650,7 +651,7 @@ static void rpc_connect_program_3_cb(struct rpc_context *rpc, int status, void * switch (rpc->s.ss_family) { case AF_INET: - rpc_port = *(uint32_t *)command_data; + rpc_port = *(uint32_t *)(void *)command_data; break; case AF_INET6: /* ouch. portmapper and ipv6 are not great */ @@ -798,7 +799,9 @@ static void free_nfs_cb_data(struct nfs_cb_data *data) free(data->saved_path); free(data->fh.data.data_val); - free(data->buffer); + if (!data->not_my_buffer) { + free(data->buffer); + } free(data); } @@ -2243,17 +2246,28 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat data->error = 1; } else { uint64_t count = res->READ3res_u.resok.count; - + if (count < data->count && data->buffer == NULL) { + /* we need a reassembly buffer after all */ + data->buffer = malloc(mdata->count); + if (data->buffer == NULL) { + data->oom = 1; + goto out; + } + } if (count > 0) { - if (count <= mdata->count) { + if (count == data->count && data->buffer == NULL) { + data->buffer = res->READ3res_u.resok.data.data_val; + data->not_my_buffer = 1; + } else if (count <= mdata->count) { /* copy data into reassembly buffer */ memcpy(&data->buffer[mdata->offset - data->offset], res->READ3res_u.resok.data.data_val, count); - if (data->max_offset < mdata->offset + count) { - data->max_offset = mdata->offset + count; - } } else { rpc_set_error(nfs->rpc, "NFS: Read overflow. Server has sent more data than requested!"); data->error = 1; + goto out; + } + if (data->max_offset < mdata->offset + count) { + data->max_offset = mdata->offset + count; } } /* check if we have received a short read */ @@ -2279,6 +2293,7 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat } } +out: free(mdata); if (data->num_calls > 0) { @@ -2352,19 +2367,20 @@ static int nfs_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh data->offset = offset; data->count = count; - nfsfh->ra.cur_ra = MAX(NFS_BLKSIZE, nfsfh->ra.cur_ra); - data->buffer = malloc(count + nfs->rpc->readahead); - if (data->buffer == NULL) { - free_nfs_cb_data(data); - return -ENOMEM; - } - if (nfsfh->pagecache.num_entries) { while (count > 0) { char *cdata = nfs_pagecache_get(&nfsfh->pagecache, offset); if (!cdata) { break; } + /* we copy data from the pagecache so we need a reassembly buffer */ + if (data->buffer == NULL) { + data->buffer = malloc(data->count); + if (data->buffer == NULL) { + free_nfs_cb_data(data); + return -ENOMEM; + } + } memcpy(data->buffer + offset - data->offset, cdata, NFS_BLKSIZE); offset += NFS_BLKSIZE; count -= NFS_BLKSIZE; @@ -2381,6 +2397,7 @@ static int nfs_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh } if (nfs->rpc->readahead) { + nfsfh->ra.cur_ra = MAX(NFS_BLKSIZE, nfsfh->ra.cur_ra); if (offset >= nfsfh->ra.fh_offset && offset - NFS_BLKSIZE <= nfsfh->ra.fh_offset + nfsfh->ra.cur_ra) { if (nfs->rpc->readahead > nfsfh->ra.cur_ra) { @@ -2393,6 +2410,17 @@ static int nfs_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh data->count += nfsfh->ra.cur_ra; } + if ((data->count > nfs_get_readmax(nfs) || data->count > data->org_count) && + (data->buffer == NULL || nfsfh->ra.cur_ra > 0)) { + /* we do readahead, a big read or aligned out the request so we + * need a (bigger) reassembly buffer */ + data->buffer = realloc(data->buffer, data->count + nfsfh->ra.cur_ra); + if (data->buffer == NULL) { + free_nfs_cb_data(data); + return -ENOMEM; + } + } + data->max_offset = data->offset; /* chop requests into chunks of at most READMAX bytes if necessary. diff --git a/lib/pdu.c b/lib/pdu.c index 55a6ab2..6b0dd65 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -86,7 +86,7 @@ unsigned int rpc_hash_xid(uint32_t xid) #define PAD_TO_8_BYTES(x) ((x + 0x07) & ~0x07) -struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, zdrproc_t zdr_decode_fn, int zdr_decode_bufsize) +struct rpc_pdu *rpc_allocate_pdu2(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, zdrproc_t zdr_decode_fn, int zdr_decode_bufsize, size_t alloc_hint) { struct rpc_pdu *pdu; struct rpc_msg msg; @@ -112,7 +112,13 @@ struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int versi pdu->zdr_decode_fn = zdr_decode_fn; pdu->zdr_decode_bufsize = zdr_decode_bufsize; - zdrmem_create(&pdu->zdr, rpc->encodebuf, rpc->encodebuflen, ZDR_ENCODE); + pdu->outdata.data = malloc(ZDR_ENCODEBUF_MINSIZE + alloc_hint); + if (pdu->outdata.data == NULL) { + rpc_set_error(rpc, "Out of memory: Failed to allocate encode buffer"); + return NULL; + } + + zdrmem_create(&pdu->zdr, pdu->outdata.data, ZDR_ENCODEBUF_MINSIZE + alloc_hint, ZDR_ENCODE); if (rpc->is_udp == 0) { zdr_setpos(&pdu->zdr, 4); /* skip past the record marker */ } @@ -138,6 +144,11 @@ struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int versi return pdu; } +struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, zdrproc_t zdr_decode_fn, int zdr_decode_bufsize) +{ + return rpc_allocate_pdu2(rpc, program, version, procedure, cb, private_data, zdr_decode_fn, zdr_decode_bufsize, 0); +} + void rpc_free_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu) { assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -175,7 +186,7 @@ int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu) unsigned int hash; // XXX add a rpc->udp_dest_sock_size and get rid of sys/socket.h and netinet/in.h - if (sendto(rpc->fd, rpc->encodebuf, size, MSG_DONTWAIT, rpc->udp_dest, sizeof(struct sockaddr_in)) < 0) { + if (sendto(rpc->fd, pdu->zdr.buf, size, MSG_DONTWAIT, rpc->udp_dest, sizeof(struct sockaddr_in)) < 0) { rpc_set_error(rpc, "Sendto failed with errno %s", strerror(errno)); rpc_free_pdu(rpc, pdu); return -1; @@ -192,14 +203,6 @@ int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu) zdr_int(&pdu->zdr, &recordmarker); pdu->outdata.size = size; - pdu->outdata.data = malloc(pdu->outdata.size); - if (pdu->outdata.data == NULL) { - rpc_set_error(rpc, "Out of memory. Failed to allocate buffer for pdu\n"); - rpc_free_pdu(rpc, pdu); - return -1; - } - - memcpy(pdu->outdata.data, rpc->encodebuf, pdu->outdata.size); rpc_enqueue(&rpc->outqueue, pdu); return 0; @@ -209,7 +212,7 @@ uint32_t rpc_get_pdu_size(char *buf) { uint32_t size; - size = ntohl(*(uint32_t *)buf); + size = ntohl(*(uint32_t *)(void *)buf); return (size & 0x7fffffff) + 4; } diff --git a/lib/socket.c b/lib/socket.c index 4cce950..db3f68a 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -552,7 +552,7 @@ int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, rpc case AF_INET: ((struct sockaddr_in *)&rpc->s)->sin_family = ai->ai_family; ((struct sockaddr_in *)&rpc->s)->sin_port = htons(port); - ((struct sockaddr_in *)&rpc->s)->sin_addr = ((struct sockaddr_in *)(ai->ai_addr))->sin_addr; + ((struct sockaddr_in *)&rpc->s)->sin_addr = ((struct sockaddr_in *)(void *)(ai->ai_addr))->sin_addr; #ifdef HAVE_SOCKADDR_LEN ((struct sockaddr_in *)&rpc->s)->sin_len = sizeof(struct sockaddr_in); #endif @@ -560,7 +560,7 @@ int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, rpc case AF_INET6: ((struct sockaddr_in6 *)&rpc->s)->sin6_family = ai->ai_family; ((struct sockaddr_in6 *)&rpc->s)->sin6_port = htons(port); - ((struct sockaddr_in6 *)&rpc->s)->sin6_addr = ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr; + ((struct sockaddr_in6 *)&rpc->s)->sin6_addr = ((struct sockaddr_in6 *)(void *)(ai->ai_addr))->sin6_addr; #ifdef HAVE_SOCKADDR_LEN ((struct sockaddr_in6 *)&rpc->s)->sin6_len = sizeof(struct sockaddr_in6); #endif diff --git a/nfs/nfs.c b/nfs/nfs.c index 335c67b..8b54d65 100644 --- a/nfs/nfs.c +++ b/nfs/nfs.c @@ -318,7 +318,7 @@ int rpc_nfs3_write_async(struct rpc_context *rpc, rpc_cb cb, struct WRITE3args * { struct rpc_pdu *pdu; - pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_WRITE, cb, private_data, (zdrproc_t)zdr_WRITE3res, sizeof(WRITE3res)); + pdu = rpc_allocate_pdu2(rpc, NFS_PROGRAM, NFS_V3, NFS3_WRITE, cb, private_data, (zdrproc_t)zdr_WRITE3res, sizeof(WRITE3res), args->count); if (pdu == NULL) { rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for NFS3/WRITE call"); return -1; @@ -1064,7 +1064,7 @@ int rpc_nfs2_write_async(struct rpc_context *rpc, rpc_cb cb, struct WRITE2args * { struct rpc_pdu *pdu; - pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V2, NFS2_WRITE, cb, private_data, (zdrproc_t)zdr_WRITE2res, sizeof(WRITE2res)); + pdu = rpc_allocate_pdu2(rpc, NFS_PROGRAM, NFS_V2, NFS2_WRITE, cb, private_data, (zdrproc_t)zdr_WRITE2res, sizeof(WRITE2res), args->totalcount); if (pdu == NULL) { rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for NFS2/WRITE call"); return -1;