Example NFS server using libnfs
commit
848e497970
|
@ -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
|
|
@ -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);
|
|
@ -0,0 +1,542 @@
|
|||
#define _FILE_OFFSET_BITS 64
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
#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("<unknown>"));
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,355 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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},
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/time.h>
|
||||
#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];
|
Loading…
Reference in New Issue