#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/vtrace.h>
#include <sys/session.h>
#include <sys/thread.h>
#include <sys/dnlc.h>
#include <sys/cred.h>
#include <sys/priv.h>
#include <sys/list.h>
#include <sys/sdt.h>
#include <sys/policy.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <nfs/nfs.h>
#include <nfs/nfs_clnt.h>
#include <nfs/nfs4.h>
#include <nfs/rnode4.h>
#include <nfs/nfs4_clnt.h>
static const struct clstat4 clstat4_tmpl = {
{ "calls", KSTAT_DATA_UINT64 },
{ "badcalls", KSTAT_DATA_UINT64 },
{ "referrals", KSTAT_DATA_UINT64 },
{ "referlinks", KSTAT_DATA_UINT64 },
{ "clgets", KSTAT_DATA_UINT64 },
{ "cltoomany", KSTAT_DATA_UINT64 },
#ifdef DEBUG
{ "clalloc", KSTAT_DATA_UINT64 },
{ "noresponse", KSTAT_DATA_UINT64 },
{ "failover", KSTAT_DATA_UINT64 },
{ "remap", KSTAT_DATA_UINT64 },
#endif
};
#ifdef DEBUG
struct clstat4_debug clstat4_debug = {
{ "nrnode", KSTAT_DATA_UINT64 },
{ "access", KSTAT_DATA_UINT64 },
{ "dirent", KSTAT_DATA_UINT64 },
{ "dirents", KSTAT_DATA_UINT64 },
{ "reclaim", KSTAT_DATA_UINT64 },
{ "clreclaim", KSTAT_DATA_UINT64 },
{ "f_reclaim", KSTAT_DATA_UINT64 },
{ "a_reclaim", KSTAT_DATA_UINT64 },
{ "r_reclaim", KSTAT_DATA_UINT64 },
{ "r_path", KSTAT_DATA_UINT64 },
};
#endif
static list_t nfs4_clnt_list;
static kmutex_t nfs4_clnt_list_lock;
zone_key_t nfs4clnt_zone_key;
static struct kmem_cache *chtab4_cache;
#ifdef DEBUG
static int nfs4_rfscall_debug;
static int nfs4_try_failover_any;
int nfs4_utf8_debug = 0;
#endif
typedef struct rddir4_cache_impl {
rddir4_cache rc;
kmutex_t lock;
uint_t count;
avl_node_t tree;
} rddir4_cache_impl;
static int rddir4_cache_compar(const void *, const void *);
static void rddir4_cache_free(rddir4_cache_impl *);
static rddir4_cache *rddir4_cache_alloc(int);
static void rddir4_cache_hold(rddir4_cache *);
static int try_failover(enum clnt_stat);
static int nfs4_readdir_cache_hits = 0;
static int nfs4_readdir_cache_waits = 0;
static int nfs4_readdir_cache_misses = 0;
void
nfs_fh4_copy(nfs_fh4 *from, nfs_fh4 *to)
{
to->nfs_fh4_len = from->nfs_fh4_len;
bcopy(from->nfs_fh4_val, to->nfs_fh4_val, to->nfs_fh4_len);
}
int
nfs4cmpfh(const nfs_fh4 *fh4p1, const nfs_fh4 *fh4p2)
{
const char *c1, *c2;
if (fh4p1->nfs_fh4_len < fh4p2->nfs_fh4_len)
return (-1);
if (fh4p1->nfs_fh4_len > fh4p2->nfs_fh4_len)
return (1);
for (c1 = fh4p1->nfs_fh4_val, c2 = fh4p2->nfs_fh4_val;
c1 < fh4p1->nfs_fh4_val + fh4p1->nfs_fh4_len;
c1++, c2++) {
if (*c1 < *c2)
return (-1);
if (*c1 > *c2)
return (1);
}
return (0);
}
int
nfs4cmpfhandle(nfs4_fhandle_t *fh1, nfs4_fhandle_t *fh2)
{
if (fh1->fh_len == fh2->fh_len)
return (bcmp(fh1->fh_buf, fh2->fh_buf, fh1->fh_len));
return (1);
}
int
stateid4_cmp(stateid4 *s1, stateid4 *s2)
{
if (bcmp(s1, s2, sizeof (stateid4)) == 0)
return (1);
else
return (0);
}
nfsstat4
puterrno4(int error)
{
switch (error) {
case 0:
return (NFS4_OK);
case EPERM:
return (NFS4ERR_PERM);
case ENOENT:
return (NFS4ERR_NOENT);
case EINTR:
return (NFS4ERR_IO);
case EIO:
return (NFS4ERR_IO);
case ENXIO:
return (NFS4ERR_NXIO);
case ENOMEM:
return (NFS4ERR_RESOURCE);
case EACCES:
return (NFS4ERR_ACCESS);
case EBUSY:
return (NFS4ERR_IO);
case EEXIST:
return (NFS4ERR_EXIST);
case EXDEV:
return (NFS4ERR_XDEV);
case ENODEV:
return (NFS4ERR_IO);
case ENOTDIR:
return (NFS4ERR_NOTDIR);
case EISDIR:
return (NFS4ERR_ISDIR);
case EINVAL:
return (NFS4ERR_INVAL);
case EMFILE:
return (NFS4ERR_RESOURCE);
case EFBIG:
return (NFS4ERR_FBIG);
case ENOSPC:
return (NFS4ERR_NOSPC);
case EROFS:
return (NFS4ERR_ROFS);
case EMLINK:
return (NFS4ERR_MLINK);
case EDEADLK:
return (NFS4ERR_DEADLOCK);
case ENOLCK:
return (NFS4ERR_DENIED);
case EREMOTE:
return (NFS4ERR_SERVERFAULT);
case ENOTSUP:
return (NFS4ERR_NOTSUPP);
case EDQUOT:
return (NFS4ERR_DQUOT);
case ENAMETOOLONG:
return (NFS4ERR_NAMETOOLONG);
case EOVERFLOW:
return (NFS4ERR_INVAL);
case ENOSYS:
return (NFS4ERR_NOTSUPP);
case ENOTEMPTY:
return (NFS4ERR_NOTEMPTY);
case EOPNOTSUPP:
return (NFS4ERR_NOTSUPP);
case ESTALE:
return (NFS4ERR_STALE);
case EAGAIN:
if (curthread->t_flag & T_WOULDBLOCK) {
curthread->t_flag &= ~T_WOULDBLOCK;
return (NFS4ERR_DELAY);
}
return (NFS4ERR_LOCKED);
default:
return ((enum nfsstat4)error);
}
}
int
geterrno4(enum nfsstat4 status)
{
switch (status) {
case NFS4_OK:
return (0);
case NFS4ERR_PERM:
return (EPERM);
case NFS4ERR_NOENT:
return (ENOENT);
case NFS4ERR_IO:
return (EIO);
case NFS4ERR_NXIO:
return (ENXIO);
case NFS4ERR_ACCESS:
return (EACCES);
case NFS4ERR_EXIST:
return (EEXIST);
case NFS4ERR_XDEV:
return (EXDEV);
case NFS4ERR_NOTDIR:
return (ENOTDIR);
case NFS4ERR_ISDIR:
return (EISDIR);
case NFS4ERR_INVAL:
return (EINVAL);
case NFS4ERR_FBIG:
return (EFBIG);
case NFS4ERR_NOSPC:
return (ENOSPC);
case NFS4ERR_ROFS:
return (EROFS);
case NFS4ERR_MLINK:
return (EMLINK);
case NFS4ERR_NAMETOOLONG:
return (ENAMETOOLONG);
case NFS4ERR_NOTEMPTY:
return (ENOTEMPTY);
case NFS4ERR_DQUOT:
return (EDQUOT);
case NFS4ERR_STALE:
return (ESTALE);
case NFS4ERR_BADHANDLE:
return (ESTALE);
case NFS4ERR_BAD_COOKIE:
return (EINVAL);
case NFS4ERR_NOTSUPP:
return (EOPNOTSUPP);
case NFS4ERR_TOOSMALL:
return (EINVAL);
case NFS4ERR_SERVERFAULT:
return (EIO);
case NFS4ERR_BADTYPE:
return (EINVAL);
case NFS4ERR_DELAY:
return (ENXIO);
case NFS4ERR_SAME:
return (EPROTO);
case NFS4ERR_DENIED:
return (ENOLCK);
case NFS4ERR_EXPIRED:
return (EPROTO);
case NFS4ERR_LOCKED:
return (EACCES);
case NFS4ERR_GRACE:
return (EAGAIN);
case NFS4ERR_FHEXPIRED:
return (ESTALE);
case NFS4ERR_SHARE_DENIED:
return (EACCES);
case NFS4ERR_WRONGSEC:
return (EPERM);
case NFS4ERR_CLID_INUSE:
return (EAGAIN);
case NFS4ERR_RESOURCE:
return (EAGAIN);
case NFS4ERR_MOVED:
return (EPROTO);
case NFS4ERR_NOFILEHANDLE:
return (EIO);
case NFS4ERR_MINOR_VERS_MISMATCH:
return (ENOTSUP);
case NFS4ERR_STALE_CLIENTID:
return (EIO);
case NFS4ERR_STALE_STATEID:
return (EIO);
case NFS4ERR_OLD_STATEID:
return (EIO);
case NFS4ERR_BAD_STATEID:
return (EIO);
case NFS4ERR_BAD_SEQID:
return (EIO);
case NFS4ERR_NOT_SAME:
return (EPROTO);
case NFS4ERR_LOCK_RANGE:
return (EPROTO);
case NFS4ERR_SYMLINK:
return (EPROTO);
case NFS4ERR_RESTOREFH:
return (EPROTO);
case NFS4ERR_LEASE_MOVED:
return (EPROTO);
case NFS4ERR_ATTRNOTSUPP:
return (ENOTSUP);
case NFS4ERR_NO_GRACE:
return (EPROTO);
case NFS4ERR_RECLAIM_BAD:
return (EPROTO);
case NFS4ERR_RECLAIM_CONFLICT:
return (EPROTO);
case NFS4ERR_BADXDR:
return (EINVAL);
case NFS4ERR_LOCKS_HELD:
return (EIO);
case NFS4ERR_OPENMODE:
return (EACCES);
case NFS4ERR_BADOWNER:
return (EACCES);
case NFS4ERR_BADCHAR:
return (EINVAL);
case NFS4ERR_BADNAME:
return (EINVAL);
case NFS4ERR_BAD_RANGE:
return (EIO);
case NFS4ERR_LOCK_NOTSUPP:
return (ENOTSUP);
case NFS4ERR_OP_ILLEGAL:
return (EINVAL);
case NFS4ERR_DEADLOCK:
return (EDEADLK);
case NFS4ERR_FILE_OPEN:
return (EACCES);
case NFS4ERR_ADMIN_REVOKED:
return (EPROTO);
case NFS4ERR_CB_PATH_DOWN:
return (EPROTO);
default:
#ifdef DEBUG
zcmn_err(getzoneid(), CE_WARN, "geterrno4: got status %d",
status);
#endif
return ((int)status);
}
}
void
nfs4_log_badowner(mntinfo4_t *mi, nfs_opnum4 op)
{
nfs4_server_t *server;
if (mi->mi_flags & MI4_BADOWNER_DEBUG)
return;
if (nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER,
mi->mi_flags & MI4_INT))
return;
server = find_nfs4_server(mi);
if (server == NULL) {
nfs_rw_exit(&mi->mi_recovlock);
return;
}
if (!(server->s_flags & N4S_BADOWNER_DEBUG)) {
zcmn_err(mi->mi_zone->zone_id, CE_WARN,
"!NFSMAPID_DOMAIN does not match"
" the server: %s domain.\n"
"Please check configuration",
mi->mi_curr_serv->sv_hostname);
server->s_flags |= N4S_BADOWNER_DEBUG;
}
mutex_exit(&server->s_lock);
nfs4_server_rele(server);
nfs_rw_exit(&mi->mi_recovlock);
mutex_enter(&mi->mi_lock);
if (!(mi->mi_flags & MI4_BADOWNER_DEBUG)) {
nfs4_queue_fact(RF_BADOWNER, mi, NFS4ERR_BADOWNER, 0, op,
FALSE, NULL, 0, NULL);
mi->mi_flags |= MI4_BADOWNER_DEBUG;
}
mutex_exit(&mi->mi_lock);
}
int
nfs4_time_ntov(nfstime4 *ntime, timestruc_t *vatime)
{
int64_t sec;
int32_t nsec;
#ifndef _LP64
if (! NFS4_TIME_OK(ntime->seconds)) {
return (EOVERFLOW);
}
#endif
if (ntime->nseconds >= 1000000000)
return (EINVAL);
if (ntime->seconds < 0) {
sec = ntime->seconds + 1;
nsec = -1000000000 + ntime->nseconds;
} else {
sec = ntime->seconds;
nsec = ntime->nseconds;
}
vatime->tv_sec = sec;
vatime->tv_nsec = nsec;
return (0);
}
int
nfs4_time_vton(timestruc_t *vatime, nfstime4 *ntime)
{
int64_t sec;
uint32_t nsec;
if (vatime->tv_nsec >= 0) {
sec = vatime->tv_sec;
nsec = vatime->tv_nsec;
} else {
sec = vatime->tv_sec - 1;
nsec = 1000000000 + vatime->tv_nsec;
}
ntime->seconds = sec;
ntime->nseconds = nsec;
return (0);
}
char *
utf8_to_fn(utf8string *u8s, uint_t *lenp, char *s)
{
ASSERT(lenp != NULL);
if (u8s == NULL || u8s->utf8string_len <= 0 ||
u8s->utf8string_val == NULL)
return (NULL);
if (utf8_strchr(u8s, '/') != NULL) {
#ifdef DEBUG
if (nfs4_utf8_debug) {
char *path;
int len = u8s->utf8string_len;
path = kmem_alloc(len + 1, KM_SLEEP);
bcopy(u8s->utf8string_val, path, len);
path[len] = '\0';
zcmn_err(getzoneid(), CE_WARN,
"Invalid UTF-8 filename: %s", path);
kmem_free(path, len + 1);
}
#endif
return (NULL);
}
return (utf8_to_str(u8s, lenp, s));
}
char *
utf8_to_str(utf8string *str, uint_t *lenp, char *s)
{
char *sp;
char *u8p;
int len;
int i;
ASSERT(lenp != NULL);
if (str == NULL)
return (NULL);
u8p = str->utf8string_val;
len = str->utf8string_len;
if (len <= 0 || u8p == NULL) {
if (s)
*s = '\0';
return (NULL);
}
sp = s;
if (sp == NULL)
sp = kmem_alloc(len + 1, KM_SLEEP);
for (i = 0; i < len; i++) {
sp[i] = u8p[i];
if (u8p[i] == '\0') {
#ifdef DEBUG
zcmn_err(getzoneid(), CE_WARN,
"Embedded NULL in UTF-8 string");
#endif
if (s == NULL)
kmem_free(sp, len + 1);
return (NULL);
}
}
sp[len] = '\0';
*lenp = len + 1;
return (sp);
}
utf8string *
str_to_utf8(char *nm, utf8string *str)
{
int len;
if (str == NULL)
return (NULL);
if (nm == NULL || *nm == '\0') {
str->utf8string_len = 0;
str->utf8string_val = NULL;
}
len = strlen(nm);
str->utf8string_val = kmem_alloc(len, KM_SLEEP);
str->utf8string_len = len;
bcopy(nm, str->utf8string_val, len);
return (str);
}
utf8string *
utf8_copy(utf8string *src, utf8string *dest)
{
if (src == NULL)
return (NULL);
if (dest == NULL)
return (NULL);
if (src->utf8string_len > 0) {
dest->utf8string_val = kmem_alloc(src->utf8string_len,
KM_SLEEP);
bcopy(src->utf8string_val, dest->utf8string_val,
src->utf8string_len);
dest->utf8string_len = src->utf8string_len;
} else {
dest->utf8string_val = NULL;
dest->utf8string_len = 0;
}
return (dest);
}
int
utf8_compare(const utf8string *a, const utf8string *b)
{
int mlen, cmp;
int alen, blen;
char *aval, *bval;
if ((a == NULL) && (b == NULL))
return (0);
else if (a == NULL)
return (-1);
else if (b == NULL)
return (1);
alen = a->utf8string_len;
blen = b->utf8string_len;
aval = a->utf8string_val;
bval = b->utf8string_val;
if (((alen == 0) || (aval == NULL)) &&
((blen == 0) || (bval == NULL)))
return (0);
else if ((alen == 0) || (aval == NULL))
return (-1);
else if ((blen == 0) || (bval == NULL))
return (1);
mlen = MIN(alen, blen);
cmp = strncmp(aval, bval, mlen);
if ((cmp == 0) && (alen == blen))
return (0);
else if ((cmp == 0) && (alen < blen))
return (-1);
else if (cmp == 0)
return (1);
else if (cmp < 0)
return (-1);
return (1);
}
nfsstat4
utf8_dir_verify(utf8string *str)
{
char *nm;
int len;
if (str == NULL)
return (NFS4ERR_INVAL);
nm = str->utf8string_val;
len = str->utf8string_len;
if (nm == NULL || len == 0) {
return (NFS4ERR_INVAL);
}
if (len == 1 && nm[0] == '.')
return (NFS4ERR_BADNAME);
if (len == 2 && nm[0] == '.' && nm[1] == '.')
return (NFS4ERR_BADNAME);
if (utf8_strchr(str, '/') != NULL)
return (NFS4ERR_BADNAME);
if (utf8_strchr(str, '\0') != NULL)
return (NFS4ERR_BADNAME);
return (NFS4_OK);
}
extern int sec_clnt_geth(CLIENT *, struct sec_data *, cred_t *, AUTH **);
extern void sec_clnt_freeh(AUTH *);
extern void sec_clnt_freeinfo(struct sec_data *);
int
authget(servinfo4_t *svp, CLIENT *ch_client, cred_t *cr)
{
int error, i;
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
if ((svp->sv_flags & SV4_TRYSECINFO) && svp->sv_secinfo) {
for (i = svp->sv_secinfo->index; i < svp->sv_secinfo->count;
i++) {
if (!(error = sec_clnt_geth(ch_client,
&svp->sv_secinfo->sdata[i],
cr, &ch_client->cl_auth))) {
svp->sv_currsec = &svp->sv_secinfo->sdata[i];
svp->sv_secinfo->index = i;
svp->sv_flags &= ~SV4_TRYSECINFO;
break;
}
if (error == ETIMEDOUT || error == ECONNRESET) {
svp->sv_secinfo->index = i;
break;
}
}
} else {
if (svp->sv_currsec) {
error = sec_clnt_geth(ch_client, svp->sv_currsec, cr,
&ch_client->cl_auth);
} else {
error = sec_clnt_geth(ch_client, svp->sv_secdata, cr,
&ch_client->cl_auth);
}
}
nfs_rw_exit(&svp->sv_lock);
return (error);
}
int
clget4(clinfo_t *ci, servinfo4_t *svp, cred_t *cr, CLIENT **newcl,
struct chtab **chp, struct nfs4_clnt *nfscl)
{
struct chhead *ch, *newch;
struct chhead **plistp;
struct chtab *cp;
int error;
k_sigset_t smask;
if (newcl == NULL || chp == NULL || ci == NULL)
return (EINVAL);
*newcl = NULL;
*chp = NULL;
newch = NULL;
nfscl->nfscl_stat.clgets.value.ui64++;
top:
mutex_enter(&nfscl->nfscl_chtable4_lock);
plistp = &nfscl->nfscl_chtable4;
for (ch = nfscl->nfscl_chtable4; ch != NULL; ch = ch->ch_next) {
if (ch->ch_prog == ci->cl_prog &&
ch->ch_vers == ci->cl_vers &&
ch->ch_dev == svp->sv_knconf->knc_rdev &&
(strcmp(ch->ch_protofmly,
svp->sv_knconf->knc_protofmly) == 0))
break;
plistp = &ch->ch_next;
}
if (ch == NULL) {
if (newch == NULL) {
mutex_exit(&nfscl->nfscl_chtable4_lock);
newch = kmem_alloc(sizeof (*newch), KM_SLEEP);
newch->ch_timesused = 0;
newch->ch_prog = ci->cl_prog;
newch->ch_vers = ci->cl_vers;
newch->ch_dev = svp->sv_knconf->knc_rdev;
newch->ch_protofmly = kmem_alloc(
strlen(svp->sv_knconf->knc_protofmly) + 1,
KM_SLEEP);
(void) strcpy(newch->ch_protofmly,
svp->sv_knconf->knc_protofmly);
newch->ch_list = NULL;
goto top;
}
ch = newch;
newch = NULL;
ch->ch_next = nfscl->nfscl_chtable4;
nfscl->nfscl_chtable4 = ch;
} else if (ch != nfscl->nfscl_chtable4) {
*plistp = ch->ch_next;
ch->ch_next = nfscl->nfscl_chtable4;
nfscl->nfscl_chtable4 = ch;
}
if (ch->ch_list != NULL) {
cp = ch->ch_list;
ch->ch_list = cp->ch_list;
mutex_exit(&nfscl->nfscl_chtable4_lock);
if (newch != NULL) {
kmem_free(newch->ch_protofmly,
strlen(newch->ch_protofmly) + 1);
kmem_free(newch, sizeof (*newch));
}
(void) clnt_tli_kinit(cp->ch_client, svp->sv_knconf,
&svp->sv_addr, ci->cl_readsize, ci->cl_retrans, cr);
error = authget(svp, cp->ch_client, cr);
if (error || cp->ch_client->cl_auth == NULL) {
CLNT_DESTROY(cp->ch_client);
kmem_cache_free(chtab4_cache, cp);
return ((error != 0) ? error : EINTR);
}
ch->ch_timesused++;
*newcl = cp->ch_client;
*chp = cp;
return (0);
}
#ifdef DEBUG
atomic_inc_64(&nfscl->nfscl_stat.clalloc.value.ui64);
#endif
mutex_exit(&nfscl->nfscl_chtable4_lock);
nfscl->nfscl_stat.cltoomany.value.ui64++;
if (newch != NULL) {
kmem_free(newch->ch_protofmly, strlen(newch->ch_protofmly) + 1);
kmem_free(newch, sizeof (*newch));
}
cp = kmem_cache_alloc(chtab4_cache, KM_SLEEP);
cp->ch_head = ch;
sigintr(&smask, (int)ci->cl_flags & MI4_INT);
error = clnt_tli_kcreate(svp->sv_knconf, &svp->sv_addr, ci->cl_prog,
ci->cl_vers, ci->cl_readsize, ci->cl_retrans, cr, &cp->ch_client);
sigunintr(&smask);
if (error != 0) {
kmem_cache_free(chtab4_cache, cp);
#ifdef DEBUG
atomic_dec_64(&nfscl->nfscl_stat.clalloc.value.ui64);
#endif
if (error != EINTR) {
nfs_cmn_err(error, CE_WARN,
"clget: couldn't create handle: %m\n");
}
return (error);
}
(void) CLNT_CONTROL(cp->ch_client, CLSET_PROGRESS, NULL);
auth_destroy(cp->ch_client->cl_auth);
error = authget(svp, cp->ch_client, cr);
if (error || cp->ch_client->cl_auth == NULL) {
CLNT_DESTROY(cp->ch_client);
kmem_cache_free(chtab4_cache, cp);
#ifdef DEBUG
atomic_dec_64(&nfscl->nfscl_stat.clalloc.value.ui64);
#endif
return ((error != 0) ? error : EINTR);
}
ch->ch_timesused++;
*newcl = cp->ch_client;
ASSERT(cp->ch_client->cl_nosignal == FALSE);
*chp = cp;
return (0);
}
static int
nfs_clget4(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, CLIENT **newcl,
struct chtab **chp, struct nfs4_clnt *nfscl)
{
clinfo_t ci;
bool_t is_recov;
int firstcall, error = 0;
ci.cl_readsize = mi->mi_tsize;
if (ci.cl_readsize != 0)
ci.cl_readsize += (RPC_MAXDATASIZE - NFS_MAXDATA);
if (!(mi->mi_flags & MI4_HARD) && (mi->mi_flags & MI4_DOWN))
ci.cl_retrans = 0;
else
ci.cl_retrans = mi->mi_retrans;
ci.cl_prog = mi->mi_prog;
ci.cl_vers = mi->mi_vers;
ci.cl_flags = mi->mi_flags;
is_recov = (curthread == mi->mi_recovthread);
firstcall = 1;
do {
error = clget4(&ci, svp, cr, newcl, chp, nfscl);
if (error == 0)
break;
if ((FS_OR_ZONE_GONE4(mi->mi_vfsp)) &&
(!is_recov || !firstcall)) {
error = EIO;
break;
}
if (!(mi->mi_flags & MI4_HARD))
break;
if (FAILOVER_MOUNT4(mi))
break;
firstcall = 0;
} while (error == ETIMEDOUT || error == ECONNRESET);
return (error);
}
void
clfree4(CLIENT *cl, struct chtab *cp, struct nfs4_clnt *nfscl)
{
if (cl->cl_auth != NULL) {
sec_clnt_freeh(cl->cl_auth);
cl->cl_auth = NULL;
}
cp->ch_freed = gethrestime_sec();
mutex_enter(&nfscl->nfscl_chtable4_lock);
cp->ch_list = cp->ch_head->ch_list;
cp->ch_head->ch_list = cp;
mutex_exit(&nfscl->nfscl_chtable4_lock);
}
#define CL_HOLDTIME 60
static void
clreclaim4_zone(struct nfs4_clnt *nfscl, uint_t cl_holdtime)
{
struct chhead *ch;
struct chtab *cp;
struct chtab *cpe;
struct chtab *cpl;
struct chtab **cpp;
#ifdef DEBUG
int n = 0;
clstat4_debug.clreclaim.value.ui64++;
#endif
cp = NULL;
mutex_enter(&nfscl->nfscl_chtable4_lock);
for (ch = nfscl->nfscl_chtable4; ch != NULL; ch = ch->ch_next) {
if (ch->ch_list == NULL)
continue;
cpl = ch->ch_list;
cpp = &ch->ch_list;
while (cpl != NULL &&
cpl->ch_freed + cl_holdtime > gethrestime_sec()) {
cpp = &cpl->ch_list;
cpl = cpl->ch_list;
}
if (cpl != NULL) {
*cpp = NULL;
if (cp != NULL) {
cpe = cpl;
while (cpe->ch_list != NULL)
cpe = cpe->ch_list;
cpe->ch_list = cp;
}
cp = cpl;
}
}
mutex_exit(&nfscl->nfscl_chtable4_lock);
if (cp == NULL)
return;
while (cp != NULL) {
#ifdef DEBUG
n++;
#endif
CLNT_DESTROY(cp->ch_client);
cpl = cp->ch_list;
kmem_cache_free(chtab4_cache, cp);
cp = cpl;
}
#ifdef DEBUG
atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, -n);
#endif
}
static void
clreclaim4(void *all)
{
struct nfs4_clnt *nfscl;
mutex_enter(&nfs4_clnt_list_lock);
nfscl = list_head(&nfs4_clnt_list);
for (; nfscl != NULL; nfscl = list_next(&nfs4_clnt_list, nfscl))
clreclaim4_zone(nfscl, CL_HOLDTIME);
mutex_exit(&nfs4_clnt_list_lock);
}
static unsigned int minimum_timeo[] = {
6, 7, 10
};
#define SHORTWAIT (NFS_COTS_TIMEO / 10)
#define MAXTIMO (20*hz)
#define backoff(tim) (((tim) < MAXTIMO) ? dobackoff(tim) : (tim))
#define dobackoff(tim) ((((tim) << 1) > MAXTIMO) ? MAXTIMO : ((tim) << 1))
static int
nfs4_rfscall(mntinfo4_t *mi, rpcproc_t which, xdrproc_t xdrargs, caddr_t argsp,
xdrproc_t xdrres, caddr_t resp, cred_t *icr, int *doqueue,
enum clnt_stat *rpc_statusp, int flags, struct nfs4_clnt *nfscl)
{
CLIENT *client;
struct chtab *ch;
cred_t *cr = icr;
struct rpc_err rpcerr, rpcerr_tmp;
enum clnt_stat status;
int error;
struct timeval wait;
int timeo;
bool_t tryagain, is_recov;
bool_t cred_cloned = FALSE;
k_sigset_t smask;
servinfo4_t *svp;
#ifdef DEBUG
char *bufp;
#endif
int firstcall;
rpcerr.re_status = RPC_SUCCESS;
mutex_enter(&mi->mi_lock);
if (mi->mi_flags & MI4_SHUTDOWN) {
mutex_exit(&mi->mi_lock);
return (EIO);
}
mutex_exit(&mi->mi_lock);
if (!cred_cloned && is_system_labeled()) {
cred_cloned = TRUE;
cr = crdup(icr);
(void) setpflags(NET_MAC_AWARE, 1, cr);
}
svp = mi->mi_curr_serv;
rpcerr.re_errno = nfs_clget4(mi, svp, cr, &client, &ch, nfscl);
if (rpcerr.re_errno != 0)
return (rpcerr.re_errno);
timeo = (mi->mi_timeo * hz) / 10;
is_recov = (curthread == mi->mi_recovthread);
firstcall = 1;
do {
tryagain = FALSE;
NFS4_DEBUG(nfs4_rfscall_debug, (CE_NOTE,
"nfs4_rfscall: vfs_flag=0x%x, %s",
mi->mi_vfsp->vfs_flag,
is_recov ? "recov thread" : "not recov thread"));
mutex_enter(&mi->mi_lock);
if (mi->mi_flags & MI4_SHUTDOWN) {
mutex_exit(&mi->mi_lock);
clfree4(client, ch, nfscl);
if (cred_cloned)
crfree(cr);
return (EIO);
}
mutex_exit(&mi->mi_lock);
if ((mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED) &&
(!is_recov || !firstcall)) {
clfree4(client, ch, nfscl);
if (cred_cloned)
crfree(cr);
return (EIO);
}
if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) {
mutex_enter(&mi->mi_lock);
if ((mi->mi_flags & MI4_TIMEDOUT) ||
!is_recov || !firstcall) {
mutex_exit(&mi->mi_lock);
clfree4(client, ch, nfscl);
if (cred_cloned)
crfree(cr);
return (EIO);
}
mutex_exit(&mi->mi_lock);
timeo = (MIN(mi->mi_timeo, SHORTWAIT) * hz) / 10;
}
firstcall = 0;
TICK_TO_TIMEVAL(timeo, &wait);
sigintr(&smask, (int)mi->mi_flags & MI4_INT);
if (!(mi->mi_flags & MI4_INT))
client->cl_nosignal = TRUE;
if (ttolwp(curthread) != NULL && ISSIG(curthread, JUSTLOOKING))
status = RPC_INTR;
else {
status = CLNT_CALL(client, which, xdrargs, argsp,
xdrres, resp, wait);
}
if (!(mi->mi_flags & MI4_INT))
client->cl_nosignal = FALSE;
sigunintr(&smask);
switch (status) {
case RPC_SUCCESS:
break;
case RPC_INTR:
rpcerr.re_status = RPC_INTR;
rpcerr.re_errno = EINTR;
break;
case RPC_UDERROR:
CLNT_GETERR(client, &rpcerr);
if (rpcerr.re_errno == ECONNRESET) {
rpcerr.re_status = RPC_PROGUNAVAIL;
rpcerr.re_errno = ECONNRESET;
break;
}
default:
if (IS_UNRECOVERABLE_RPC(status))
break;
mutex_enter(&mi->mi_lock);
mi->mi_noresponse++;
mutex_exit(&mi->mi_lock);
#ifdef DEBUG
nfscl->nfscl_stat.noresponse.value.ui64++;
#endif
if (zone_status_get(curproc->p_zone) >=
ZONE_IS_SHUTTING_DOWN) {
mutex_enter(&mi->mi_lock);
mi->mi_flags |= MI4_TIMEDOUT;
mutex_exit(&mi->mi_lock);
clfree4(client, ch, nfscl);
if (cred_cloned)
crfree(cr);
return (EIO);
}
if (mi->mi_vers == 4 && FAILOVER_MOUNT4(mi) &&
(error = try_failover(status)) != 0) {
clfree4(client, ch, nfscl);
if (cred_cloned)
crfree(cr);
*rpc_statusp = status;
return (error);
}
if (flags & RFSCALL_SOFT)
break;
tryagain = TRUE;
if (status == RPC_INPROGRESS)
break;
timeo = backoff(timeo);
CLNT_GETERR(client, &rpcerr_tmp);
mutex_enter(&mi->mi_lock);
if (!(mi->mi_flags & MI4_PRINTED)) {
mi->mi_flags |= MI4_PRINTED;
mutex_exit(&mi->mi_lock);
if ((status == RPC_CANTSEND) &&
(rpcerr_tmp.re_errno == ENOBUFS))
nfs4_queue_fact(RF_SENDQ_FULL, mi, 0,
0, 0, FALSE, NULL, 0, NULL);
else
nfs4_queue_fact(RF_SRV_NOT_RESPOND, mi,
0, 0, 0, FALSE, NULL, 0, NULL);
} else
mutex_exit(&mi->mi_lock);
if (*doqueue && nfs_has_ctty()) {
*doqueue = 0;
if (!(mi->mi_flags & MI4_NOPRINT)) {
if ((status == RPC_CANTSEND) &&
(rpcerr_tmp.re_errno == ENOBUFS))
nfs4_queue_fact(RF_SENDQ_FULL,
mi, 0, 0, 0, FALSE, NULL,
0, NULL);
else
nfs4_queue_fact(
RF_SRV_NOT_RESPOND, mi, 0,
0, 0, FALSE, NULL, 0, NULL);
}
}
}
} while (tryagain);
DTRACE_PROBE2(nfs4__rfscall_debug, enum clnt_stat, status,
int, rpcerr.re_errno);
if (status != RPC_SUCCESS) {
zoneid_t zoneid = mi->mi_zone->zone_id;
if (status == RPC_INPROGRESS)
status = RPC_TIMEDOUT;
nfscl->nfscl_stat.badcalls.value.ui64++;
if (status != RPC_INTR) {
mutex_enter(&mi->mi_lock);
mi->mi_flags |= MI4_DOWN;
mutex_exit(&mi->mi_lock);
CLNT_GETERR(client, &rpcerr);
#ifdef DEBUG
bufp = clnt_sperror(client, svp->sv_hostname);
zprintf(zoneid, "NFS%d %s failed for %s\n",
mi->mi_vers, mi->mi_rfsnames[which], bufp);
if (nfs_has_ctty()) {
if (!(mi->mi_flags & MI4_NOPRINT)) {
uprintf("NFS%d %s failed for %s\n",
mi->mi_vers, mi->mi_rfsnames[which],
bufp);
}
}
kmem_free(bufp, MAXPATHLEN);
#else
zprintf(zoneid,
"NFS %s failed for server %s: error %d (%s)\n",
mi->mi_rfsnames[which], svp->sv_hostname,
status, clnt_sperrno(status));
if (nfs_has_ctty()) {
if (!(mi->mi_flags & MI4_NOPRINT)) {
uprintf(
"NFS %s failed for server %s: error %d (%s)\n",
mi->mi_rfsnames[which],
svp->sv_hostname, status,
clnt_sperrno(status));
}
}
#endif
if (status == RPC_VERSMISMATCH ||
status == RPC_PROGVERSMISMATCH)
rpcerr.re_errno = EIO;
}
} else {
if (mi->mi_flags & (MI4_DOWN | MI4_PRINTED)) {
mutex_enter(&mi->mi_lock);
mi->mi_flags &= ~MI4_DOWN;
if (mi->mi_flags & MI4_PRINTED) {
mi->mi_flags &= ~MI4_PRINTED;
mutex_exit(&mi->mi_lock);
if (!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
nfs4_queue_fact(RF_SRV_OK, mi, 0, 0,
0, FALSE, NULL, 0, NULL);
} else
mutex_exit(&mi->mi_lock);
}
if (*doqueue == 0) {
if (!(mi->mi_flags & MI4_NOPRINT) &&
!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
nfs4_queue_fact(RF_SRV_OK, mi, 0, 0, 0,
FALSE, NULL, 0, NULL);
*doqueue = 1;
}
}
clfree4(client, ch, nfscl);
if (cred_cloned)
crfree(cr);
ASSERT(rpcerr.re_status == RPC_SUCCESS || rpcerr.re_errno != 0);
TRACE_1(TR_FAC_NFS, TR_RFSCALL_END, "nfs4_rfscall_end:errno %d",
rpcerr.re_errno);
*rpc_statusp = status;
return (rpcerr.re_errno);
}
void
rfs4call(mntinfo4_t *mi, COMPOUND4args_clnt *argsp, COMPOUND4res_clnt *resp,
cred_t *cr, int *doqueue, int flags, nfs4_error_t *ep)
{
int i, error;
enum clnt_stat rpc_status = RPC_SUCCESS;
int num_resops;
struct nfs4_clnt *nfscl;
ASSERT(nfs_zone() == mi->mi_zone);
nfscl = zone_getspecific(nfs4clnt_zone_key, nfs_zone());
ASSERT(nfscl != NULL);
nfscl->nfscl_stat.calls.value.ui64++;
mi->mi_reqs[NFSPROC4_COMPOUND].value.ui64++;
resp->argsp = argsp;
resp->array = NULL;
resp->status = 0;
resp->decode_len = 0;
error = nfs4_rfscall(mi, NFSPROC4_COMPOUND,
xdr_COMPOUND4args_clnt, (caddr_t)argsp,
xdr_COMPOUND4res_clnt, (caddr_t)resp, cr,
doqueue, &rpc_status, flags, nfscl);
if (error) {
ep->error = error;
ep->stat = resp->status;
ep->rpc_status = rpc_status;
return;
}
num_resops = resp->decode_len;
for (i = 0; i < num_resops; i++) {
if (resp->array[i].resop >= NFSPROC4_NULL &&
resp->array[i].resop <= OP_WRITE)
mi->mi_reqs[resp->array[i].resop].value.ui64++;
}
ep->error = 0;
ep->stat = resp->status;
ep->rpc_status = rpc_status;
}
void
nfs4rename_update(vnode_t *renvp, vnode_t *ndvp, nfs_fh4 *nfh4p, char *nnm)
{
sfh4_update(VTOR4(renvp)->r_fh, nfh4p);
fn_move(VTOSV(renvp)->sv_name, VTOSV(ndvp)->sv_name, nnm);
}
#define RML_ORDINARY 1
#define RML_NAMED_ATTR 2
#define RML_ATTRDIR 3
static void
remap_lookup(nfs4_fname_t *fname, vnode_t *rootvp,
int filetype, cred_t *cr,
nfs_fh4 *fhp, nfs4_ga_res_t *garp,
nfs_fh4 *pfhp, nfs4_ga_res_t *pgarp,
nfs4_error_t *ep)
{
COMPOUND4args_clnt args;
COMPOUND4res_clnt res;
nfs_argop4 *argop;
nfs_resop4 *resop;
int num_argops;
lookup4_param_t lookuparg;
nfs_fh4 *tmpfhp;
int doqueue = 1;
char *path;
mntinfo4_t *mi;
ASSERT(fname != NULL);
ASSERT(rootvp->v_type == VDIR);
mi = VTOMI4(rootvp);
path = fn_path(fname);
switch (filetype) {
case RML_NAMED_ATTR:
lookuparg.l4_getattrs = LKP4_LAST_NAMED_ATTR;
args.ctag = TAG_REMAP_LOOKUP_NA;
break;
case RML_ATTRDIR:
lookuparg.l4_getattrs = LKP4_LAST_ATTRDIR;
args.ctag = TAG_REMAP_LOOKUP_AD;
break;
case RML_ORDINARY:
lookuparg.l4_getattrs = LKP4_ALL_ATTRIBUTES;
args.ctag = TAG_REMAP_LOOKUP;
break;
default:
ep->error = EINVAL;
return;
}
lookuparg.argsp = &args;
lookuparg.resp = &res;
lookuparg.header_len = 1;
lookuparg.trailer_len = 0;
lookuparg.ga_bits = NFS4_VATTR_MASK;
lookuparg.mi = VTOMI4(rootvp);
(void) nfs4lookup_setup(path, &lookuparg, 1);
argop = args.array;
argop[0].argop = OP_CPUTFH;
argop[0].nfs_argop4_u.opcputfh.sfh = VTOR4(rootvp)->r_fh;
num_argops = args.array_len;
rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, ep);
if (ep->error || res.status != NFS4_OK)
goto exit;
resop = &res.array[res.array_len - 2];
if (resop->resop != OP_GETFH) {
nfs4_queue_event(RE_FAIL_REMAP_OP, mi, NULL,
0, NULL, NULL, 0, NULL, 0, TAG_NONE, TAG_NONE, 0, 0);
ep->stat = NFS4ERR_SERVERFAULT;
goto exit;
}
tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
if (tmpfhp->nfs_fh4_len > NFS4_FHSIZE) {
nfs4_queue_event(RE_FAIL_REMAP_LEN, mi, NULL,
tmpfhp->nfs_fh4_len, NULL, NULL, 0, NULL, 0, TAG_NONE,
TAG_NONE, 0, 0);
ep->stat = NFS4ERR_SERVERFAULT;
goto exit;
}
fhp->nfs_fh4_val = kmem_alloc(tmpfhp->nfs_fh4_len, KM_SLEEP);
nfs_fh4_copy(tmpfhp, fhp);
resop = &res.array[res.array_len - 1];
if (garp && resop->resop == OP_GETATTR)
*garp = resop->nfs_resop4_u.opgetattr.ga_res;
if ((int)res.array_len - 5 <= 0)
goto exit;
resop = &res.array[res.array_len - 5];
if (resop->resop != OP_GETFH) {
nfs4_queue_event(RE_FAIL_REMAP_OP, mi, NULL,
0, NULL, NULL, 0, NULL, 0, TAG_NONE, TAG_NONE, 0, 0);
ep->stat = NFS4ERR_SERVERFAULT;
goto exit;
}
tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
if (tmpfhp->nfs_fh4_len > NFS4_FHSIZE) {
nfs4_queue_event(RE_FAIL_REMAP_LEN, mi, NULL,
tmpfhp->nfs_fh4_len, NULL, NULL, 0, NULL, 0, TAG_NONE,
TAG_NONE, 0, 0);
ep->stat = NFS4ERR_SERVERFAULT;
goto exit;
}
pfhp->nfs_fh4_val = kmem_alloc(tmpfhp->nfs_fh4_len, KM_SLEEP);
nfs_fh4_copy(tmpfhp, pfhp);
resop = &res.array[res.array_len - 4];
if (pgarp && resop->resop == OP_GETATTR)
*pgarp = resop->nfs_resop4_u.opgetattr.ga_res;
exit:
nfs4args_lookup_free(argop, num_argops);
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
if (!ep->error)
xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
kmem_free(path, strlen(path)+1);
}
void
nfs4_remap_file(mntinfo4_t *mi, vnode_t *vp, int flags, nfs4_error_t *ep)
{
int is_stub;
rnode4_t *rp = VTOR4(vp);
vnode_t *rootvp = NULL;
vnode_t *dvp = NULL;
cred_t *cr, *cred_otw;
nfs4_ga_res_t gar, pgar;
nfs_fh4 newfh = {0, NULL}, newpfh = {0, NULL};
int filetype = RML_ORDINARY;
nfs4_recov_state_t recov = {NULL, 0, 0};
int badfhcount = 0;
nfs4_open_stream_t *osp = NULL;
bool_t first_time = TRUE;
bool_t last_time = FALSE;
NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
"nfs4_remap_file: remapping %s", rnode4info(rp)));
ASSERT(nfs4_consistent_type(vp));
if (vp->v_flag & VROOT) {
nfs4_remap_root(mi, ep, flags);
return;
}
ep->error = VFS_ROOT(mi->mi_vfsp, &rootvp);
if (ep->error != 0)
return;
cr = curthread->t_cred;
ASSERT(cr != NULL);
get_remap_cred:
cred_otw = nfs4_get_otw_cred_by_osp(rp, cr, &osp,
&first_time, &last_time);
ASSERT(cred_otw != NULL);
if (rp->r_flags & R4ISXATTR) {
filetype = RML_NAMED_ATTR;
(void) vtodv(vp, &dvp, cred_otw, FALSE);
}
if (vp->v_flag & V_XATTRDIR) {
filetype = RML_ATTRDIR;
}
if (filetype == RML_ORDINARY && rootvp->v_type == VREG) {
goto done;
}
again:
remap_lookup(rp->r_svnode.sv_name, rootvp, filetype, cred_otw,
&newfh, &gar, &newpfh, &pgar, ep);
NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
"nfs4_remap_file: remap_lookup returned %d/%d",
ep->error, ep->stat));
if (last_time == FALSE && ep->error == EACCES) {
crfree(cred_otw);
if (dvp != NULL)
VN_RELE(dvp);
goto get_remap_cred;
}
if (ep->error != 0)
goto done;
switch (ep->stat) {
case NFS4_OK:
badfhcount = 0;
if (recov.rs_flags & NFS4_RS_DELAY_MSG) {
mutex_enter(&rp->r_statelock);
rp->r_delay_interval = 0;
mutex_exit(&rp->r_statelock);
uprintf("NFS File Available..\n");
}
break;
case NFS4ERR_FHEXPIRED:
case NFS4ERR_BADHANDLE:
case NFS4ERR_STALE:
if (badfhcount++ > 0)
goto done;
if (newfh.nfs_fh4_len != 0) {
kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len);
newfh.nfs_fh4_len = 0;
}
if (newpfh.nfs_fh4_len != 0) {
kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len);
newpfh.nfs_fh4_len = 0;
}
VN_RELE(rootvp);
rootvp = NULL;
nfs4_remap_root(mi, ep, flags);
if (ep->error != 0 || ep->stat != NFS4_OK)
goto done;
ep->error = VFS_ROOT(mi->mi_vfsp, &rootvp);
if (ep->error != 0)
goto done;
goto again;
case NFS4ERR_DELAY:
badfhcount = 0;
nfs4_set_delay_wait(vp);
ep->error = nfs4_wait_for_delay(vp, &recov);
if (ep->error != 0)
goto done;
goto again;
case NFS4ERR_ACCESS:
if (last_time == TRUE)
goto done;
if (dvp != NULL)
VN_RELE(dvp);
crfree(cred_otw);
goto get_remap_cred;
default:
goto done;
}
mutex_enter(&rp->r_statelock);
if (flags & NFS4_REMAP_CKATTRS) {
if (vp->v_type != gar.n4g_va.va_type ||
(vp->v_type != VDIR &&
rp->r_size != gar.n4g_va.va_size)) {
NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
"nfs4_remap_file: size %d vs. %d, type %d vs. %d",
(int)rp->r_size, (int)gar.n4g_va.va_size,
vp->v_type, gar.n4g_va.va_type));
mutex_exit(&rp->r_statelock);
nfs4_queue_event(RE_FILE_DIFF, mi,
rp->r_server->sv_hostname, 0, vp, NULL, 0, NULL, 0,
TAG_NONE, TAG_NONE, 0, 0);
nfs4_fail_recov(vp, NULL, 0, NFS4_OK);
goto done;
}
}
ASSERT(gar.n4g_va.va_type != VNON);
rp->r_server = mi->mi_curr_serv;
is_stub = 0;
if (gar.n4g_fsid_valid) {
(void) nfs_rw_enter_sig(&rp->r_server->sv_lock, RW_READER, 0);
rp->r_srv_fsid = gar.n4g_fsid;
if (!FATTR4_FSID_EQ(&gar.n4g_fsid, &rp->r_server->sv_fsid))
is_stub = 1;
nfs_rw_exit(&rp->r_server->sv_lock);
#ifdef DEBUG
} else {
NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
"remap_file: fsid attr not provided by server. rp=%p",
(void *)rp));
#endif
}
if (is_stub)
r4_stub_mirrormount(rp);
else
r4_stub_none(rp);
mutex_exit(&rp->r_statelock);
nfs4_attrcache_noinval(vp, &gar, gethrtime());
sfh4_update(rp->r_fh, &newfh);
ASSERT(nfs4_consistent_type(vp));
if (newpfh.nfs_fh4_len != 0) {
if (rp->r_svnode.sv_dfh != NULL)
sfh4_update(rp->r_svnode.sv_dfh, &newpfh);
if (dvp != NULL) {
nfs4_attrcache_noinval(dvp, &pgar, gethrtime());
}
}
done:
if (newfh.nfs_fh4_len != 0)
kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len);
if (newpfh.nfs_fh4_len != 0)
kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len);
if (cred_otw != NULL)
crfree(cred_otw);
if (rootvp != NULL)
VN_RELE(rootvp);
if (dvp != NULL)
VN_RELE(dvp);
if (osp != NULL)
open_stream_rele(osp, rp);
}
void
nfs4_check_remap(mntinfo4_t *mi, vnode_t *vp, int flags, nfs4_error_t *ep)
{
if (vp == NULL)
return;
if (!(vp->v_vfsp->vfs_flag & VFS_RDONLY))
return;
if (VTOR4(vp)->r_server == mi->mi_curr_serv)
return;
nfs4_remap_file(mi, vp, flags, ep);
}
int
nfs4_make_dotdot(nfs4_sharedfh_t *fhp, hrtime_t t, vnode_t *dvp,
cred_t *cr, vnode_t **vpp, int need_start_op)
{
mntinfo4_t *mi = VTOMI4(dvp);
nfs4_fname_t *np = NULL, *pnp = NULL;
vnode_t *vp = NULL, *rootvp = NULL;
rnode4_t *rp;
nfs_fh4 newfh = {0, NULL}, newpfh = {0, NULL};
nfs4_ga_res_t gar, pgar;
vattr_t va, pva;
nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
nfs4_sharedfh_t *sfh = NULL, *psfh = NULL;
nfs4_recov_state_t recov_state;
#ifdef DEBUG
{
int no_need_start_op = (tsd_get(nfs4_tsd_key) ||
(curthread == mi->mi_recovthread));
ASSERT(((need_start_op) && (!no_need_start_op)) ||
((! need_start_op) && (no_need_start_op)));
}
#endif
ASSERT(VTOMI4(dvp)->mi_zone == nfs_zone());
NFS4_DEBUG(nfs4_client_shadow_debug, (CE_NOTE,
"nfs4_make_dotdot: called with fhp %p, dvp %s", (void *)fhp,
rnode4info(VTOR4(dvp))));
e.error = VFS_ROOT(mi->mi_vfsp, &rootvp);
if (e.error != 0)
goto out;
rp = r4find_unlocked(fhp, mi->mi_vfsp);
if (rp != NULL) {
*vpp = RTOV4(rp);
VN_RELE(rootvp);
return (0);
}
np = fn_parent(VTOSV(dvp)->sv_name);
if (np == NULL) {
e.error = ENOENT;
goto out;
}
recov_state.rs_flags = 0;
recov_state.rs_num_retry_despite_err = 0;
recov_retry:
if (need_start_op) {
e.error = nfs4_start_fop(mi, rootvp, NULL, OH_LOOKUP,
&recov_state, NULL);
if (e.error != 0) {
goto out;
}
}
pgar.n4g_va.va_type = VNON;
gar.n4g_va.va_type = VNON;
remap_lookup(np, rootvp, RML_ORDINARY, cr,
&newfh, &gar, &newpfh, &pgar, &e);
if (nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp)) {
if (need_start_op) {
bool_t abort;
abort = nfs4_start_recovery(&e, mi,
rootvp, NULL, NULL, NULL, OP_LOOKUP, NULL, NULL,
NULL);
if (abort) {
nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
&recov_state, FALSE);
if (e.error == 0)
e.error = EIO;
goto out;
}
nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
&recov_state, TRUE);
goto recov_retry;
}
if (e.error == 0)
e.error = EIO;
goto out;
}
va = gar.n4g_va;
pva = pgar.n4g_va;
if ((e.error != 0) ||
(va.va_type != VDIR)) {
if (need_start_op)
nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
&recov_state, FALSE);
if (e.error == 0)
e.error = EIO;
goto out;
}
if (e.stat != NFS4_OK) {
if (need_start_op)
nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
&recov_state, FALSE);
e.error = EIO;
goto out;
}
if (pva.va_type != VDIR) {
pnp = fn_parent(np);
if (pnp != NULL) {
remap_lookup(pnp, rootvp, RML_ORDINARY, cr,
&newpfh, &pgar, NULL, NULL, &e);
pva = pgar.n4g_va;
if (nfs4_needs_recovery(&e, FALSE,
mi->mi_vfsp)) {
if (need_start_op) {
bool_t abort;
abort = nfs4_start_recovery(&e, mi,
rootvp, NULL, NULL, NULL,
OP_LOOKUP, NULL, NULL, NULL);
if (abort) {
nfs4_end_fop(mi, rootvp, NULL,
OH_LOOKUP, &recov_state,
FALSE);
if (e.error == 0)
e.error = EIO;
goto out;
}
nfs4_end_fop(mi, rootvp, NULL,
OH_LOOKUP, &recov_state, TRUE);
goto recov_retry;
}
if (e.error == 0)
e.error = EIO;
goto out;
}
if (e.stat != NFS4_OK) {
if (need_start_op)
nfs4_end_fop(mi, rootvp, NULL,
OH_LOOKUP, &recov_state, FALSE);
e.error = EIO;
goto out;
}
}
if ((pnp == NULL) ||
(e.error != 0) ||
(pva.va_type == VNON)) {
if (need_start_op)
nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
&recov_state, FALSE);
if (e.error == 0)
e.error = EIO;
goto out;
}
}
ASSERT(newpfh.nfs_fh4_len != 0);
if (need_start_op)
nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, &recov_state, FALSE);
psfh = sfh4_get(&newpfh, mi);
sfh = sfh4_get(&newfh, mi);
vp = makenfs4node_by_fh(sfh, psfh, &np, &gar, mi, cr, t);
out:
if (np != NULL)
fn_rele(&np);
if (pnp != NULL)
fn_rele(&pnp);
if (newfh.nfs_fh4_len != 0)
kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len);
if (newpfh.nfs_fh4_len != 0)
kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len);
if (sfh != NULL)
sfh4_rele(&sfh);
if (psfh != NULL)
sfh4_rele(&psfh);
if (rootvp != NULL)
VN_RELE(rootvp);
*vpp = vp;
return (e.error);
}
#ifdef DEBUG
size_t r_path_memuse = 0;
#endif
void
sv4_free(servinfo4_t *svp)
{
servinfo4_t *next;
struct knetconfig *knconf;
while (svp != NULL) {
next = svp->sv_next;
if (svp->sv_dhsec)
sec_clnt_freeinfo(svp->sv_dhsec);
if (svp->sv_secdata)
sec_clnt_freeinfo(svp->sv_secdata);
if (svp->sv_save_secinfo &&
svp->sv_save_secinfo != svp->sv_secinfo)
secinfo_free(svp->sv_save_secinfo);
if (svp->sv_secinfo)
secinfo_free(svp->sv_secinfo);
if (svp->sv_hostname && svp->sv_hostnamelen > 0)
kmem_free(svp->sv_hostname, svp->sv_hostnamelen);
knconf = svp->sv_knconf;
if (knconf != NULL) {
if (knconf->knc_protofmly != NULL)
kmem_free(knconf->knc_protofmly, KNC_STRSIZE);
if (knconf->knc_proto != NULL)
kmem_free(knconf->knc_proto, KNC_STRSIZE);
kmem_free(knconf, sizeof (*knconf));
}
knconf = svp->sv_origknconf;
if (knconf != NULL) {
if (knconf->knc_protofmly != NULL)
kmem_free(knconf->knc_protofmly, KNC_STRSIZE);
if (knconf->knc_proto != NULL)
kmem_free(knconf->knc_proto, KNC_STRSIZE);
kmem_free(knconf, sizeof (*knconf));
}
if (svp->sv_addr.buf != NULL && svp->sv_addr.maxlen != 0)
kmem_free(svp->sv_addr.buf, svp->sv_addr.maxlen);
if (svp->sv_path != NULL) {
kmem_free(svp->sv_path, svp->sv_pathlen);
}
nfs_rw_destroy(&svp->sv_lock);
kmem_free(svp, sizeof (*svp));
svp = next;
}
}
void
nfs4_printfhandle(nfs4_fhandle_t *fhp)
{
int *ip;
char *buf;
size_t bufsize;
char *cp;
bufsize = 13 + ((NFS_FHANDLE_LEN / sizeof (*ip)) * (1 + 8)) + 3;
buf = kmem_alloc(bufsize, KM_NOSLEEP);
if (buf == NULL)
return;
cp = buf;
(void) strcpy(cp, "(file handle:");
while (*cp != '\0')
cp++;
for (ip = (int *)fhp->fh_buf;
ip < (int *)&fhp->fh_buf[fhp->fh_len];
ip++) {
(void) sprintf(cp, " %x", *ip);
while (*cp != '\0')
cp++;
}
(void) strcpy(cp, ")\n");
zcmn_err(getzoneid(), CE_CONT, "%s", buf);
kmem_free(buf, bufsize);
}
static int
rddir4_cache_compar(const void *x, const void *y)
{
rddir4_cache_impl *ai = (rddir4_cache_impl *)x;
rddir4_cache_impl *bi = (rddir4_cache_impl *)y;
rddir4_cache *a = &ai->rc;
rddir4_cache *b = &bi->rc;
if (a->nfs4_cookie == b->nfs4_cookie) {
if (a->buflen == b->buflen)
return (0);
if (a->buflen < b->buflen)
return (-1);
return (1);
}
if (a->nfs4_cookie < b->nfs4_cookie)
return (-1);
return (1);
}
void
rddir4_cache_create(rnode4_t *rp)
{
ASSERT(rp->r_dir == NULL);
rp->r_dir = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
avl_create(rp->r_dir, rddir4_cache_compar, sizeof (rddir4_cache_impl),
offsetof(rddir4_cache_impl, tree));
}
void
rddir4_cache_purge(rnode4_t *rp)
{
rddir4_cache_impl *rdip;
rddir4_cache_impl *nrdip;
ASSERT(MUTEX_HELD(&rp->r_statelock));
if (rp->r_dir == NULL)
return;
rdip = avl_first(rp->r_dir);
while (rdip != NULL) {
nrdip = AVL_NEXT(rp->r_dir, rdip);
avl_remove(rp->r_dir, rdip);
rdip->rc.flags &= ~RDDIRCACHED;
rddir4_cache_rele(rp, &rdip->rc);
rdip = nrdip;
}
ASSERT(avl_numnodes(rp->r_dir) == 0);
}
void
rddir4_cache_destroy(rnode4_t *rp)
{
ASSERT(MUTEX_HELD(&rp->r_statelock));
if (rp->r_dir == NULL)
return;
rddir4_cache_purge(rp);
avl_destroy(rp->r_dir);
kmem_free(rp->r_dir, sizeof (avl_tree_t));
rp->r_dir = NULL;
}
rddir4_cache *
rddir4_cache_lookup(rnode4_t *rp, offset_t cookie, int count)
{
rddir4_cache_impl *rdip = NULL;
rddir4_cache_impl srdip;
rddir4_cache *srdc;
rddir4_cache *rdc = NULL;
rddir4_cache *nrdc = NULL;
avl_index_t where;
top:
ASSERT(nfs_rw_lock_held(&rp->r_rwlock, RW_READER));
ASSERT(MUTEX_HELD(&rp->r_statelock));
if (rp->r_dir == NULL) {
if (nrdc == NULL) {
mutex_exit(&rp->r_statelock);
rdc = rddir4_cache_alloc(KM_SLEEP);
rdc->nfs4_cookie = cookie;
rdc->buflen = count;
mutex_enter(&rp->r_statelock);
return (rdc);
}
return (nrdc);
}
srdc = &srdip.rc;
srdc->nfs4_cookie = cookie;
srdc->buflen = count;
rdip = avl_find(rp->r_dir, &srdip, &where);
if (rdip == NULL) {
if (nrdc != NULL) {
avl_insert(rp->r_dir, nrdc->data, where);
nrdc->flags |= RDDIRCACHED;
rddir4_cache_hold(nrdc);
return (nrdc);
}
#ifdef DEBUG
nfs4_readdir_cache_misses++;
#endif
nrdc = rddir4_cache_alloc(KM_NOSLEEP);
if (nrdc != NULL) {
nrdc->nfs4_cookie = cookie;
nrdc->buflen = count;
avl_insert(rp->r_dir, nrdc->data, where);
nrdc->flags |= RDDIRCACHED;
rddir4_cache_hold(nrdc);
return (nrdc);
}
mutex_exit(&rp->r_statelock);
nrdc = rddir4_cache_alloc(KM_SLEEP);
nrdc->nfs4_cookie = cookie;
nrdc->buflen = count;
mutex_enter(&rp->r_statelock);
goto top;
}
if (nrdc != NULL)
rddir4_cache_free((rddir4_cache_impl *)nrdc->data);
#ifdef DEBUG
nfs4_readdir_cache_hits++;
#endif
rdc = &rdip->rc;
rddir4_cache_hold(rdc);
while (rdc->flags & RDDIR) {
nfs_rw_exit(&rp->r_rwlock);
rdc->flags |= RDDIRWAIT;
#ifdef DEBUG
nfs4_readdir_cache_waits++;
#endif
while (rdc->flags & RDDIRWAIT) {
if (!cv_wait_sig(&rdc->cv, &rp->r_statelock)) {
rddir4_cache_rele(rp, rdc);
mutex_exit(&rp->r_statelock);
(void) nfs_rw_enter_sig(&rp->r_rwlock,
RW_READER, FALSE);
mutex_enter(&rp->r_statelock);
return (NULL);
}
}
mutex_exit(&rp->r_statelock);
(void) nfs_rw_enter_sig(&rp->r_rwlock,
RW_READER, FALSE);
mutex_enter(&rp->r_statelock);
}
if (!(rdc->flags & RDDIRCACHED)) {
rddir4_cache_rele(rp, rdc);
goto top;
}
return (rdc);
}
static rddir4_cache *
rddir4_cache_alloc(int flags)
{
rddir4_cache_impl *rdip = NULL;
rddir4_cache *rc = NULL;
rdip = kmem_alloc(sizeof (rddir4_cache_impl), flags);
if (rdip != NULL) {
rc = &rdip->rc;
rc->data = (void *)rdip;
rc->nfs4_cookie = 0;
rc->nfs4_ncookie = 0;
rc->entries = NULL;
rc->eof = 0;
rc->entlen = 0;
rc->buflen = 0;
rc->actlen = 0;
rc->flags = RDDIRREQ;
cv_init(&rc->cv, NULL, CV_DEFAULT, NULL);
rc->error = 0;
mutex_init(&rdip->lock, NULL, MUTEX_DEFAULT, NULL);
rdip->count = 1;
#ifdef DEBUG
atomic_inc_64(&clstat4_debug.dirent.value.ui64);
#endif
}
return (rc);
}
static void
rddir4_cache_hold(rddir4_cache *rc)
{
rddir4_cache_impl *rdip = (rddir4_cache_impl *)rc->data;
mutex_enter(&rdip->lock);
rdip->count++;
mutex_exit(&rdip->lock);
}
void
rddir4_cache_rele(rnode4_t *rp, rddir4_cache *rdc)
{
rddir4_cache_impl *rdip = (rddir4_cache_impl *)rdc->data;
ASSERT(MUTEX_HELD(&rp->r_statelock));
if (rdc->flags & RDDIRWAIT) {
rdc->flags &= ~RDDIRWAIT;
cv_broadcast(&rdc->cv);
}
mutex_enter(&rdip->lock);
ASSERT(rdip->count > 0);
if (--rdip->count == 0) {
mutex_exit(&rdip->lock);
rddir4_cache_free(rdip);
} else
mutex_exit(&rdip->lock);
}
static void
rddir4_cache_free(rddir4_cache_impl *rdip)
{
rddir4_cache *rc = &rdip->rc;
#ifdef DEBUG
atomic_dec_64(&clstat4_debug.dirent.value.ui64);
#endif
if (rc->entries != NULL)
kmem_free(rc->entries, rc->buflen);
cv_destroy(&rc->cv);
mutex_destroy(&rdip->lock);
kmem_free(rdip, sizeof (*rdip));
}
static int
cl4_snapshot(kstat_t *ksp, void *buf, int rw)
{
ksp->ks_snaptime = gethrtime();
if (rw == KSTAT_WRITE) {
bcopy(buf, ksp->ks_private, sizeof (clstat4_tmpl));
#ifdef DEBUG
if (INGLOBALZONE(curproc))
bcopy((char *)buf + sizeof (clstat4_tmpl),
&clstat4_debug, sizeof (clstat4_debug));
#endif
} else {
bcopy(ksp->ks_private, buf, sizeof (clstat4_tmpl));
#ifdef DEBUG
bcopy(&clstat4_debug, (char *)buf + sizeof (clstat4_tmpl),
sizeof (clstat4_debug));
#endif
}
return (0);
}
static void *
clinit4_zone(zoneid_t zoneid)
{
kstat_t *nfs4_client_kstat;
struct nfs4_clnt *nfscl;
uint_t ndata;
nfscl = kmem_alloc(sizeof (*nfscl), KM_SLEEP);
mutex_init(&nfscl->nfscl_chtable4_lock, NULL, MUTEX_DEFAULT, NULL);
nfscl->nfscl_chtable4 = NULL;
nfscl->nfscl_zoneid = zoneid;
bcopy(&clstat4_tmpl, &nfscl->nfscl_stat, sizeof (clstat4_tmpl));
ndata = sizeof (clstat4_tmpl) / sizeof (kstat_named_t);
#ifdef DEBUG
ndata += sizeof (clstat4_debug) / sizeof (kstat_named_t);
#endif
if ((nfs4_client_kstat = kstat_create_zone("nfs", 0, "nfs4_client",
"misc", KSTAT_TYPE_NAMED, ndata,
KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE, zoneid)) != NULL) {
nfs4_client_kstat->ks_private = &nfscl->nfscl_stat;
nfs4_client_kstat->ks_snapshot = cl4_snapshot;
kstat_install(nfs4_client_kstat);
}
mutex_enter(&nfs4_clnt_list_lock);
list_insert_head(&nfs4_clnt_list, nfscl);
mutex_exit(&nfs4_clnt_list_lock);
return (nfscl);
}
static void
clfini4_zone(zoneid_t zoneid, void *arg)
{
struct nfs4_clnt *nfscl = arg;
chhead_t *chp, *next;
if (nfscl == NULL)
return;
mutex_enter(&nfs4_clnt_list_lock);
list_remove(&nfs4_clnt_list, nfscl);
mutex_exit(&nfs4_clnt_list_lock);
clreclaim4_zone(nfscl, 0);
for (chp = nfscl->nfscl_chtable4; chp != NULL; chp = next) {
ASSERT(chp->ch_list == NULL);
kmem_free(chp->ch_protofmly, strlen(chp->ch_protofmly) + 1);
next = chp->ch_next;
kmem_free(chp, sizeof (*chp));
}
kstat_delete_byname_zone("nfs", 0, "nfs4_client", zoneid);
mutex_destroy(&nfscl->nfscl_chtable4_lock);
kmem_free(nfscl, sizeof (*nfscl));
}
void
clcleanup4_zone(zoneid_t zoneid)
{
struct nfs4_clnt *nfscl;
mutex_enter(&nfs4_clnt_list_lock);
nfscl = list_head(&nfs4_clnt_list);
for (; nfscl != NULL; nfscl = list_next(&nfs4_clnt_list, nfscl)) {
if (nfscl->nfscl_zoneid == zoneid) {
clreclaim4_zone(nfscl, 0);
break;
}
}
mutex_exit(&nfs4_clnt_list_lock);
}
int
nfs4_subr_init(void)
{
chtab4_cache = kmem_cache_create("client_handle4_cache",
sizeof (struct chtab), 0, NULL, NULL, clreclaim4, NULL,
NULL, 0);
list_create(&nfs4_clnt_list, sizeof (struct nfs4_clnt),
offsetof(struct nfs4_clnt, nfscl_node));
zone_key_create(&nfs4clnt_zone_key, clinit4_zone, NULL, clfini4_zone);
if (nfs4err_delay_time == 0)
nfs4err_delay_time = NFS4ERR_DELAY_TIME;
return (0);
}
int
nfs4_subr_fini(void)
{
kmem_cache_destroy(chtab4_cache);
(void) zone_key_delete(nfs4clnt_zone_key);
return (0);
}
int
nfs4_directio(vnode_t *vp, int cmd, cred_t *cr)
{
int error = 0;
rnode4_t *rp;
rp = VTOR4(vp);
if (cmd == DIRECTIO_ON) {
if (rp->r_flags & R4DIRECTIO)
return (0);
(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
if (rp->r_flags & R4DIRECTIO) {
VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
return (0);
}
if (nfs4_has_pages(vp) &&
((rp->r_flags & R4DIRTY) || rp->r_awcount > 0)) {
error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0,
B_INVAL, cr, NULL);
if (error) {
if (error == ENOSPC || error == EDQUOT) {
mutex_enter(&rp->r_statelock);
if (!rp->r_error)
rp->r_error = error;
mutex_exit(&rp->r_statelock);
}
VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
return (error);
}
}
mutex_enter(&rp->r_statelock);
rp->r_flags |= R4DIRECTIO;
mutex_exit(&rp->r_statelock);
VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
return (0);
}
if (cmd == DIRECTIO_OFF) {
mutex_enter(&rp->r_statelock);
rp->r_flags &= ~R4DIRECTIO;
mutex_exit(&rp->r_statelock);
return (0);
}
return (EINVAL);
}
bool_t
nfs4_has_pages(vnode_t *vp)
{
rnode4_t *rp;
rp = VTOR4(vp);
if (IS_SHADOW(vp, rp))
vp = RTOV4(rp);
return (vn_has_cached_data(vp));
}
static struct try_failover_tab {
enum clnt_stat cstat;
int error;
} try_failover_table [] = {
RPC_SUCCESS, 0,
RPC_CANTENCODEARGS, 0,
RPC_CANTDECODERES, 0,
RPC_CANTSEND, ECOMM,
RPC_CANTRECV, ECOMM,
RPC_TIMEDOUT, ETIMEDOUT,
RPC_VERSMISMATCH, 0,
RPC_AUTHERROR, 0,
RPC_PROGUNAVAIL, 0,
RPC_PROGVERSMISMATCH, 0,
RPC_PROCUNAVAIL, 0,
RPC_CANTDECODEARGS, 0,
RPC_SYSTEMERROR, ENOSR,
RPC_UNKNOWNHOST, EHOSTUNREACH,
RPC_RPCBFAILURE, ENETUNREACH,
RPC_PROGNOTREGISTERED, ECONNREFUSED,
RPC_FAILED, ETIMEDOUT,
RPC_UNKNOWNPROTO, EHOSTUNREACH,
RPC_INTR, 0,
RPC_UNKNOWNADDR, EHOSTUNREACH,
RPC_TLIERROR, 0,
RPC_NOBROADCAST, EHOSTUNREACH,
RPC_N2AXLATEFAILURE, ECONNREFUSED,
RPC_UDERROR, 0,
RPC_INPROGRESS, 0,
RPC_STALERACHANDLE, EINVAL,
RPC_CANTCONNECT, ECONNREFUSED,
RPC_XPRTFAILED, ECONNABORTED,
RPC_CANTCREATESTREAM, ECONNREFUSED,
RPC_CANTSTORE, ENOBUFS
};
int
nfs4_try_failover(nfs4_error_t *ep)
{
if (ep->error == ETIMEDOUT || ep->stat == NFS4ERR_RESOURCE)
return (TRUE);
if (ep->error && ep->rpc_status != RPC_SUCCESS)
return (try_failover(ep->rpc_status) != 0 ? TRUE : FALSE);
return (FALSE);
}
static int
try_failover(enum clnt_stat rpc_status)
{
int err = 0;
if (rpc_status == RPC_SUCCESS)
return (0);
#ifdef DEBUG
if (rpc_status != 0 && nfs4_try_failover_any) {
err = ETIMEDOUT;
goto done;
}
#endif
if (rpc_status < RPC_SUCCESS || rpc_status >=
sizeof (try_failover_table)/sizeof (try_failover_table[0]) ||
try_failover_table[rpc_status].cstat != rpc_status) {
err = ETIMEDOUT;
#ifdef DEBUG
cmn_err(CE_NOTE, "try_failover: unexpected rpc error %d",
rpc_status);
#endif
} else
err = try_failover_table[rpc_status].error;
done:
if (rpc_status)
NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
"nfs4_try_failover: %strying failover on error %d",
err ? "" : "NOT ", rpc_status));
return (err);
}
void
nfs4_error_zinit(nfs4_error_t *ep)
{
ep->error = 0;
ep->stat = NFS4_OK;
ep->rpc_status = RPC_SUCCESS;
}
void
nfs4_error_init(nfs4_error_t *ep, int error)
{
ep->error = error;
ep->stat = NFS4_OK;
ep->rpc_status = RPC_SUCCESS;
}
#ifdef DEBUG
int
hash16(void *p, int len)
{
int i, rem;
uint_t *wp;
uint_t key = 0;
if ((rem = len & 3) != 0)
len &= ~3;
for (i = 0, wp = (uint_t *)p; i < len; i += 4, wp++) {
key ^= (*wp >> 16) ^ *wp;
}
for (i = 0; i < rem; i++)
key ^= *((uchar_t *)p + i);
return (key & 0xffff);
}
char *
rnode4info(rnode4_t *rp)
{
static char buf[80];
nfs4_fhandle_t fhandle;
char *path;
char *type;
if (rp == NULL)
return ("null");
if (rp->r_flags & R4ISXATTR)
type = "attr";
else if (RTOV4(rp)->v_flag & V_XATTRDIR)
type = "attrdir";
else if (RTOV4(rp)->v_flag & VROOT)
type = "root";
else if (RTOV4(rp)->v_type == VDIR)
type = "dir";
else if (RTOV4(rp)->v_type == VREG)
type = "file";
else
type = "other";
sfh4_copyval(rp->r_fh, &fhandle);
path = fn_path(rp->r_svnode.sv_name);
(void) snprintf(buf, 80, "$%p[%s], type=%s, flags=%04X, FH=%04X\n",
(void *)rp, path, type, rp->r_flags,
hash16((void *)&fhandle.fh_buf, fhandle.fh_len));
kmem_free(path, strlen(path)+1);
return (buf);
}
#endif