/*
Copyright (c) 2012 Gluster, Inc. <http://www.gluster.com>
This file is part of GlusterFS.
This file is licensed to you under your choice of the GNU Lesser
General Public License, version 3 or any later version (LGPLv3 or
later), or the GNU General Public License, version 2 (GPLv2), in all
cases as published by the Free Software Foundation.
*/
#include "xdr-nfs3.h"
#include <glusterfs/logging.h>
#include <glusterfs/mem-pool.h>
#include "nfs-mem-types.h"
#include "nfs-messages.h"
#include "mount3.h"
#include <stdio.h>
#include <stdlib.h>
#include <rpc/pmap_clnt.h>
#include <string.h>
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>
extern struct nfs3_fh *
nfs3_rootfh(struct svc_req *req, xlator_t *nfsx, char *dp, char *expname);
extern mountres3
mnt3svc_set_mountres3(mountstat3 stat, struct nfs3_fh *fh, int *authflavor,
u_int aflen);
extern int
mount3udp_add_mountlist(xlator_t *nfsx, char *host, char *expname);
extern int
mount3udp_delete_mountlist(xlator_t *nfsx, char *host, char *expname);
extern mountstat3
mnt3svc_errno_to_mnterr(int32_t errnum);
/* only this thread will use this, no locking needed */
char mnthost[INET_ADDRSTRLEN + 1];
#define MNT3UDP_AUTH_LEN 1 /* Only AUTH_UNIX for now */
mountres3 *
mountudpproc3_mnt_3_svc(dirpath **dpp, struct svc_req *req)
{
struct mountres3 *res = NULL;
int *autharr = NULL;
struct nfs3_fh *fh = NULL;
char *mpath = NULL;
xlator_t *nfsx = THIS;
char expname[PATH_MAX] = {
0,
};
mountstat3 stat = MNT3ERR_SERVERFAULT;
errno = 0; /* RESET errno */
mpath = (char *)*dpp;
while (*mpath == '/')
mpath++;
res = GF_CALLOC(1, sizeof(*res), gf_nfs_mt_mountres3);
if (res == NULL) {
gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
"Unable to allocate memory");
goto err;
}
autharr = GF_CALLOC(MNT3UDP_AUTH_LEN, sizeof(int), gf_nfs_mt_int);
if (autharr == NULL) {
gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
"Unable to allocate memory");
goto err;
}
autharr[0] = AUTH_UNIX;
fh = nfs3_rootfh(req, nfsx, mpath, (char *)expname);
/* FAILURE: No FH */
if (fh == NULL) {
gf_msg(GF_MNT, GF_LOG_ERROR, errno, NFS_MSG_GET_FH_FAIL,
"Unable to get fh for %s", mpath);
if (errno)
stat = mnt3svc_errno_to_mnterr(errno);
*res = mnt3svc_set_mountres3(stat, NULL /* fh */, autharr,
MNT3UDP_AUTH_LEN);
return res;
}
/* SUCCESS */
stat = MNT3_OK;
*res = mnt3svc_set_mountres3(stat, fh, autharr, MNT3UDP_AUTH_LEN);
(void)mount3udp_add_mountlist(nfsx, mnthost, (char *)expname);
return res;
err:
GF_FREE(fh);
GF_FREE(res);
GF_FREE(autharr);
return NULL;
}
mountstat3 *
mountudpproc3_umnt_3_svc(dirpath **dp, struct svc_req *req)
{
mountstat3 *stat = NULL;
char *mpath = (char *)*dp;
xlator_t *nfsx = THIS;
stat = GF_MALLOC(sizeof(mountstat3), gf_nfs_mt_mountstat3);
if (stat == NULL) {
gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
"Unable to allocate memory");
return NULL;
}
*stat = MNT3_OK;
(void)mount3udp_delete_mountlist(nfsx, mnthost, mpath);
return stat;
}
static void
mountudp_program_3(struct svc_req *rqstp, register SVCXPRT *transp)
{
union {
dirpath mountudpproc3_mnt_3_arg;
} argument;
char *result = NULL;
xdrproc_t _xdr_argument = NULL, _xdr_result = NULL;
char *(*local)(char *, struct svc_req *) = NULL;
mountres3 *res = NULL;
struct sockaddr_in *sin = NULL;
#if !defined(_TIRPC_SVC_H)
sin = svc_getcaller(transp);
#else
sin = (struct sockaddr_in *)svc_getcaller(transp);
/* TIRPC's svc_getcaller() returns a pointer to a sockaddr_in6, even
* though it might actually be an IPv4 address. It ought return a
* struct sockaddr and make the caller upcast it to the proper
* address family. Sigh.
*/
#endif
/* And let's make sure that it's actually an IPv4 address. */
GF_ASSERT(sin->sin_family == AF_INET);
inet_ntop(AF_INET, &sin->sin_addr, mnthost, INET_ADDRSTRLEN + 1);
switch (rqstp->rq_proc) {
case NULLPROC:
(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
return;
case MOUNT3_MNT:
_xdr_argument = (xdrproc_t)xdr_dirpath;
_xdr_result = (xdrproc_t)xdr_mountres3;
local = (char *(*)(char *,
struct svc_req *))mountudpproc3_mnt_3_svc;
break;
case MOUNT3_UMNT:
_xdr_argument = (xdrproc_t)xdr_dirpath;
_xdr_result = (xdrproc_t)xdr_mountstat3;
local = (char *(*)(char *,
struct svc_req *))mountudpproc3_umnt_3_svc;
break;
default:
svcerr_noproc(transp);
return;
}
memset((char *)&argument, 0, sizeof(argument));
if (!svc_getargs(transp, (xdrproc_t)_xdr_argument, (caddr_t)&argument)) {
svcerr_decode(transp);
return;
}
result = (*local)((char *)&argument, rqstp);
if (result == NULL) {
gf_msg_debug(GF_MNT, 0, "PROC returned error");
svcerr_systemerr(transp);
}
if (result != NULL &&
!svc_sendreply(transp, (xdrproc_t)_xdr_result, result)) {
gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_ERROR,
"svc_sendreply returned error");
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, (xdrproc_t)_xdr_argument, (caddr_t)&argument)) {
gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_ARG_FREE_FAIL,
"Unable to free arguments");
}
if (result == NULL)
return;
/* free the result */
switch (rqstp->rq_proc) {
case MOUNT3_MNT:
res = (mountres3 *)result;
GF_FREE(res->mountres3_u.mountinfo.fhandle.fhandle3_val);
GF_FREE(res->mountres3_u.mountinfo.auth_flavors.auth_flavors_val);
GF_FREE(res);
break;
case MOUNT3_UMNT:
GF_FREE(result);
break;
}
return;
}
void *
mount3udp_thread(void *argv)
{
xlator_t *nfsx = argv;
register SVCXPRT *transp = NULL;
GF_ASSERT(nfsx);
glusterfs_this_set(nfsx);
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL) {
gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_ERROR,
"svcudp_create error");
return NULL;
}
if (!svc_register(transp, MOUNT_PROGRAM, MOUNT_V3, mountudp_program_3,
IPPROTO_UDP)) {
gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_ERROR,
"svc_register error");
return NULL;
}
svc_run();
gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_RUN_RETURNED,
"svc_run returned");
return NULL;
}