commit 848e497970ce5ea22ddaba84027edfafc08c46c4 Author: Vitaliy Filippov Date: Mon Oct 11 01:03:58 2021 +0300 Example NFS server using libnfs diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5fd9c7b --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +nfs-server: nfs-server.c nfs-service.c nfs-service.h + gcc -g -I/usr/include/nfsc nfs-server.c nfs-service.c -o nfs-server -lnfs -levent diff --git a/make-stub.js b/make-stub.js new file mode 100644 index 0000000..ed6b6e0 --- /dev/null +++ b/make-stub.js @@ -0,0 +1,48 @@ +const rpc = [ + 'GETATTR', + 'SETATTR', + 'LOOKUP', + 'ACCESS', + 'READLINK', + 'READ', + 'WRITE', + 'CREATE', + 'MKDIR', + 'SYMLINK', + 'MKNOD', + 'REMOVE', + 'RMDIR', + 'RENAME', + 'LINK', + 'READDIR', + 'READDIRPLUS', + 'FSSTAT', + 'FSINFO', + 'PATHCONF', + 'COMMIT', +]; + +const len = rpc.reduce((a, c) => a < c.length ? c.length : a, 0); +let t = ''; +let s = ''; +for (const f of rpc) +{ + let pad = ''; + for (let i = f.length; i < len; i++) + pad += ' '; + t += ` {NFS3_${f}, ${pad}nfs3_${f.toLowerCase()}_proc, ${pad}(zdrproc_t)zdr_${f}3args, ${pad}sizeof(${f}3args)},\n`; + s += `static int nfs3_${f.toLowerCase()}_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + ${f}3args *args = call->body.cbody.args; + ${f}3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_${f}3res, sizeof(${f}3res)); + return 0; +} + +`; +} + +t = `struct service_proc nfs3_pt[] = {\n${t}};\n`; +console.log(t); +console.log(s); diff --git a/nfs-server.c b/nfs-server.c new file mode 100644 index 0000000..65c65e4 --- /dev/null +++ b/nfs-server.c @@ -0,0 +1,542 @@ +#define _FILE_OFFSET_BITS 64 +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "nfs-service.h" + +struct event_base *base; + +struct server +{ + struct rpc_context *rpc; + struct event *read_event; + struct event *write_event; +}; + +struct mapping +{ + struct mapping *next; + u_int prog; + u_int vers; + int port; + char *netid; + char *addr; + char *owner; +}; +struct mapping *map; + +void free_map_item(struct mapping *item) +{ + free(item->netid); + free(item->addr); + free(item->owner); + free(item); +} + +static void free_server(struct server *server) +{ + if (server->rpc) + { + rpc_disconnect(server->rpc, NULL); + rpc_destroy_context(server->rpc); + } + if (server->read_event) + { + event_free(server->read_event); + } + if (server->write_event) + { + event_free(server->write_event); + } + free(server); +} + +/* + * Based on the state of libnfs and its context, update libevent + * accordingly regarding which events we are interested in. + */ +static void update_events(struct rpc_context *rpc, struct event *read_event, struct event *write_event) +{ + int events = rpc_which_events(rpc); + if (read_event) + { + if (events & POLLIN) + event_add(read_event, NULL); + else + event_del(read_event); + } + if (write_event) + { + if (events & POLLOUT) + event_add(write_event, NULL); + else + event_del(write_event); + } +} + +/* + * Add a registration for program,version,netid. + */ +int pmap_register(int prog, int vers, char *netid, char *addr, char *owner) +{ + struct mapping *item; + char *str; + int count = 0; + + item = malloc(sizeof(struct mapping)); + item->prog = prog; + item->vers = vers; + item->netid = netid; + item->addr = addr; + item->owner = owner; + + /* The port are the last two dotted decimal fields in the address */ + for (str = item->addr + strlen(item->addr) - 1; str >= item->addr; str--) + { + if (*str != '.') + { + if (*str < '0' || *str > '9') + break; + continue; + } + count++; + if (count == 2) + { + int high, low; + sscanf(str, ".%d.%d", &high, &low); + item->port = high * 256 + low; + break; + } + } + + item->next = map; + map = item; +} + +/* + * Find and return a registration matching program,version,netid. + */ +struct mapping *map_lookup(int prog, int vers, char *netid) +{ + struct mapping *tmp; + for (tmp = map; tmp; tmp = tmp->next) + { + if (tmp->prog != prog) + continue; + if (tmp->vers != vers) + continue; + if (strcmp(tmp->netid, netid)) + continue; + return tmp; + } + return NULL; +} + +/* + * Remove a registration from our map or registrations. + */ +void map_remove(int prog, int vers, char *netid) +{ + struct mapping *prev = NULL; + struct mapping *tmp; + for (tmp = map; tmp; prev = tmp, tmp = tmp->next) + { + if (tmp->prog != prog) + continue; + if (tmp->vers != vers) + continue; + if (strcmp(tmp->netid, netid)) + continue; + break; + } + if (tmp == NULL) + return; + if (prev) + prev->next = tmp->next; + else + map = tmp->next; + free_map_item(tmp); + return; +} + +/* + * The NULL procedure. All protocols/versions must provide a NULL procedure + * as index 0. + * It is used by clients, and rpcinfo, to "ping" a service and verify that + * the service is available and that it does support the indicated version. + */ +static int pmap2_null_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); + return 0; +} + +/* + * v2 GETPORT. + * This is the lookup function for portmapper version 2. + * A client provides program, version and protocol (tcp or udp) + * and portmapper returns which port that service is available on, + * (or 0 if no such program is registered.) + */ +static int pmap2_getport_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + PMAP2GETPORTargs *args = call->body.cbody.args; + struct mapping *tmp; + char *netid; + uint32_t port = 0; + if (args->prot == IPPROTO_TCP) + netid = "tcp"; + else + netid = "udp"; + tmp = map_lookup(args->prog, args->vers, netid); + if (tmp) + port = tmp->port; + rpc_send_reply(rpc, call, &port, (zdrproc_t)zdr_uint32_t, sizeof(uint32_t)); + return 0; +} + +/* + * v2 DUMP. + * This RPC returns a list of all endpoints that are registered with + * portmapper. + */ +static int pmap2_dump_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + PMAP2DUMPres reply; + struct mapping *tmp; + + reply.list = NULL; + for (tmp = map; tmp; tmp = tmp->next) + { + struct pmap2_mapping_list *tmp_list; + int proto; + + /* pmap2 only support ipv4 */ + if (!strcmp(tmp->netid, "tcp")) + proto = IPPROTO_TCP; + else if (!strcmp(tmp->netid, "udp")) + proto = IPPROTO_UDP; + else + continue; + + tmp_list = malloc(sizeof(struct pmap2_mapping_list)); + tmp_list->map.prog = tmp->prog; + tmp_list->map.vers = tmp->vers; + tmp_list->map.prot = proto; + tmp_list->map.port = tmp->port; + + tmp_list->next = reply.list; + reply.list = tmp_list; + } + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_PMAP2DUMPres, sizeof(PMAP2DUMPres)); + + while (reply.list) + { + struct pmap2_mapping_list *tmp_list = reply.list->next; + free(reply.list); + reply.list = tmp_list; + } + + return 0; +} + +/* + * v2 SET + * This procedure is used to register and endpoint with portmapper. + */ +static int pmap2_set_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + PMAP2GETPORTargs *args = call->body.cbody.args; + char *prot; + char *addr; + uint32_t response = 1; + + if (args->prot == IPPROTO_TCP) + prot = "tcp"; + else + prot = "udp"; + + /* Don't update if we already have a mapping */ + if (map_lookup(args->prog, args->vers, prot)) + { + response = 0; + rpc_send_reply(rpc, call, &response, (zdrproc_t)zdr_uint32_t, sizeof(uint32_t)); + return 0; + } + + asprintf(&addr, "0.0.0.0.%d.%d", args->port >> 8, args->port & 0xff); + pmap_register(args->prog, args->vers, strdup(prot), addr, strdup("")); + + rpc_send_reply(rpc, call, &response, (zdrproc_t)zdr_uint32_t, sizeof(uint32_t)); + return 0; +} + +/* + * v2 UNSET + * This procedure is used to remove a registration from portmappers + * list of endpoints. + */ +static int pmap2_unset_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + PMAP2GETPORTargs *args = call->body.cbody.args; + char *prot; + char *addr; + uint32_t response = 1; + if (args->prot == IPPROTO_TCP) + prot = "tcp"; + else + prot = "udp"; + map_remove(args->prog, args->vers, prot); + rpc_send_reply(rpc, call, &response, (zdrproc_t)zdr_uint32_t, sizeof(uint32_t)); + return 0; +} + +/* + * Service table for portmapper v2. + * + * Service management is table driven in libnfsand this is the table + * that defines which procedures we implement for portmapper v2. + * If clients try to connect to the not-yet-implemented procedures here + * libnfs will automatically respond with an RPC layer error that flags + * PROCEDURE UNAVAILABLE. + * + * This table contains the procedure number, the callback function to implement + * this procedure, the unmarshalling function that libnfs should use to unppack + * the client payload as well as its size. + * + * Version 2 does not support ipv6 so this version of portmapper is + * not too commonly used any more. + */ +struct service_proc pmap2_pt[] = { + {PMAP2_NULL, pmap2_null_proc, (zdrproc_t)zdr_void, 0}, + {PMAP2_SET, pmap2_set_proc, (zdrproc_t)zdr_PMAP2SETargs, sizeof(PMAP2SETargs)}, + {PMAP2_UNSET, pmap2_unset_proc, (zdrproc_t)zdr_PMAP2UNSETargs, sizeof(PMAP2UNSETargs)}, + {PMAP2_GETPORT, pmap2_getport_proc, (zdrproc_t)zdr_PMAP2GETPORTargs, sizeof(PMAP2GETPORTargs)}, + {PMAP2_DUMP, pmap2_dump_proc, (zdrproc_t)zdr_void, 0}, + //{PMAP2_CALLIT, pmap2_...}, +}; + +/* + * The NULL procedure. All protocols/versions must provide a NULL procedure + * as index 0. + * It is used by clients, and rpcinfo, to "ping" a service and verify that + * the service is available and that it does support the indicated version. + */ +static int pmap3_null_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); + return 0; +} + +/* + * v3 DUMP. + * This RPC returns a list of all endpoints that are registered with + * portmapper. + */ +static int pmap3_dump_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + PMAP3DUMPres reply; + struct mapping *tmp; + reply.list = NULL; + for (tmp = map; tmp; tmp = tmp->next) + { + struct pmap3_mapping_list *tmp_list; + + tmp_list = malloc(sizeof(struct pmap3_mapping_list)); + tmp_list->map.prog = tmp->prog; + tmp_list->map.vers = tmp->vers; + tmp_list->map.netid = tmp->netid; + tmp_list->map.addr = tmp->addr; + tmp_list->map.owner = tmp->owner; + + tmp_list->next = reply.list; + reply.list = tmp_list; + } + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_PMAP3DUMPres, sizeof(PMAP3DUMPres)); + + while (reply.list) + { + struct pmap3_mapping_list *tmp_list = reply.list->next; + free(reply.list); + reply.list = tmp_list; + } + + return 0; +} + +/* + * Service table for portmapper v3. + * + * Service management is table driven in libnfs and this is the table + * that defines which procedures we implement for portmapper v3. + * If clients try to connect to the not-yet-implemented procedures here + * libnfs will automatically respond with an RPC layer error that flags + * PROCEDURE UNAVAILABLE. + * + * This table contains the procedure number, the callback function to implement + * this procedure, the unmarshalling function that libnfs should use to unppack + * the client payload as well as its size. + */ +struct service_proc pmap3_pt[] = { + {PMAP3_NULL, pmap3_null_proc, (zdrproc_t)zdr_void, 0}, + //{PMAP3_SET, pmap3_...}, + //{PMAP3_UNSET, pmap3_...}, + //{PMAP3_GETADDR, pmap3_...}, + {PMAP3_DUMP, pmap3_dump_proc, (zdrproc_t)zdr_void, 0}, + //{PMAP3_CALLIT, pmap3_...}, + //{PMAP3_GETTIME, pmap3_...}, + //{PMAP3_UADDR2TADDR, pmap3_...}, + //{PMAP3_TADDR2UADDR, pmap3_...}, +}; + +// Handle incoming event +static void server_io(evutil_socket_t fd, short events, void *private_data) +{ + struct server *server = private_data; + int revents = 0; + if (events & EV_READ) + revents |= POLLIN; + if (events & EV_WRITE) + revents |= POLLOUT; + // Let libnfs process the event + if (rpc_service(server->rpc, revents) < 0) + { + free_server(server); + return; + } + // Update which events we are interested in + update_events(server->rpc, server->read_event, server->write_event); +} + +// Accept a connection +static void do_accept(evutil_socket_t s, short events, void *private_data) +{ + struct sockaddr_storage ss; + socklen_t len = sizeof(ss); + struct server *server; + int fd; + + server = malloc(sizeof(struct server)); + if (server == NULL) + return; + memset(server, 0, sizeof(*server)); + + if ((fd = accept(s, (struct sockaddr *)&ss, &len)) < 0) + { + free_server(server); + return; + } + evutil_make_socket_nonblocking(fd); + + server->rpc = rpc_init_server_context(fd); + if (server->rpc == NULL) + { + close(fd); + free_server(server); + return; + } + + // portmap + rpc_register_service(server->rpc, PMAP_PROGRAM, PMAP_V2, pmap2_pt, sizeof(pmap2_pt) / sizeof(pmap2_pt[0])); + rpc_register_service(server->rpc, PMAP_PROGRAM, PMAP_V3, pmap3_pt, sizeof(pmap3_pt) / sizeof(pmap3_pt[0])); + + // NFS + rpc_register_service(server->rpc, NFS_PROGRAM, NFS_V3, nfs3_pt, sizeof(nfs3_pt) / sizeof(nfs3_pt[0])); + rpc_register_service(server->rpc, MOUNT_PROGRAM, MOUNT_V3, nfs3_mount_pt, sizeof(nfs3_mount_pt) / sizeof(nfs3_mount_pt[0])); + + // read and write events + server->read_event = event_new(base, fd, EV_READ|EV_PERSIST, server_io, server); + server->write_event = event_new(base, fd, EV_WRITE|EV_PERSIST, server_io, server); + update_events(server->rpc, server->read_event, server->write_event); +} + +int main(int argc, char *argv[]) +{ + struct sockaddr_in in; + int one = 1; + + base = event_base_new(); + if (base == NULL) + { + printf("Failed create event context\n"); + exit(10); + } + + in.sin_family = AF_INET; + in.sin_port = htons(111); + in.sin_addr.s_addr = htonl(INADDR_ANY); + + pmap_register(PMAP_PROGRAM, PMAP_V2, strdup("tcp"), strdup("0.0.0.0.0.111"), strdup("portmapper-service")); + pmap_register(PMAP_PROGRAM, PMAP_V3, strdup("tcp"), strdup("0.0.0.0.0.111"), strdup("portmapper-service")); + pmap_register(NFS_PROGRAM, NFS_V3, strdup("tcp"), strdup("0.0.0.0.0.2049"), strdup("nfs-server")); + pmap_register(MOUNT_PROGRAM, MOUNT_V3, strdup("tcp"), strdup("0.0.0.0.0.2049"), strdup("rpc.mountd")); + + // Portmap socket + int listen_socket = socket(AF_INET, SOCK_STREAM, 0); + if (listen_socket == -1) + { + printf("Failed to create listening socket\n"); + exit(10); + } + evutil_make_socket_nonblocking(listen_socket); + setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (bind(listen_socket, (struct sockaddr *)&in, sizeof(in)) < 0) + { + printf("Failed to bind listening socket\n"); + exit(10); + } + if (listen(listen_socket, 16) < 0) + { + printf("failed to listen to socket\n"); + exit(10); + } + struct event *listen_event = event_new(base, listen_socket, EV_READ|EV_PERSIST, do_accept, NULL); + event_add(listen_event, NULL); + + // NFS socket + int nfs_socket = socket(AF_INET, SOCK_STREAM, 0); + if (nfs_socket == -1) + { + printf("Failed to create listening socket\n"); + exit(10); + } + evutil_make_socket_nonblocking(nfs_socket); + setsockopt(nfs_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + in.sin_family = AF_INET; + in.sin_port = htons(2049); + in.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(nfs_socket, (struct sockaddr *)&in, sizeof(in)) < 0) + { + printf("Failed to bind listening socket\n"); + exit(10); + } + if (listen(nfs_socket, 16) < 0) + { + printf("failed to listen to socket\n"); + exit(10); + } + listen_event = event_new(base, nfs_socket, EV_READ|EV_PERSIST, do_accept, NULL); + event_add(listen_event, NULL); + + // Start the event loop + event_base_dispatch(base); + + return 0; +} diff --git a/nfs-service.c b/nfs-service.c new file mode 100644 index 0000000..0caede3 --- /dev/null +++ b/nfs-service.c @@ -0,0 +1,355 @@ +#include +#include +#include "nfs-service.h" + +static void fill_example_fsattr(struct fattr3 *attr) +{ + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + attr->type = NF3DIR; + attr->mode = 0755; + attr->nlink = 1; + attr->uid = 0; + attr->gid = 0; + attr->size = 4096; + attr->used = 4096; + attr->rdev = (specdata3){ 0, 0 }; + attr->fsid = 1; + attr->fileid = 1; + attr->atime.seconds = now.tv_sec; + attr->atime.nseconds = now.tv_nsec; + attr->mtime = attr->atime; + attr->ctime = attr->atime; +} + +static int nfs3_null_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); + return 0; +} + +static int nfs3_getattr_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + GETATTR3args *args = call->body.cbody.args; + GETATTR3res reply; + if (args->object.data.data_len != 10) + { + // Example error + reply.status = NFS3ERR_PERM; + } + else + { + // Fill info + reply.status = NFS3_OK; + fill_example_fsattr(&reply.GETATTR3res_u.resok.obj_attributes); + } + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_GETATTR3res, sizeof(GETATTR3res)); + return 0; +} + +static int nfs3_setattr_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + SETATTR3args *args = call->body.cbody.args; + SETATTR3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_SETATTR3res, sizeof(SETATTR3res)); + return 0; +} + +static int nfs3_lookup_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + LOOKUP3args *args = call->body.cbody.args; + LOOKUP3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_LOOKUP3res, sizeof(LOOKUP3res)); + return 0; +} + +static int nfs3_access_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + ACCESS3args *args = call->body.cbody.args; + ACCESS3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_ACCESS3res, sizeof(ACCESS3res)); + return 0; +} + +static int nfs3_readlink_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + READLINK3args *args = call->body.cbody.args; + READLINK3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READLINK3res, sizeof(READLINK3res)); + return 0; +} + +static int nfs3_read_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + READ3args *args = call->body.cbody.args; + READ3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READ3res, sizeof(READ3res)); + return 0; +} + +static int nfs3_write_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + WRITE3args *args = call->body.cbody.args; + WRITE3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_WRITE3res, sizeof(WRITE3res)); + return 0; +} + +static int nfs3_create_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + CREATE3args *args = call->body.cbody.args; + CREATE3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_CREATE3res, sizeof(CREATE3res)); + return 0; +} + +static int nfs3_mkdir_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + MKDIR3args *args = call->body.cbody.args; + MKDIR3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_MKDIR3res, sizeof(MKDIR3res)); + return 0; +} + +static int nfs3_symlink_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + SYMLINK3args *args = call->body.cbody.args; + SYMLINK3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_SYMLINK3res, sizeof(SYMLINK3res)); + return 0; +} + +static int nfs3_mknod_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + MKNOD3args *args = call->body.cbody.args; + MKNOD3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_MKNOD3res, sizeof(MKNOD3res)); + return 0; +} + +static int nfs3_remove_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + REMOVE3args *args = call->body.cbody.args; + REMOVE3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_REMOVE3res, sizeof(REMOVE3res)); + return 0; +} + +static int nfs3_rmdir_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + RMDIR3args *args = call->body.cbody.args; + RMDIR3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_RMDIR3res, sizeof(RMDIR3res)); + return 0; +} + +static int nfs3_rename_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + RENAME3args *args = call->body.cbody.args; + RENAME3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_RENAME3res, sizeof(RENAME3res)); + return 0; +} + +static int nfs3_link_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + LINK3args *args = call->body.cbody.args; + LINK3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_LINK3res, sizeof(LINK3res)); + return 0; +} + +static int nfs3_readdir_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + READDIR3args *args = call->body.cbody.args; + READDIR3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READDIR3res, sizeof(READDIR3res)); + return 0; +} + +static int nfs3_readdirplus_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + READDIRPLUS3args *args = call->body.cbody.args; + READDIRPLUS3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READDIRPLUS3res, sizeof(READDIRPLUS3res)); + return 0; +} + +static int nfs3_fsstat_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + FSSTAT3args *args = call->body.cbody.args; + FSSTAT3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_FSSTAT3res, sizeof(FSSTAT3res)); + return 0; +} + +static int nfs3_fsinfo_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + FSINFO3args *args = call->body.cbody.args; + FSINFO3res reply; + if (args->fsroot.data.data_len != 10) + { + // Example error + reply.status = NFS3ERR_INVAL; + } + else + { + // Fill info + reply.status = NFS3_OK; + reply.FSINFO3res_u.resok.obj_attributes.attributes_follow = TRUE; + fill_example_fsattr(&reply.FSINFO3res_u.resok.obj_attributes.post_op_attr_u.attributes); + reply.FSINFO3res_u.resok.rtmax = 128*1024*1024; + reply.FSINFO3res_u.resok.rtpref = 128*1024*1024; + reply.FSINFO3res_u.resok.rtmult = 4096; + reply.FSINFO3res_u.resok.wtmax = 128*1024*1024; + reply.FSINFO3res_u.resok.wtpref = 128*1024*1024; + reply.FSINFO3res_u.resok.wtmult = 4096; + reply.FSINFO3res_u.resok.dtpref = 128; + reply.FSINFO3res_u.resok.maxfilesize = 0x7fffffffffffffff; + reply.FSINFO3res_u.resok.time_delta.seconds = 1; + reply.FSINFO3res_u.resok.time_delta.nseconds = 0; + reply.FSINFO3res_u.resok.properties = FSF3_SYMLINK | FSF3_HOMOGENEOUS; + } + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_FSINFO3res, sizeof(FSINFO3res)); + return 0; +} + +static int nfs3_pathconf_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + PATHCONF3args *args = call->body.cbody.args; + PATHCONF3res reply; + if (args->object.data.data_len != 10) + { + // Example error + reply.status = NFS3ERR_INVAL; + } + else + { + // Fill info + reply.status = NFS3_OK; + reply.PATHCONF3res_u.resok.obj_attributes.attributes_follow = FALSE; + reply.PATHCONF3res_u.resok.linkmax = 0; + reply.PATHCONF3res_u.resok.name_max = 255; + reply.PATHCONF3res_u.resok.no_trunc = TRUE; + reply.PATHCONF3res_u.resok.chown_restricted = FALSE; + reply.PATHCONF3res_u.resok.case_insensitive = FALSE; + reply.PATHCONF3res_u.resok.case_preserving = TRUE; + } + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_PATHCONF3res, sizeof(PATHCONF3res)); + return 0; +} + +static int nfs3_commit_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + COMMIT3args *args = call->body.cbody.args; + COMMIT3res reply; + + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_COMMIT3res, sizeof(COMMIT3res)); + return 0; +} + +static int mount3_mnt_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + dirpath *arg = call->body.cbody.args; + int flavor = AUTH_NONE; + mountres3 reply; + reply.fhs_status = MNT3_OK; + reply.mountres3_u.mountinfo.fhandle.fhandle3_len = 10; + reply.mountres3_u.mountinfo.fhandle.fhandle3_val = "roothandle"; + reply.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1; + reply.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &flavor; + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_mountres3, sizeof(mountres3)); + return 0; +} + +static int mount3_dump_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + mountlist reply; + reply = (struct mountbody*)malloc(sizeof(struct mountbody)); + reply->ml_hostname = "10.0.2.15"; + reply->ml_directory = "/test"; + reply->ml_next = NULL; + rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_mountlist, sizeof(mountlist)); + free(reply); + return 0; +} + +static int mount3_umnt_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + dirpath *arg = call->body.cbody.args; + // do nothing + rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); + return 0; +} + +static int mount3_umntall_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + // do nothing + rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); + return 0; +} + +static int mount3_export_proc(struct rpc_context *rpc, struct rpc_msg *call) +{ + exports reply; + reply = (struct exportnode*)malloc(sizeof(struct exportnode) + sizeof(struct groupnode)); + reply->ex_dir = "/test"; + reply->ex_groups = (struct groupnode*)(reply+1); + reply->ex_groups->gr_name = "10.0.2.15"; + reply->ex_groups->gr_next = NULL; + reply->ex_next = NULL; + rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_exports, sizeof(exports)); + free(reply); + return 0; +} + +struct service_proc nfs3_pt[22] = { + {NFS3_NULL, nfs3_null_proc, (zdrproc_t)zdr_void, 0}, + {NFS3_GETATTR, nfs3_getattr_proc, (zdrproc_t)zdr_GETATTR3args, sizeof(GETATTR3args)}, + {NFS3_SETATTR, nfs3_setattr_proc, (zdrproc_t)zdr_SETATTR3args, sizeof(SETATTR3args)}, + {NFS3_LOOKUP, nfs3_lookup_proc, (zdrproc_t)zdr_LOOKUP3args, sizeof(LOOKUP3args)}, + {NFS3_ACCESS, nfs3_access_proc, (zdrproc_t)zdr_ACCESS3args, sizeof(ACCESS3args)}, + {NFS3_READLINK, nfs3_readlink_proc, (zdrproc_t)zdr_READLINK3args, sizeof(READLINK3args)}, + {NFS3_READ, nfs3_read_proc, (zdrproc_t)zdr_READ3args, sizeof(READ3args)}, + {NFS3_WRITE, nfs3_write_proc, (zdrproc_t)zdr_WRITE3args, sizeof(WRITE3args)}, + {NFS3_CREATE, nfs3_create_proc, (zdrproc_t)zdr_CREATE3args, sizeof(CREATE3args)}, + {NFS3_MKDIR, nfs3_mkdir_proc, (zdrproc_t)zdr_MKDIR3args, sizeof(MKDIR3args)}, + {NFS3_SYMLINK, nfs3_symlink_proc, (zdrproc_t)zdr_SYMLINK3args, sizeof(SYMLINK3args)}, + {NFS3_MKNOD, nfs3_mknod_proc, (zdrproc_t)zdr_MKNOD3args, sizeof(MKNOD3args)}, + {NFS3_REMOVE, nfs3_remove_proc, (zdrproc_t)zdr_REMOVE3args, sizeof(REMOVE3args)}, + {NFS3_RMDIR, nfs3_rmdir_proc, (zdrproc_t)zdr_RMDIR3args, sizeof(RMDIR3args)}, + {NFS3_RENAME, nfs3_rename_proc, (zdrproc_t)zdr_RENAME3args, sizeof(RENAME3args)}, + {NFS3_LINK, nfs3_link_proc, (zdrproc_t)zdr_LINK3args, sizeof(LINK3args)}, + {NFS3_READDIR, nfs3_readdir_proc, (zdrproc_t)zdr_READDIR3args, sizeof(READDIR3args)}, + {NFS3_READDIRPLUS, nfs3_readdirplus_proc, (zdrproc_t)zdr_READDIRPLUS3args, sizeof(READDIRPLUS3args)}, + {NFS3_FSSTAT, nfs3_fsstat_proc, (zdrproc_t)zdr_FSSTAT3args, sizeof(FSSTAT3args)}, + {NFS3_FSINFO, nfs3_fsinfo_proc, (zdrproc_t)zdr_FSINFO3args, sizeof(FSINFO3args)}, + {NFS3_PATHCONF, nfs3_pathconf_proc, (zdrproc_t)zdr_PATHCONF3args, sizeof(PATHCONF3args)}, + {NFS3_COMMIT, nfs3_commit_proc, (zdrproc_t)zdr_COMMIT3args, sizeof(COMMIT3args)}, +}; + +struct service_proc nfs3_mount_pt[6] = { + {MOUNT3_NULL, nfs3_null_proc, (zdrproc_t)zdr_void, 0}, + {MOUNT3_MNT, mount3_mnt_proc, (zdrproc_t)zdr_dirpath, sizeof(dirpath)}, + {MOUNT3_DUMP, mount3_dump_proc, (zdrproc_t)zdr_void, 0}, + {MOUNT3_UMNT, mount3_umnt_proc, (zdrproc_t)zdr_dirpath, sizeof(dirpath)}, + {MOUNT3_UMNTALL, mount3_umntall_proc, (zdrproc_t)zdr_void, 0}, + {MOUNT3_EXPORT, mount3_export_proc, (zdrproc_t)zdr_void, 0}, +}; diff --git a/nfs-service.h b/nfs-service.h new file mode 100644 index 0000000..75c7a34 --- /dev/null +++ b/nfs-service.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-raw-mount.h" +#include "libnfs-raw-nfs.h" +#include "libnfs-raw-portmap.h" + +extern struct service_proc nfs3_pt[22]; +extern struct service_proc nfs3_mount_pt[6];