299 lines
8.7 KiB
C
299 lines
8.7 KiB
C
|
|
/* Copyright (c) 2010
|
||
|
|
* The Regents of the University of Michigan
|
||
|
|
* All Rights Reserved
|
||
|
|
*
|
||
|
|
* Permission is granted to use, copy and redistribute this software
|
||
|
|
* for noncommercial education and research purposes, so long as no
|
||
|
|
* fee is charged, and so long as the name of the University of Michigan
|
||
|
|
* is not used in any advertising or publicity pertaining to the use
|
||
|
|
* or distribution of this software without specific, written prior
|
||
|
|
* authorization. Permission to modify or otherwise create derivative
|
||
|
|
* works of this software is not granted.
|
||
|
|
*
|
||
|
|
* This software is provided as is, without representation or warranty
|
||
|
|
* of any kind either express or implied, including without limitation
|
||
|
|
* the implied warranties of merchantability, fitness for a particular
|
||
|
|
* purpose, or noninfringement. The Regents of the University of
|
||
|
|
* Michigan shall not be liable for any damages, including special,
|
||
|
|
* indirect, incidental, or consequential damages, with respect to any
|
||
|
|
* claim arising out of or in connection with the use of the software,
|
||
|
|
* even if it has been or is hereafter advised of the possibility of
|
||
|
|
* such damages.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "nfs41.h"
|
||
|
|
#include "daemon_debug.h"
|
||
|
|
#include "nfs41_xdr.h"
|
||
|
|
#include "nfs41_callback.h"
|
||
|
|
|
||
|
|
#include "rpc/rpc.h"
|
||
|
|
|
||
|
|
static enum clnt_stat send_null(CLIENT *client)
|
||
|
|
{
|
||
|
|
struct timeval timeout = {10, 0};
|
||
|
|
|
||
|
|
return clnt_call(client, 0,
|
||
|
|
(xdrproc_t)xdr_void, NULL,
|
||
|
|
(xdrproc_t)xdr_void, NULL, timeout);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int get_client_for_netaddr(
|
||
|
|
IN const netaddr4 *netaddr,
|
||
|
|
IN uint32_t wsize,
|
||
|
|
IN uint32_t rsize,
|
||
|
|
IN nfs41_rpc_clnt *rpc,
|
||
|
|
OUT CLIENT **client_out)
|
||
|
|
{
|
||
|
|
int status = ERROR_NETWORK_UNREACHABLE;
|
||
|
|
struct netconfig *nconf;
|
||
|
|
struct netbuf *addr;
|
||
|
|
CLIENT *client;
|
||
|
|
|
||
|
|
nconf = getnetconfigent(netaddr->netid);
|
||
|
|
if (nconf == NULL)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
addr = uaddr2taddr(nconf, netaddr->uaddr);
|
||
|
|
if (addr == NULL)
|
||
|
|
goto out_free_conf;
|
||
|
|
|
||
|
|
dprintf(1, "callback function %p args %p\n", nfs41_handle_callback, rpc);
|
||
|
|
client = clnt_tli_create(RPC_ANYFD, nconf, addr,
|
||
|
|
NFS41_RPC_PROGRAM, NFS41_RPC_VERSION, wsize, rsize,
|
||
|
|
rpc?proc_cb_compound_res:NULL, rpc?nfs41_handle_callback:NULL, rpc?rpc:NULL);
|
||
|
|
if (client) {
|
||
|
|
*client_out = client;
|
||
|
|
status = NO_ERROR;
|
||
|
|
goto out_free_addr;
|
||
|
|
}
|
||
|
|
out_free_addr:
|
||
|
|
freenetbuf(addr);
|
||
|
|
out_free_conf:
|
||
|
|
freenetconfigent(nconf);
|
||
|
|
out:
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int get_client_for_multi_addr(
|
||
|
|
IN const multi_addr4 *addrs,
|
||
|
|
IN uint32_t wsize,
|
||
|
|
IN uint32_t rsize,
|
||
|
|
IN nfs41_rpc_clnt *rpc,
|
||
|
|
OUT CLIENT **client_out,
|
||
|
|
OUT uint32_t *addr_index)
|
||
|
|
{
|
||
|
|
int status = ERROR_NETWORK_UNREACHABLE;
|
||
|
|
uint32_t i;
|
||
|
|
for (i = 0; i < addrs->count; i++) {
|
||
|
|
status = get_client_for_netaddr(&addrs->arr[i],
|
||
|
|
wsize, rsize, rpc, client_out);
|
||
|
|
if (status == NO_ERROR) {
|
||
|
|
*addr_index = i;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Returns a client structure and an associated lock */
|
||
|
|
int nfs41_rpc_clnt_create(
|
||
|
|
IN const multi_addr4 *addrs,
|
||
|
|
IN uint32_t wsize,
|
||
|
|
IN uint32_t rsize,
|
||
|
|
bool_t needcb,
|
||
|
|
OUT nfs41_rpc_clnt **rpc_out)
|
||
|
|
{
|
||
|
|
CLIENT *client;
|
||
|
|
nfs41_rpc_clnt *rpc;
|
||
|
|
uint32_t addr_index;
|
||
|
|
int status;
|
||
|
|
|
||
|
|
rpc = calloc(1, sizeof(nfs41_rpc_clnt));
|
||
|
|
if (rpc == NULL) {
|
||
|
|
status = GetLastError();
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
rpc->cond = CreateEvent(NULL, TRUE, FALSE, "rpc_recovery_cond");
|
||
|
|
if (rpc->cond == NULL) {
|
||
|
|
status = GetLastError();
|
||
|
|
fprintf(stderr, "CreateEvent failed %d\n", status);
|
||
|
|
goto out_free_rpc_clnt;
|
||
|
|
}
|
||
|
|
|
||
|
|
status = get_client_for_multi_addr(addrs,
|
||
|
|
wsize, rsize, needcb?rpc:NULL, &client, &addr_index);
|
||
|
|
if (status) {
|
||
|
|
clnt_pcreateerror("connecting failed");
|
||
|
|
goto out_free_rpc_clnt;
|
||
|
|
}
|
||
|
|
// XXX Pick credentials in better manner
|
||
|
|
client->cl_auth = authsys_create_default();
|
||
|
|
if (client->cl_auth == NULL) {
|
||
|
|
// XXX log failure in auth creation somewhere
|
||
|
|
// XXX Better error return
|
||
|
|
status = ERROR_NETWORK_UNREACHABLE;
|
||
|
|
goto out_err_client;
|
||
|
|
}
|
||
|
|
if (send_null(client) != RPC_SUCCESS) {
|
||
|
|
// XXX Do what here?
|
||
|
|
dprintf(1, " send_null failed\n");
|
||
|
|
status = ERROR_NETWORK_UNREACHABLE;
|
||
|
|
goto out_err_auth;
|
||
|
|
}
|
||
|
|
|
||
|
|
rpc->rpc = client;
|
||
|
|
|
||
|
|
/* keep a copy of the address and buffer sizes for reconnect */
|
||
|
|
memcpy(&rpc->addrs, addrs, sizeof(multi_addr4));
|
||
|
|
/* save the index of the address we connected to */
|
||
|
|
rpc->addr_index = addr_index;
|
||
|
|
rpc->wsize = wsize;
|
||
|
|
rpc->rsize = rsize;
|
||
|
|
rpc->is_valid_session = TRUE;
|
||
|
|
|
||
|
|
//initialize rpc client lock
|
||
|
|
InitializeSRWLock(&rpc->lock);
|
||
|
|
|
||
|
|
*rpc_out = rpc;
|
||
|
|
out:
|
||
|
|
return status;
|
||
|
|
out_err_auth:
|
||
|
|
auth_destroy(client->cl_auth);
|
||
|
|
out_err_client:
|
||
|
|
CloseHandle(rpc->cond);
|
||
|
|
clnt_destroy(client);
|
||
|
|
out_free_rpc_clnt:
|
||
|
|
free(rpc);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Frees resources allocated in clnt_create */
|
||
|
|
void nfs41_rpc_clnt_free(
|
||
|
|
IN nfs41_rpc_clnt *rpc)
|
||
|
|
{
|
||
|
|
auth_destroy(rpc->rpc->cl_auth);
|
||
|
|
clnt_destroy(rpc->rpc);
|
||
|
|
CloseHandle(rpc->cond);
|
||
|
|
free(rpc);
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool_t rpc_renew_in_progress(nfs41_rpc_clnt *rpc, int *value)
|
||
|
|
{
|
||
|
|
bool_t status = FALSE;
|
||
|
|
AcquireSRWLockExclusive(&rpc->lock);
|
||
|
|
if (value) {
|
||
|
|
dprintf(1, "nfs41_rpc_renew_in_progress: setting value %d\n", *value);
|
||
|
|
rpc->in_recovery = *value;
|
||
|
|
if (!rpc->in_recovery)
|
||
|
|
SetEvent(rpc->cond);
|
||
|
|
} else {
|
||
|
|
status = rpc->in_recovery;
|
||
|
|
dprintf(1, "nfs41_rpc_renew_in_progress: returning value %d\n", status);
|
||
|
|
}
|
||
|
|
ReleaseSRWLockExclusive(&rpc->lock);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool_t rpc_should_retry(nfs41_rpc_clnt *rpc, uint32_t version)
|
||
|
|
{
|
||
|
|
bool_t status = 0;
|
||
|
|
AcquireSRWLockExclusive(&rpc->lock);
|
||
|
|
if (rpc->version > version)
|
||
|
|
status = 1;
|
||
|
|
ReleaseSRWLockExclusive(&rpc->lock);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int rpc_reconnect(
|
||
|
|
IN nfs41_rpc_clnt *rpc)
|
||
|
|
{
|
||
|
|
CLIENT *client = NULL;
|
||
|
|
uint32_t addr_index;
|
||
|
|
int status;
|
||
|
|
|
||
|
|
AcquireSRWLockExclusive(&rpc->lock);
|
||
|
|
|
||
|
|
status = get_client_for_multi_addr(&rpc->addrs,
|
||
|
|
rpc->wsize, rpc->rsize, rpc, &client, &addr_index);
|
||
|
|
if (status)
|
||
|
|
goto out_unlock;
|
||
|
|
|
||
|
|
client->cl_auth = rpc->rpc->cl_auth;
|
||
|
|
if (send_null(client) != RPC_SUCCESS)
|
||
|
|
goto out_err_client;
|
||
|
|
|
||
|
|
clnt_destroy(rpc->rpc);
|
||
|
|
rpc->rpc = client;
|
||
|
|
rpc->addr_index = addr_index;
|
||
|
|
rpc->version++;
|
||
|
|
dprintf(1, "nfs41_send_compound: reestablished RPC connection\n");
|
||
|
|
|
||
|
|
out_unlock:
|
||
|
|
ReleaseSRWLockExclusive(&rpc->lock);
|
||
|
|
return status;
|
||
|
|
|
||
|
|
out_err_client:
|
||
|
|
clnt_destroy(client);
|
||
|
|
goto out_unlock;
|
||
|
|
}
|
||
|
|
|
||
|
|
int nfs41_send_compound(
|
||
|
|
IN nfs41_rpc_clnt *rpc,
|
||
|
|
IN char *inbuf,
|
||
|
|
OUT char *outbuf)
|
||
|
|
{
|
||
|
|
struct timeval timeout = {10, 0};
|
||
|
|
enum clnt_stat rpc_status;
|
||
|
|
int status, count = 0, one = 1, zero = 0;
|
||
|
|
uint32_t version;
|
||
|
|
|
||
|
|
try_again:
|
||
|
|
AcquireSRWLockShared(&rpc->lock);
|
||
|
|
version = rpc->version;
|
||
|
|
rpc_status = clnt_call(rpc->rpc, 1,
|
||
|
|
(xdrproc_t)nfs_encode_compound, inbuf,
|
||
|
|
(xdrproc_t)nfs_decode_compound, outbuf,
|
||
|
|
timeout);
|
||
|
|
ReleaseSRWLockShared(&rpc->lock);
|
||
|
|
|
||
|
|
if (rpc_status != RPC_SUCCESS) {
|
||
|
|
eprintf("clnt_call returned rpc_status=%i\n", rpc_status);
|
||
|
|
switch(rpc_status) {
|
||
|
|
case RPC_CANTRECV:
|
||
|
|
case RPC_CANTSEND:
|
||
|
|
case RPC_TIMEDOUT:
|
||
|
|
if (!rpc->is_valid_session && ++count > 3) {
|
||
|
|
status = ERROR_NETWORK_UNREACHABLE;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (rpc_should_retry(rpc, version))
|
||
|
|
goto try_again;
|
||
|
|
while (rpc_renew_in_progress(rpc, NULL)) {
|
||
|
|
status = WaitForSingleObject(rpc->cond, INFINITE);
|
||
|
|
if (status != WAIT_OBJECT_0) {
|
||
|
|
dprintf(1, "nfs41_rpc_renew_in_progress: WaitForSingleObject failed\n");
|
||
|
|
print_condwait_status(1, status);
|
||
|
|
status = ERROR_LOCK_VIOLATION;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
rpc_renew_in_progress(rpc, &zero);
|
||
|
|
goto try_again;
|
||
|
|
}
|
||
|
|
rpc_renew_in_progress(rpc, &one);
|
||
|
|
if (rpc_reconnect(rpc))
|
||
|
|
eprintf("Failed to reconnect!\n");
|
||
|
|
rpc_renew_in_progress(rpc, &zero);
|
||
|
|
goto try_again;
|
||
|
|
default:
|
||
|
|
eprintf("UNHANDLED RPC_ERROR: %d\n", rpc_status);
|
||
|
|
status = ERROR_NETWORK_UNREACHABLE;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
status = 0;
|
||
|
|
out:
|
||
|
|
return status;
|
||
|
|
}
|