From: Luke M. <lu...@us...> - 2005-06-21 02:57:48
|
Update of /cvsroot/ipbench/ipbench2/src/tests/nfs_latency/libnfs In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26125/libnfs Added Files: Makefile callback.c callback.h mount.c nfs.c nfs.h nfsrpc.h portmap.c rpc.c rpc.h test.c xdr.c xdr.h Log Message: initial commit of nfs library --- portions by Charles Gray and Luke Macpherson --- NEW FILE: xdr.c --- /* * transport.c - all the crappy functions that should be hidden at all * costs */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/socket.h> #include "nfs.h" #include "rpc.h" #include "xdr.h" #include "nfsrpc.h" /************************************************************ * Debugging defines ***********************************************************/ #define DEBUG_RPC #ifdef DEBUG_RPC #define debug(x...) fprintf(stderr, x ) #else #define debug(x...) #endif #define NFS_MACHINE_NAME "boggo" /************************************************************ * XID Code ***********************************************************/ static xid cur_xid = 100; int get_xid(void) { return ++cur_xid; } /*************************************************************** * Buffer handling code * ***************************************************************/ /* Add a string to the packet */ void addstring(struct pbuf *pbuf, char *data) { adddata(pbuf, data, strlen(data), 0); } /* Add a fixed sized buffer to the packet */ void adddata(struct pbuf *pbuf, void *data, uintptr_t size, int byteswap) { int padded; int fill = 0; padded = size; addtobuf(pbuf, &size, sizeof(int), 1); addtobuf(pbuf, data, size, byteswap); if (padded % 4) { addtobuf(pbuf, (char *)&fill, 4 - (padded % 4), 0); } } void skipstring(struct pbuf* pbuf) { int size; /* extract the size */ getfrombuf(pbuf, (char *)&size, sizeof(size), 1); if (size % 4) size += 4 - (size % 4); pbuf->pos += size; } void getstring(struct pbuf *pbuf, char *data, int len) { getdata(pbuf, data, len, 1, 0); } int getdata(struct pbuf *pbuf, char *data, int len, int null, int byteswap) { int size, padsize; assert(len > 0); /* extract the size */ getfrombuf(pbuf, (char *)&size, sizeof(size), 1); padsize = size; if (size < len) len = size; /* copy bytes into tmp */ if (padsize % 4) padsize += 4 - (padsize % 4); getfrombuf(pbuf, data, len, byteswap); pbuf->pos += (padsize - len); /* add the null pointer to the name */ if (null) data[len] = '\0'; return len; } void* getpointfrombuf(struct pbuf * pbuf, int len) { void *ret = &pbuf->buf[pbuf->pos]; pbuf->pos += len; return ret; } #define SWAP_NTOH 0 #define SWAP_HTON 1 /* * XXX byte-swapping is a nasty hack * assumes all sub-types are 32-bit aligned 32-bit entities. */ static void bswap_memcpy(void *dst, void *src, uintptr_t len, int direction){ uint32_t *d=dst, *s=src; uintptr_t i, count; /* XXX catch possible alignment problems */ assert(((uintptr_t)s)%4==0); assert(((uintptr_t)d)%4==0); assert(len%4==0); count = len / sizeof(uint32_t); if(direction==SWAP_NTOH){ for(i=0; i<count; i++){ d[i] = ntohl(s[i]); } }else{ for(i=0; i<count; i++){ d[i] = htonl(s[i]); } } } void getfrombuf(struct pbuf *pbuf, void *data, uintptr_t len, int byteswap) { if(byteswap==0){ memcpy(data, &pbuf->buf[pbuf->pos], len); }else{ bswap_memcpy(data, &pbuf->buf[pbuf->pos], len, SWAP_NTOH); } pbuf->pos += len; } void addtobuf(struct pbuf *pbuf, void *data, uintptr_t len, int byteswap) { if(byteswap==0){ memcpy(&pbuf->buf[pbuf->pos], data, len); }else{ bswap_memcpy(&pbuf->buf[pbuf->pos], data, len, SWAP_HTON); } pbuf->pos += len; } /* for synchronous calls */ void initbuf(struct pbuf *pbuf, int prognum, int vernum, int procnum) { int txid = get_xid(); int calltype = MSG_CALL; call_body bod; opaque_auth_t cred, verf; int nsize; int tval; //debug("initbuf(): creating xid: %d\n", txid); /* zero the pbuf */ pbuf->pos = 0; memset(pbuf->buf, 0, PBUF_SIZE); /* add the xid */ addtobuf(pbuf, (char *)&txid, sizeof(xid), 1); /* set it to call */ addtobuf(pbuf, (char *)&calltype, sizeof(int), 1); /* add the call body - prog/proc info */ /* XXX lukem --- this should zero the struct first? (no cred) */ bod.rpcvers = SRPC_VERSION; bod.prog = prognum; bod.vers = vernum; bod.proc = procnum; addtobuf(pbuf, (char *)&bod, sizeof(bod), 1); /* work out size of name */ nsize = strlen(NFS_MACHINE_NAME); if (nsize % 4) nsize += 4 - (nsize % 4); /* now add the authentication */ cred.flavour = AUTH_UNIX; cred.size = 5 * sizeof(int) + nsize; addtobuf(pbuf, (char *)&cred, sizeof(cred), 1); /* add the STAMP field */ tval = 37; /* FIXME: Magic number! */ addtobuf(pbuf, (char *)&tval, sizeof(tval), 1); /* add machine name */ addstring(pbuf, NFS_MACHINE_NAME); /* add uid */ tval = 0; /* root */ addtobuf(pbuf, (char *)&tval, sizeof(tval), 1); /* add gid */ tval = 0; /* root */ addtobuf(pbuf, (char *)&tval, sizeof(tval), 1); /* add gids */ tval = 0; addtobuf(pbuf, (char *)&tval, sizeof(tval), 1); verf.flavour = AUTH_NULL; verf.size = 0; addtobuf(pbuf, (char *)&verf, sizeof(verf), 1); } /******************************************************** * General functions *********************************************************/ int extract_xid(char *data) { int xid; /* extract the xid */ memcpy(&xid, data, sizeof(int)); return ntohl(xid); } --- NEW FILE: callback.c --- #include <stdlib.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/socket.h> #include <inttypes.h> #include "rpc.h" #include "callback.h" struct callback_node{ uint32_t xid; struct callback callback; }; static struct callback_node *callback_list = NULL; static uintptr_t callback_list_length = 0; static uintptr_t callback_list_head = 0; static uint64_t drops = 0; static uint32_t last_xid = 0; static void callback_init2(uintptr_t slots){ assert(callback_list==NULL && callback_list_length==0); callback_list = malloc(slots*sizeof(struct callback_node)); assert(callback_list!=NULL); memset(callback_list, 0, (slots*sizeof(struct callback_node))); callback_list_length = slots; } void callback_init(uint32_t secs, uint32_t req_per_sec){ callback_init2(secs*req_per_sec); } void callback_add(uint32_t xid, struct callback c) { /* provided xid's must be increment by 1 for each call */ assert(xid>0); if(last_xid>0){ if(xid != (last_xid+1)){ printf("xid=%d, last_xid=%d\n", xid, last_xid); assert(xid == (last_xid+1)); } } last_xid = xid; /* increment callback list position */ callback_list_head = (callback_list_head + 1) % callback_list_length; /* if we are overwriting a slot, increment drops */ drops += (callback_list[callback_list_head].xid != 0); callback_list[callback_list_head].xid = xid; callback_list[callback_list_head].callback = c; } int callback_del(uint32_t xid, struct callback *c) { uint64_t offset, index; assert(xid > 0); offset = callback_list[callback_list_head].xid - xid; /* callback is too old */ if (offset >= callback_list_length){ return -1; } if (callback_list_head >= offset) { index = callback_list_head - offset; } else { index = callback_list_length - (offset - callback_list_head); } assert(index < callback_list_length); if (callback_list[index].xid != xid){ /* no matching xid */ return -1; } /* found matching xid */ if(c!=NULL){ *c = callback_list[index].callback; } callback_list[index].xid = 0; return 0; } --- NEW FILE: nfs.c --- #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <assert.h> #include <string.h> #include "nfs.h" #include "rpc.h" #include "xdr.h" #define debug(x...) fprintf(stderr,x) int nfs_fd; static int check_errors(struct pbuf * pbuf); /* this should be called once at beginning to setup everything */ /* * XXX this code should be abstracted out, as it is almost identical to any * other portmap connection (see mnt_init) */ int nfs_init(struct sockaddr_in * name) { struct sockaddr_in s; mapping_t map; uint16_t port = 0; /* make RPC to get nfs info */ map.prog = NFS_NUMBER; map.vers = NFS_VERSION; map.prot = IPPROTO_UDP; if (map_getport(&map) == 0) { port = map.port; debug("nfs port number is %d\n", port); } else { if (port == 0) { printf("Invalid NFS port\n"); return 1; } debug("Error getting NFS port number\n"); return 1; } /* set up the mount socket */ memcpy(&s, name, sizeof(struct sockaddr_in)); if ((nfs_fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { return -1; } s.sin_port = htons(port); if (connect(nfs_fd, (struct sockaddr *) & s, sizeof(struct sockaddr_in)) < 0) { close(nfs_fd); return -1; } /* make nfs_fd non-blocking */ assert(fcntl(nfs_fd, F_SETFL, O_NONBLOCK)!=-1); return 0; } /****************************************** * Async functions ******************************************/ void nfs_getattr_cb(struct callback *c, struct pbuf *pbuf) { void *callback = c->param.nfs.cb; uintptr_t token = c->param.nfs.arg; int err = 0, status = -1; fattr_t pattrs; void (*cb) (uintptr_t, int, fattr_t *)= callback; assert(callback != NULL); err = check_errors(pbuf); if (err == 0) { /* get the status out */ getfrombuf(pbuf, (char *)&status, sizeof(status), 1); if (status == NFS_OK) { /* it worked, so take out the return stuff! */ getfrombuf(pbuf, (void *)&pattrs, sizeof(fattr_t), 1); } } cb(token, status, &pattrs); return; } int nfs_getattr(struct cookie * fh, void (*func) (uintptr_t, int, fattr_t *), uintptr_t token) { struct pbuf pbuf; /* now the user data struct is setup, do some call stuff! */ initbuf(&pbuf, NFS_NUMBER, NFS_VERSION, NFSPROC_GETATTR); /* put in the fhandle */ addtobuf(&pbuf, (char *)fh, sizeof(struct cookie), 1); /* send it! */ return rpc_send(&pbuf, nfs_fd, create_nfs_callback(nfs_getattr_cb, func, token)); } void nfs_lookup_cb(struct callback *c, struct pbuf * pbuf) { void *callback = c->param.nfs.cb; uintptr_t token = c->param.nfs.arg; int err = 0, status = -1; struct cookie new_fh; fattr_t pattrs; void (*cb) (uintptr_t, int, struct cookie *, fattr_t *)= callback; assert(callback != NULL); err = check_errors(pbuf); if (err == 0) { /* get the status out */ getfrombuf(pbuf, (char *)&status, sizeof(status), 1); if (status == NFS_OK) { /* it worked, so take out the return stuff! */ getfrombuf(pbuf, (void *)&new_fh, sizeof(struct cookie), 1); getfrombuf(pbuf, (void *)&pattrs, sizeof(fattr_t), 1); } } cb(token, status, &new_fh, &pattrs); return; } /* request a file handle */ int nfs_lookup(struct cookie * cwd, char *name, void (*func) (uintptr_t, int, struct cookie *, fattr_t *), uintptr_t token) { struct pbuf pbuf; /* now the user data struct is setup, do some call stuff! */ initbuf(&pbuf, NFS_NUMBER, NFS_VERSION, NFSPROC_LOOKUP); /* put in the fhandle */ addtobuf(&pbuf, (char *)cwd, sizeof(struct cookie), 1); /* put in the name */ addstring(&pbuf, name); /* send it! */ return rpc_send(&pbuf, nfs_fd, create_nfs_callback(nfs_lookup_cb, func, token)); } void nfs_read_cb(struct callback *c, struct pbuf *pbuf) { void *callback = c->param.nfs.cb; uintptr_t token = c->param.nfs.arg; int err = 0, status = -1; fattr_t pattrs; char *data = NULL; int size = 0; void (*cb) (uintptr_t, int, fattr_t *, int, char *)= callback; err = check_errors(pbuf); assert(callback != NULL); if (err == 0) { /* get the status out */ getfrombuf(pbuf, (char *)&status, sizeof(status), 1); if (status == NFS_OK) { /* it worked, so take out the return stuff! */ getfrombuf(pbuf, (void *)&pattrs, sizeof(fattr_t), 1); getfrombuf(pbuf, (void *)&size, sizeof(int), 1); data = getpointfrombuf(pbuf, pattrs.size); } } cb(token, status, &pattrs, size, data); return; } int nfs_read(struct cookie * fh, int pos, int count, void (*func) (uintptr_t, int, fattr_t *, int, char *), uintptr_t token) { struct pbuf pbuf; readargs_t args; /* now the user data struct is setup, do some call stuff! */ initbuf(&pbuf, NFS_NUMBER, NFS_VERSION, NFSPROC_READ); /* copy in the fhandle */ memcpy(&args.file, (char *)fh, sizeof(struct cookie)); args.offset = pos; args.count = count; args.totalcount = 0; /* unused as per RFC */ /* add them to the buffer */ addtobuf(&pbuf, (char *)&args, sizeof(args), 1); return rpc_send(&pbuf, nfs_fd, create_nfs_callback(nfs_read_cb, func, token)); } void nfs_write_cb(struct callback *c, struct pbuf *pbuf) { void *callback = c->param.nfs.cb; uintptr_t token = c->param.nfs.arg; int err = 0, status = -1; fattr_t pattrs; void (*cb) (uintptr_t, int, fattr_t *)= callback; err = check_errors(pbuf); assert(callback != NULL); if (err == 0) { /* get the status out */ getfrombuf(pbuf, (char *)&status, sizeof(status), 1); if (status == NFS_OK) { /* it worked, so take out the return stuff! */ getfrombuf(pbuf, (void *)&pattrs, sizeof(fattr_t), 1); } } cb(token, status, &pattrs); return; } int nfs_write(struct cookie * fh, int offset, int count, void *data, void (*func) (uintptr_t, int, fattr_t *), uintptr_t token) { struct pbuf pbuf; writeargs_t args; /* now the user data struct is setup, do some call stuff! */ initbuf(&pbuf, NFS_NUMBER, NFS_VERSION, NFSPROC_WRITE); /* copy in the fhandle */ memcpy(&args.file, (char *)fh, sizeof(struct cookie)); args.offset = offset; args.beginoffset = 0; /* unused as per RFC */ args.totalcount = 0; /* unused as per RFC */ /* add them to the buffer */ addtobuf(&pbuf, (char *)&args, sizeof(args), 1); /* put the data in */ adddata(&pbuf, data, count, 0); return rpc_send(&pbuf, nfs_fd, create_nfs_callback(nfs_write_cb, func, token)); } void nfs_create_cb(struct callback *c, struct pbuf *pbuf) { void *callback = c->param.nfs.cb; uintptr_t token = c->param.nfs.arg; int err = 0, status = -1; struct cookie new_fh; fattr_t pattrs; void (*cb) (uintptr_t, int, struct cookie *, fattr_t *)= callback; assert(callback != NULL); err = check_errors(pbuf); if (err == 0) { /* get the status out */ getfrombuf(pbuf, (char *)&status, sizeof(status), 1); if (status == NFS_OK) { /* it worked, so take out the return stuff! */ getfrombuf(pbuf, (void *)&new_fh, sizeof(struct cookie), 1); getfrombuf(pbuf, (void *)&pattrs, sizeof(fattr_t), 1); } } debug("NFS CREATE CALLBACK\n"); cb(token, status, &new_fh, &pattrs); return; } int nfs_create(struct cookie * fh, char *name, sattr_t * sat, void (*func) (uintptr_t, int, struct cookie *, fattr_t *), uintptr_t token) { struct pbuf pbuf; /* now the user data struct is setup, do some call stuff! */ initbuf(&pbuf, NFS_NUMBER, NFS_VERSION, NFSPROC_CREATE); /* put in the fhandle */ addtobuf(&pbuf, (char *)fh, sizeof(struct cookie), 1); /* put in the name */ addstring(&pbuf, name); addtobuf(&pbuf, (char *)sat, sizeof(sattr_t), 1); return rpc_send(&pbuf, nfs_fd, create_nfs_callback(nfs_create_cb, func, token)); } int getentries_readdir(struct pbuf * pbuf, int *cookie) { int old_pos = pbuf->pos; int tmp = 1; int count = 0; int fileid; getfrombuf(pbuf, (char *)&tmp, sizeof(tmp), 1); debug("Got entry: %d\n", tmp); while (tmp) { getfrombuf(pbuf, (char *)&fileid, sizeof(fileid), 1); skipstring(pbuf); debug("Skipped string\n"); getfrombuf(pbuf, (char *)cookie, sizeof(int), 1); debug("Skipped string %d\n", *cookie); count++; getfrombuf(pbuf, (char *)&tmp, sizeof(tmp), 1); } getfrombuf(pbuf, (char *)&tmp, sizeof(tmp), 1); if (tmp == 0) cookie = 0; pbuf->pos = old_pos; debug("Returning: %d\n", count); return count; } void nfs_readdir_cb(struct callback *c, struct pbuf *pbuf) { void *callback = c->param.nfs.cb; uintptr_t token = c->param.nfs.arg; int err = 0, status = -1, num_entries = 0, next_cookie = 0; struct nfs_filename *entries = NULL; void (*cb) (uintptr_t, int, int, struct nfs_filename *, int)= callback; int count = 0; debug("NFS READDIR CALLBACK\n"); assert(callback != NULL); err = check_errors(pbuf); if (err == 0) { /* get the status out */ getfrombuf(pbuf, (char *)&status, sizeof(status), 1); if (status == NFS_OK) { int tmp, fileid, cookie; debug("Getting entries\n"); num_entries = getentries_readdir(pbuf, &next_cookie); entries = malloc(sizeof(struct nfs_filename) * num_entries); getfrombuf(pbuf, (char *)&tmp, sizeof(tmp), 1); debug("Got entry: %d\n", tmp); while (tmp) { int size; getfrombuf(pbuf, (char *)&fileid, sizeof(fileid), 1); debug("Got filed: %d\n", fileid); getfrombuf(pbuf, (char *)&entries[count].size, sizeof(int), 1); debug("Got size: %d\n", entries[count].size); entries[count].file = &pbuf->buf[pbuf->pos]; size = entries[count].size; if (size % 4) size += 4 - (size % 4); pbuf->pos += size; debug("Got size: %d\n", pbuf->pos); getfrombuf(pbuf, (char *)&cookie, sizeof(int), 1); count++; getfrombuf(pbuf, (char *)&tmp, sizeof(tmp), 1); } } } cb(token, status, num_entries, entries, next_cookie); return; } /* send a request for a directory item */ int nfs_readdir(struct cookie * pfh, int cookie, int size, void (*func) (uintptr_t, int, int, struct nfs_filename *, int), uintptr_t token) { struct pbuf pbuf; readdirargs_t args; /* now the user data struct is setup, do some call stuff! */ initbuf(&pbuf, NFS_NUMBER, NFS_VERSION, NFSPROC_READDIR); /* copy the buffer */ memcpy(&args.dir, pfh, sizeof(struct cookie)); /* set the cookie */ args.cookie = cookie; args.count = size; /* copy the arguments into the packet */ addtobuf(&pbuf, (char *)&args, sizeof(args), 1); /* make the call! */ return rpc_send(&pbuf, nfs_fd, create_nfs_callback(nfs_readdir_cb, func, token)); } /******************************************** * Data extraction functions ********************************************/ static int check_errors(struct pbuf * pbuf) { int txid, ctype; opaque_auth_t auth; int r; /* extract the xid */ getfrombuf(pbuf, (char *)&txid, sizeof(txid), 1); /* and the call type */ getfrombuf(pbuf, (char *)&ctype, sizeof(ctype), 1); if (ctype != MSG_REPLY) { debug("Got a reply to something else!!\n"); debug("Looking for msgtype %d\n", MSG_REPLY); debug("Got msgtype %d\n", ctype); return ERR_BAD_MSG; } /* check if it was an accepted reply */ getfrombuf(pbuf, (char *)&r, sizeof(r), 1); if (r != MSG_ACCEPTED) { debug("Message NOT accepted (%d)\n", r); /* extract error code */ getfrombuf(pbuf, (char *)&r, sizeof(r), 1); debug("Error code %d\n", r); if (r == 1) { /* get the auth problem */ getfrombuf(pbuf, (char *)&r, sizeof(r), 1); debug("auth_stat %d\n", r); } return ERR_NOT_ACCEPTED; } /* and the auth data! */ getfrombuf(pbuf, (char *)&auth, sizeof(auth), 1); if (auth.flavour != AUTH_NULL) assert("gave back other auth type!\n"); /* check its accept stat */ getfrombuf(pbuf, (char *)&r, sizeof(r), 1); if (r == SUCCESS) { return 0; } else { debug("reply stat was %d\n", r); return ERR_FAILURE; } } --- NEW FILE: xdr.h --- /* transport.h */ #ifndef __XDR_H #define __XDR_H /* these all modify RPC buffers */ void clearbuf(struct pbuf * pbuf); void initbuf(struct pbuf *pbuf, int prognum, int vernum, int procnum); void addtobuf(struct pbuf *pbuf, void *data, uintptr_t len, int byteswap); void getfrombuf(struct pbuf *pbuf, void *data, uintptr_t len, int byteswap); void getstring(struct pbuf *pbuf, char *data, int len); int getdata(struct pbuf *pbuf, char *data, int len, int null, int byteswap); void* getpointfrombuf(struct pbuf *pbuf, int len); void addstring(struct pbuf *pbuf, char *data); void adddata(struct pbuf *pbuf, void *data, uintptr_t size, int byteswap); void skipstring(struct pbuf *pbuf); /* do we need this in transport?? */ void change_endian(struct pbuf *pbuf); void resetbuf(struct pbuf *pbuf); struct pbuf *initbuf_xid(int txid, int prognum, int vernum, int procnum); int extract_xid(char *data); #endif /* __XDR_H */ --- NEW FILE: test.c --- #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include "nfs.h" #include "rpc.h" #include "callback.h" int finished = 0; void print_usage(){ printf("wrong arguments!\n"); } int resolve(char *name, struct sockaddr_in *sock){ struct hostent *hp; if((hp = gethostbyname(name))==NULL) return -1; memset(sock, 0, sizeof(struct sockaddr_in)); memcpy(&sock->sin_addr, hp->h_addr, hp->h_length); assert(hp->h_addrtype == PF_INET); sock->sin_family = PF_INET; sock->sin_port = htons(0); return 0; } #define READ_SIZE 1024 #define READ_COUNT 4096 static void read_cb(uintptr_t token, int status, fattr_t *pattrs, int size, char *data){ static uintptr_t count = 0; static int total = 0; assert(status==NFS_OK); total += size; printf("read %d:%d bytes\n", (int)token, total); if(++count==READ_COUNT){ printf("finished reading %d bytes!\n", total); finished=1; } } static void open_cb(uintptr_t token, int status, struct cookie *fh, fattr_t *pattrs){ static int idem = 0; int i; uint8_t data[READ_SIZE]; bzero(data, READ_SIZE); printf("open_cb() called!\n"); assert(idem==0); idem = 1; assert(status==NFS_OK); printf("uid=%d, gid=%d, size=%d\n", pattrs->uid, pattrs->uid, pattrs->size); i=0; while(!finished){ struct pbuf buf; nfs_read(fh, (i%READ_COUNT)*READ_SIZE, READ_SIZE, read_cb, i); /* nonblocking recv prevents massive queue buildup */ rpc_recv(&buf, nfs_fd, 0); i++; } printf("Performed %d requests to get %d blocks\n", i, READ_COUNT); printf("lost %d%% of requests\n", 100-((100*READ_COUNT)/i)); } static void runtest(){ struct cookie pfh; uintptr_t token = 0; mnt_get_export_list(); mnt_mount("/home/lukem/nfs", &pfh); nfs_lookup(&pfh, "bench.file", open_cb, token); } int main(int argc, char *argv[]){ struct sockaddr_in addr; if(argc<2){ print_usage(); exit(-1); } if(resolve(argv[1], &addr) < 0){ printf("error resolving \"%s\"\n", argv[1]); exit(-1); } callback_init(10,100000); map_init(&addr); mnt_init(&addr); nfs_init(&addr); runtest(); while(!finished){ struct pbuf buf; printf("receive loop\n"); rpc_recv(&buf, nfs_fd, 1); } return 0; } --- NEW FILE: nfs.h --- #ifndef __NFS_H #define __NFS_H #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "nfsrpc.h" extern int nfs_fd; extern int nfs_nb_fd; /* to initialise the lot */ int map_init(struct sockaddr_in * name); int mnt_init(struct sockaddr_in * name); int nfs_init(struct sockaddr_in * name); /* mount functions */ unsigned int mnt_get_export_list(void); unsigned int mnt_mount(char *dir, struct cookie * pfh); /* NFS functions */ int nfs_getattr(struct cookie * fh, void (*func) (uintptr_t, int, fattr_t *), uintptr_t token); int nfs_lookup(struct cookie * cwd, char *name, void (*func) (uintptr_t, int, struct cookie *, fattr_t *), uintptr_t token); int nfs_create(struct cookie * fh, char *name, sattr_t * sat, void (*func) (uintptr_t, int, struct cookie *, fattr_t *), uintptr_t token); int nfs_read(struct cookie * fh, int pos, int count, void (*func) (uintptr_t, int, fattr_t * attr, int, char *), uintptr_t token); int nfs_write(struct cookie * fh, int offset, int count, void *data, void (*func) (uintptr_t, int, fattr_t *), uintptr_t token); int nfs_readdir(struct cookie * pfh, int cookie, int size, void (*func) (uintptr_t, int, int, struct nfs_filename *, int), uintptr_t token); /* some error codes from nfsrpc */ #define ERR_OK 0 #define ERR_BAD_MSG -1 #define ERR_NOT_ACCEPTED -2 #define ERR_FAILURE -3 #define ERR_NOT_OK -4 #define ERR_NOT_FOUND -5 #define ERR_NEXT_AVAIL -6 #endif /* __NFS_H */ --- NEW FILE: mount.c --- #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <assert.h> #include <string.h> #include "nfs.h" #include "rpc.h" #include "xdr.h" #define debug(x...) fprintf(stderr,x) #define MOUNT_TCP 0 int mount_fd; int mnt_init(struct sockaddr_in *name){ struct sockaddr_in s; mapping_t map; uint16_t port; debug("mnt_init(): called\n"); /* make RPC to get mountd info */ map.prog = MNT_NUMBER; map.vers = MNT_VERSION; map.prot = #if MOUNT_TCP IPPROTO_TCP; #else IPPROTO_UDP; #endif if (map_getport(&map) == 0) { debug("mountd port number is %d\n", map.port); port = map.port; if (port == 0) { debug("mnt_init(): mount port invalid\n"); return 1; } } else { debug("mnt_init(): error getting mountd port number\n"); return 1; } /* set up the mount socket */ memcpy(&s, name, sizeof(struct sockaddr_in)); if((mount_fd = socket(PF_INET, #if MOUNT_TCP SOCK_STREAM, #else SOCK_DGRAM, #endif 0)) < 0){ perror("mnt_init(): error creating socket: "); return -1; } printf("mnt_init(): connecting to port %d\n", port); s.sin_port = htons(port); if(connect(mount_fd, (struct sockaddr*)&s, sizeof(struct sockaddr_in)) < 0){ perror("mnt_init(): error connecting socket"); close(mount_fd); return -1; } /* make mount_fd non-blocking */ assert(fcntl(mount_fd, F_SETFL, O_NONBLOCK)!=-1); debug("mnt_init(): done"); return 0; } unsigned int mnt_get_export_list(void) { struct pbuf pbuf, ret; char str[100]; int opt; initbuf(&pbuf, MNT_NUMBER, MNT_VERSION, MNTPROC_EXPORT); assert(rpc_call(&pbuf, &ret, mount_fd)==0); while (getfrombuf(&ret, (char *)&opt, sizeof(opt), 1), opt) { printf("NFS Export...\n"); getstring(&ret, str, 100); printf("* Export name is %s\n", (char *)&str); /* now to extract more stuff... */ while (getfrombuf(&ret, (char *)&opt, sizeof(opt), 1), opt) { getstring(&ret, str, 100); printf("* Group %s\n", (char *)str); } } return 0; } unsigned int mnt_mount(char *dir, struct cookie * pfh) { struct pbuf pbuf, ret; int status; debug("mnt_mount(): mounting \"%s\"\n", dir); initbuf(&pbuf, MNT_NUMBER, MNT_VERSION, MNTPROC_MNT); addstring(&pbuf, dir); assert(rpc_call(&pbuf, &ret, mount_fd)==0); /* now we do some stuff :) */ getfrombuf(&ret, (char *)&status, sizeof(status), 1); if (status != 0) { debug("mnt_mount(): could not mount \"%s\"\n", dir); return 0; } getfrombuf(&ret, (char *)pfh, sizeof(struct cookie), 1); debug("mnt_mount(): \"%s\" has been mounted\n", dir); return 0; } --- NEW FILE: nfsrpc.h --- /* these are RPC and NFS struct definitions */ #ifndef __NFSRPC_H #define __NFSRPC_H // #include "nfs.h" /* this is general */ #define SETBUF_LEN 1024 typedef struct setbuf { char buf[SETBUF_LEN]; int pos; int len; } setbuf_t; /**************************************************** * * This is the RPC section * ****************************************************/ /* enum msg_type { CALL = 0, REPLY = 1 }; */ #define MSG_CALL 0 #define MSG_REPLY 1 /* enum reply_stat { MSG_ACCEPTED = 0, MSG_DENIED = 1 }; */ #define MSG_ACCEPTED 0 #define MSG_DENIED 1 /* enum accept_stat { SUCCESS = 0, // RPC executed successfully _UNAVAIL = 1, // remote hasn't exported program PROG_MISMATCH = 2, // remote can't support version # PROC_UNAVAIL = 3, // program can't support procedure GARBAGE_ARGS = 4 // procedure can't decode params }; */ #define SUCCESS 0 #define PROG_UNAVAIL 1 #define PROG_MISMATCH 2 #define PROC_UNAVAIL 3 #define GARBAGE_ARGS 4 /* struct rpc_msg { unsigned int xid; union switch (msg_type mtype) { case CALL: call_body cbody; case REPLY: reply_body rbody; } body; }; */ typedef unsigned int xid; /* struct call_body { unsigned int rpcvers; // Must be two (2) int prog; unsigned int vers; unsigned int proc; opaque_auth cred; paque_auth verf; // procedure specific parameters start here }; */ #define AUTH_NULL 0 #define AUTH_UNIX 1 #define AUTH_SHORT 2 #define AUTH_DES 3 typedef struct opaque_auth { unsigned int flavour; unsigned int size; /* you can put other stuff after here, too! */ } opaque_auth_t; typedef struct { unsigned int rpcvers; //Must be two(2) unsigned int prog; unsigned int vers; unsigned int proc; /* * opaque_auth_t cred; opaque_auth_t verf; */ //procedure specific parameters start here } call_body; /* * union reply_body switch (reply_stat stat) { case MSG_ACCEPTED: * ed_reply areply; case MSG_DENIED: rejected_reply rreply; } reply; */ typedef unsigned int reply_stat; /* * struct accepted_reply { opaque_auth verf; union switch * (accept_stat stat) { case SUCCESS: opaque results[0]; // * * procedure-specific results start here case PROG_MISMATCH: struct { * nsigned int low; unsigned int high; } mismatch_info; default: // * * Void. Cases include PROG_UNAVAIL, PROC_UNAVAIL, * and GARBAGE_ARGS. * ; } reply_data; }; */ /* typedef accept_stat stat; */ #define SRPC_VERSION 2 /**************************************************** * * This is the portmapper section * ****************************************************/ /* we will only implement the getmap funciton */ #define PMAP_PORT 111 /* portmapper port number */ #define PMAP_NUMBER 100000 #define PMAP_VERSION 2 /* * Structs */ typedef struct mapping { unsigned int prog; unsigned int vers; unsigned int prot; unsigned int port; }mapping_t; #ifndef IPPROTO_TCP #define IPPROTO_TCP 6 /* protocol number for TCP/IP */ #endif #ifndef IPPROTO_UDP #define IPPROTO_UDP 17 /* protocol number for UDP/IP */ #endif /* * Functions */ /* unsigned int PMAPPROC_GETPORT(mapping) = 3; */ #define PMAPPROC_GETPORT 3 unsigned int map_getport(mapping_t * pmap); /**************************************************** * * This is the mountd section * ****************************************************/ #define MNT_NUMBER 100005 #define MNT_VERSION 1 /* file handle */ #define FHSIZE 32 struct cookie { char data[FHSIZE]; }; /* exportlist MNTPROC_EXPORT(void) = 5; */ #define MNTPROC_EXPORT 5 /* fhstatus MNTPROC_MNT(dirpath) = 1; */ #define MNTPROC_MNT 1 /**************************************************** * * This is the NFS section * ****************************************************/ #define NFS_NUMBER 100003 #define NFS_VERSION 2 /* status */ #define NFS_OK 0 #define NFSERR_PERM 1 #define NFSERR_NOENT 2 #define NFSERR_IO 5 #define NFSERR_NXIO 6 #define NFSERR_ACCES 13 #define NFSERR_EXIST 17 #define NFSERR_NODEV 19 #define NFSERR_NOTDIR 20 #define NFSERR_ISDIR 21 #define NFSERR_FBIG 27 #define NFSERR_NOSPC 28 #define NFSERR_ROFS 30 #define NFSERR_NAMETOOLONG 63 #define NFSERR_NOTEMPTY 66 #define NFSERR_DQUOT 69 #define NFSERR_STALE 70 #define NFSERR_WFLUSH 99 /* functions */ #define NFSPROC_NULL 0 struct nfs_filename { int size; const char *file; }; #define COOKIE_START 0 typedef int nfscookie_t; typedef struct readdirargs { struct cookie dir; nfscookie_t cookie; unsigned int count; } readdirargs_t; /* readdirres NFSPROC_READDIR (readdirargs) = 16; */ #define NFSPROC_READDIR 16 /* * struct diropargs { fhandle dir; filename name; }; * * * union diropres switch (stat status) { case NFS_OK: struct { fhandle * file; fattr attributes; } diropok; default: void; }; * */ typedef enum ftype { NFNON = 0, NFREG = 1, NFDIR = 2, NFBLK = 3, NFCHR = 4, NFLNK = 5 } ftype_t; typedef struct timeval2 { unsigned int seconds; unsigned int useconds; } timeval_t; typedef struct fattr{ ftype_t type; unsigned int mode; unsigned int nlink; unsigned int uid; unsigned int gid; unsigned int size; unsigned int blocksize; unsigned int rdev; unsigned int blocks; unsigned int fsid; unsigned int fileid; timeval_t atime; timeval_t mtime; timeval_t ctime; } fattr_t; #define NFSPROC_GETATTR 1 /* diropres NFSPROC_LOOKUP(diropargs) = 4; */ #define NFSPROC_LOOKUP 4 /* * struct createargs { diropargs where; sattr attributes; }; */ typedef struct sattr { unsigned int mode; unsigned int uid; unsigned int gid; unsigned int size; timeval_t atime; timeval_t mtime; } sattr_t; /* diropres NFSPROC_CREATE(createargs) = 9; */ #define NFSPROC_CREATE 9 typedef struct readargs { struct cookie file; unsigned offset; unsigned count; unsigned totalcount; } readargs_t; /* * union readres switch (stat status) { case NFS_OK: fattr attributes; * fsdata data; default: void; }; */ /* readres NFSPROC_READ(readargs) = 6; */ #define NFSPROC_READ 6 typedef struct writeargs { struct cookie file; int beginoffset; int offset; int totalcount; /* nfsdata data; */ } writeargs_t; /* attrstat NFSPROC_WRITE(writeargs) = 8; */ #define NFSPROC_WRITE 8 #endif /* __NFSRPC_H */ --- NEW FILE: callback.h --- /* callback.h */ #ifndef __CALLBACK_H #define __CALLBACK_H void callback_init(uint32_t secs, uint32_t req_per_sec); void callback_add(uint32_t xid, struct callback c); /* returns -1 on fail, zero on success */ int callback_del(uint32_t xid, struct callback *c); #endif /* __CALLBACK_H */ --- NEW FILE: rpc.c --- #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <poll.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/errno.h> #include "nfs.h" #include "rpc.h" #include "xdr.h" #include "callback.h" #include "nfsrpc.h" /************************************************************ * Debugging defines ***********************************************************/ #define DEBUG_RPC #ifdef DEBUG_RPC #define debug(x...) fprintf(stderr, x ) #else #define debug(x...) #endif static void rpc_print(char *text, unsigned char *data, size_t length){ size_t i, j; if(0) rpc_print(text, data, length); printf("%s\n", text); i = 0; while(i<length){ j = 0; printf("%04x", (int)i); while(j<16 && i<length){ printf(" %02x", (int)data[i]); i++; j++; } printf("\n"); } } int rpc_send(struct pbuf *pbuf, int fd, struct callback c) { int xid; assert(pbuf && pbuf->pos >=0 && pbuf->pos <= PBUF_SIZE); /* we always add the callback */ /* anything after here is just packet loss */ xid = extract_xid(pbuf->buf); callback_add(xid, c); if(pbuf->pos==0) return -1; /* don't send zero length rpc */ //rpc_print("sending:", pbuf->buf, pbuf->pos); if(send(fd, pbuf->buf, pbuf->pos, 0) != pbuf->pos) return -1; //printf("done\n"); return 0; } int rpc_recv(struct pbuf *pbuf, int fd, int block) { int x, xid; struct callback c; if(block){ #ifndef INFTIM #define INFTIM -1 #endif struct pollfd p = {fd, POLLIN, 0}; assert(poll(&p, 1, INFTIM)==1); } /* zero the pbuf */ pbuf->pos = 0; memset(pbuf->buf, 0, PBUF_SIZE); /* receive new packet */ //printf("receiving:\n"); x = recv(fd, pbuf->buf, PBUF_SIZE, 0); switch(x){ case -1: if(errno==EAGAIN){ return 0; }else{ perror("rpc_recv(): recv failed"); return -1; } case 0: printf("rpc_recv(): remote host closed connection?\n"); exit(-1); } //rpc_print("received:", pbuf->buf, x); /* find and execute callback function */ xid = extract_xid(pbuf->buf); if(callback_del(xid, &c) < 0) return -1; /* do the callback */ if(c.func!=NULL) c.func(&c, pbuf); return 0; } void rpc_call_cb(struct callback *c, struct pbuf *p){ int *notify = c->param.call.notify; struct pbuf **result = c->param.call.result; *result = p; *notify = ~0; } int rpc_call(struct pbuf *snd, struct pbuf *rcv, int fd) { struct callback c; int notify = 0; struct pbuf *result; c.func = rpc_call_cb; c.param.call.notify = ¬ify; c.param.call.result = &result; assert(rpc_send(snd, fd, c)==0); while(notify==0){ rpc_recv(rcv, fd, 1); } assert(result==rcv); /* parse the rpc headers */ { opaque_auth_t auth; reply_stat r; rcv->pos += 8; /* check if it was an accepted reply */ getfrombuf(rcv, (char*) &r, sizeof(r), 1); if(r != MSG_ACCEPTED){ debug( "Message NOT accepted (%d)\n", r ); /* extract error code */ getfrombuf(rcv, (char*) &r, sizeof(r), 1); debug( "Error code %d\n", r ); if(r == 1) { /* get the auth problem */ getfrombuf(rcv, (char*) &r, sizeof(r), 1); debug( "auth_stat %d\n", r ); } return 0; } /* and the auth data!*/ getfrombuf(rcv, (char*) &auth, sizeof(auth), 1); debug("Got auth data. size is %d\n", auth.size); /* check its accept stat */ getfrombuf(rcv, (char*) &r, sizeof(r), 1); if( r != SUCCESS ){ debug("reply stat was %d\n", r); return 0; } } return 0; } struct callback create_nfs_callback(void (*func)(struct callback *c, struct pbuf *p), void *callback, uintptr_t arg){ struct callback c; c.func = func; c.param.nfs.cb = callback; c.param.nfs.arg = arg; return c; } --- NEW FILE: Makefile --- CFLAGS=-g -O2 -Wall -Werror default: test nfs.o: nfs.c $(CC) $(CFLAGS) -c -o nfs.o nfs.c portmap.o: portmap.c $(CC) $(CFLAGS) -c -o portmap.o portmap.c mount.o: mount.c $(CC) $(CFLAGS) -c -o mount.o mount.c rpc.o: rpc.c $(CC) $(CFLAGS) -c -o rpc.o rpc.c xdr.o: xdr.c $(CC) $(CFLAGS) -c -o xdr.o xdr.c callback.o: callback.c $(CC) $(CFLAGS) -c -o callback.o callback.c test.o: test.c $(CC) $(CFLAGS) -c -o test.o test.c test: test.o portmap.o mount.o nfs.o rpc.o xdr.o callback.o $(CC) $(CCFLAGS) -o test test.o portmap.o mount.o nfs.o \ rpc.o xdr.o callback.o clean: rm -f *.o test test.core --- NEW FILE: portmap.c --- #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <assert.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include "nfs.h" #include "rpc.h" #include "xdr.h" #define debug(x...) fprintf(stderr,x) #define PORTMAP_TCP 0 int map_fd; int map_init(struct sockaddr_in *name){ struct sockaddr_in s; debug("map_init(): called\n"); memcpy(&s, name, sizeof(struct sockaddr_in)); if((map_fd = socket(PF_INET, #if PORTMAP_TCP SOCK_STREAM, #else SOCK_DGRAM, #endif 0)) < 0){ perror("map_init(): error creating socket"); return -1; } s.sin_port = htons(PMAP_PORT); if(connect(map_fd, (struct sockaddr*)&s, sizeof(struct sockaddr_in)) < 0){ perror("map_init(): error connecting socket"); close(map_fd); return -1; } /* make nfs_fd non-blocking */ assert(fcntl(map_fd, F_SETFL, O_NONBLOCK)!=-1); debug("map_init(): done\n"); return 0; } unsigned int map_getport(mapping_t *pmap) { int port; struct pbuf pbuf, ret; debug("map_getport(): called\n"); pmap->port = 0; /* add the xid */ initbuf(&pbuf, PMAP_NUMBER, PMAP_VERSION, PMAPPROC_GETPORT); /* pack up the map struct */ addtobuf(&pbuf, (char *)pmap, sizeof(mapping_t), 1); debug("map_getport(): doing rpc_call()\n"); /* make the call */ assert(rpc_call(&pbuf, &ret, map_fd)==0); debug("map_getport(): rpc_call() returned\n"); /* now we can extract the port */ getfrombuf(&ret, (char *)&port, sizeof(port), 1); pmap->port = port; debug("map_getport(): got port %d\n", port); return 0; } --- NEW FILE: rpc.h --- /* transport.h */ #ifndef __RPC_H #define __RPC_H #define PBUF_SIZE 1460 struct pbuf{ int pos; /* start of data inside buf */ char buf[PBUF_SIZE]; /* space where data is stored */ }; struct callback_args_nfs{ void *cb; uintptr_t arg; }; struct callback_args_time{ uint64_t timestamp; }; struct callback_args_call{ int *notify; struct pbuf **result; }; struct callback{ void (*func)(struct callback *c, struct pbuf *p); union { struct callback_args_nfs nfs; struct callback_args_time time; struct callback_args_call call; } param; }; int rpc_send(struct pbuf *pbuf, int fd, struct callback c); int rpc_recv(struct pbuf *pbuf, int fd, int block); int rpc_call(struct pbuf *snd, struct pbuf *rcv, int fd); int init_transport(struct sockaddr *name, socklen_t namelen); struct callback create_nfs_callback(void (*func)(struct callback *c, struct pbuf *p), void *callback, uintptr_t arg); #endif /* __RPC_H */ |