6232737 Client should support NFS4ERR_MOVED and fs_locations
authorRobert Thurlow <Robert.Thurlow@Sun.COM>
Wed, 09 Dec 2009 17:27:22 -0600
changeset 11291 80bdcd03e626
parent 11290 0e798b22ab58
child 11292 f9f7f6a68749
6232737 Client should support NFS4ERR_MOVED and fs_locations 6232743 Server should support NFS4ERR_MOVED and fs_locations 6891289 client panick mutex_vector_tryenter with some stress testing
usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c
usr/src/cmd/fs.d/autofs/Makefile
usr/src/cmd/fs.d/autofs/autod_nfs.c
usr/src/cmd/fs.d/autofs/autod_xdr.c
usr/src/cmd/fs.d/nfs/Makefile
usr/src/cmd/fs.d/nfs/lib/nfs_resolve.c
usr/src/cmd/fs.d/nfs/lib/nfs_resolve.h
usr/src/cmd/fs.d/nfs/lib/ref_subr.c
usr/src/cmd/fs.d/nfs/lib/ref_subr.h
usr/src/cmd/fs.d/nfs/nfsmapid/Makefile
usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c
usr/src/cmd/fs.d/nfs/nfsref/Makefile
usr/src/cmd/fs.d/nfs/nfsref/nfsref.c
usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c
usr/src/cmd/fs.d/nfs/rp_basic/Makefile
usr/src/cmd/fs.d/nfs/rp_basic/Makefile.com
usr/src/cmd/fs.d/nfs/rp_basic/amd64/Makefile
usr/src/cmd/fs.d/nfs/rp_basic/i386/Makefile
usr/src/cmd/fs.d/nfs/rp_basic/libnfs_basic.c
usr/src/cmd/fs.d/nfs/rp_basic/mapfile-vers
usr/src/cmd/fs.d/nfs/rp_basic/sparc/Makefile
usr/src/cmd/fs.d/nfs/rp_basic/sparcv9/Makefile
usr/src/cmd/fs.d/nfs/svc/server.xml
usr/src/head/rpcsvc/daemon_utils.h
usr/src/head/rpcsvc/nfs4_prot.x
usr/src/lib/libshare/nfs/libshare_nfs.c
usr/src/lib/libshare/nfs/libshare_nfs.h
usr/src/pkgdefs/SUNWnfscu/prototype_com
usr/src/pkgdefs/SUNWnfssu/prototype_com
usr/src/pkgdefs/SUNWnfssu/prototype_i386
usr/src/pkgdefs/SUNWnfssu/prototype_sparc
usr/src/uts/common/fs/fs_subr.c
usr/src/uts/common/fs/nfs/nfs3_srv.c
usr/src/uts/common/fs/nfs/nfs4_attr.c
usr/src/uts/common/fs/nfs/nfs4_callback.c
usr/src/uts/common/fs/nfs/nfs4_client.c
usr/src/uts/common/fs/nfs/nfs4_client_debug.c
usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c
usr/src/uts/common/fs/nfs/nfs4_common.c
usr/src/uts/common/fs/nfs/nfs4_idmap.c
usr/src/uts/common/fs/nfs/nfs4_recovery.c
usr/src/uts/common/fs/nfs/nfs4_rnode.c
usr/src/uts/common/fs/nfs/nfs4_srv.c
usr/src/uts/common/fs/nfs/nfs4_srv_attr.c
usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c
usr/src/uts/common/fs/nfs/nfs4_state.c
usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c
usr/src/uts/common/fs/nfs/nfs4_subr.c
usr/src/uts/common/fs/nfs/nfs4_vfsops.c
usr/src/uts/common/fs/nfs/nfs4_vnops.c
usr/src/uts/common/fs/nfs/nfs4_xdr.c
usr/src/uts/common/fs/nfs/nfs_srv.c
usr/src/uts/common/fs/nfs/nfs_stats.c
usr/src/uts/common/fs/vnode.c
usr/src/uts/common/nfs/export.h
usr/src/uts/common/nfs/mount.h
usr/src/uts/common/nfs/nfs.h
usr/src/uts/common/nfs/nfs4.h
usr/src/uts/common/nfs/nfs4_attr.h
usr/src/uts/common/nfs/nfs4_clnt.h
usr/src/uts/common/nfs/nfs4_kprot.h
usr/src/uts/common/nfs/nfsid_map.h
usr/src/uts/common/nfs/rnode4.h
usr/src/uts/common/sys/mkdev.h
usr/src/uts/common/sys/vnode.h
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c	Wed Dec 09 17:27:22 2009 -0600
@@ -19,13 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-
 #include <ctype.h>
 #include <string.h>
 #include <strings.h>
@@ -578,7 +575,7 @@
 static char *utf8localize(utf8string *);
 static void utf8free(void);
 static void sum_pathname4(char *, size_t, pathname4 *);
-static void detail_pathname4(pathname4 *pathp);
+static void detail_pathname4(pathname4 *pathp, char *);
 static void sum_compname4(char *buf, size_t buflen, component4 *comp);
 static void detail_compname4(component4 *comp);
 
@@ -696,8 +693,8 @@
 
 		if (type == CALL) {
 			(void) sprintf(line, "NFS C %s",
-				    proc == CB_COMPOUND ? "CB4" :
-					    cb_procnames_short[proc]);
+			    proc == CB_COMPOUND ? "CB4" :
+			    cb_procnames_short[proc]);
 			line += strlen(line);
 
 			if (proc == CB_COMPOUND) {
@@ -706,15 +703,15 @@
 				if (!xdr_utf8string(&xdrm, &tag))
 					longjmp(xdr_err, 1);
 				sprintf(line, " (%.20s) %s",
-					utf8localize(&tag),
-					sum_cb_compound4args());
+				    utf8localize(&tag),
+				    sum_cb_compound4args());
 				xdr_free(xdr_utf8string, (char *)&tag);
 			}
 			check_retransmit(line, xid);
 		} else {
 			(void) sprintf(line, "NFS R %s ",
-				    proc == CB_COMPOUND ? "CB4" :
-					    cb_procnames_short[proc]);
+			    proc == CB_COMPOUND ? "CB4" :
+			    cb_procnames_short[proc]);
 			line += strlen(line);
 			if (proc == CB_COMPOUND)
 				sum_comp4res(line, sum_cb_compound4res);
@@ -725,7 +722,7 @@
 		show_header("NFS:  ", "Sun NFS4 CallBack", len);
 		show_space();
 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
-			proc, cb_procnames_long[proc]);
+		    proc, cb_procnames_long[proc]);
 		if (proc == CB_COMPOUND) {
 			if (type == CALL) {
 				showxdr_utf8string("Tag = %s");
@@ -736,7 +733,7 @@
 				status = getxdr_long();
 				showxdr_utf8string("Tag = %s");
 				sprintf(get_line(0, 0), "Status = %d (%s)",
-					status, status_name(status));
+				    status, status_name(status));
 				detail_cb_resop4();
 			}
 		}
@@ -766,8 +763,8 @@
 
 		if (type == CALL) {
 			(void) sprintf(line, "NFS C %s",
-				    proc == NFSPROC4_COMPOUND ? "4" :
-					    procnames_short[proc]);
+			    proc == NFSPROC4_COMPOUND ? "4" :
+			    procnames_short[proc]);
 			line += strlen(line);
 
 			if (proc == NFSPROC4_COMPOUND) {
@@ -776,15 +773,15 @@
 				if (!xdr_utf8string(&xdrm, &tag))
 					longjmp(xdr_err, 1);
 				sprintf(line, " (%.20s) %s",
-					utf8localize(&tag),
-					sum_compound4args());
+				    utf8localize(&tag),
+				    sum_compound4args());
 				xdr_free(xdr_utf8string, (char *)&tag);
 			}
 			check_retransmit(line, xid);
 		} else {
 			(void) sprintf(line, "NFS R %s ",
-				    proc == NFSPROC4_COMPOUND ? "4" :
-					    procnames_short[proc]);
+			    proc == NFSPROC4_COMPOUND ? "4" :
+			    procnames_short[proc]);
 			line += strlen(line);
 
 			if (proc == NFSPROC4_COMPOUND)
@@ -796,7 +793,7 @@
 		show_header("NFS:  ", "Sun NFS", len);
 		show_space();
 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
-			proc, procnames_long[proc]);
+		    proc, procnames_long[proc]);
 		if (proc == NFSPROC4_COMPOUND) {
 			if (type == CALL) {
 				showxdr_utf8string("Tag = %s");
@@ -807,7 +804,7 @@
 				status = getxdr_long();
 				showxdr_utf8string("Tag = %s");
 				sprintf(get_line(0, 0), "Status = %d (%s)",
-					status, status_name(status));
+				    status, status_name(status));
 				detail_nfs_resop4();
 			}
 		}
@@ -840,7 +837,7 @@
 	if (setjmp(xdr_err)) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf),
-			nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
+		    nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
 		return (buf);
 	}
 
@@ -863,7 +860,7 @@
 			longjmp(xdr_err, 1);
 		}
 		snprintf(bp, buflen - (bp - buf), "%s ",
-			opcode_name(one_op.argop));
+		    opcode_name(one_op.argop));
 		bp += strlen(bp);
 
 		operand = sum_operand(&one_op);
@@ -952,7 +949,7 @@
 	if (setjmp(xdr_err)) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
-			" RPC>");
+		    " RPC>");
 		return (buf);
 	}
 
@@ -981,7 +978,7 @@
 		}
 
 		snprintf(bp, buflen - (bp - buf), "%s ",
-			cb_opcode_name(one_op.argop));
+		    cb_opcode_name(one_op.argop));
 		bp += strlen(bp);
 		operand = sum_cb_operand(&one_op);
 		if (strlen(operand) > 0) {
@@ -1058,11 +1055,11 @@
 		longjmp(xdr_err, 1);
 
 	(void) sprintf(get_line(0, 0), "Minor version = %u",
-		minor_version);
+	    minor_version);
 
 	numops = getxdr_long();
 	(void) sprintf(get_line(0, 0), "Number of operations = %d",
-		    numops);
+	    numops);
 
 	while (numops-- > 0) {
 		bzero(&one_op, sizeof (one_op));
@@ -1074,7 +1071,7 @@
 
 		get_line(0, 0);		/* blank line to separate ops */
 		sprintf(get_line(0, 0), "Op = %d (%s)",
-			one_op.argop, opcode_name(one_op.argop));
+		    one_op.argop, opcode_name(one_op.argop));
 		if (one_op.argop < num_opcodes) {
 			fmtproc = opcode_info[one_op.argop].dtlarg;
 			if (fmtproc != NULL)
@@ -1104,16 +1101,16 @@
 	if (!xdr_uint32_t(&xdrm, &minor_version))
 		longjmp(xdr_err, 1);
 	(void) sprintf(get_line(0, 0), "Minor version = %u",
-		minor_version);
+	    minor_version);
 
 	if (!xdr_uint32_t(&xdrm, &callback_ident))
 		longjmp(xdr_err, 1);
 	(void) sprintf(get_line(0, 0), "Callback Ident = %u",
-		callback_ident);
+	    callback_ident);
 
 	numops = getxdr_long();
 	(void) sprintf(get_line(0, 0), "Number of operations = %d",
-		    numops);
+	    numops);
 
 	while (numops-- > 0) {
 		bzero(&one_op, sizeof (one_op));
@@ -1124,7 +1121,7 @@
 
 		get_line(0, 0);		/* blank line to separate ops */
 		sprintf(get_line(0, 0), "Op = %d (%s)",
-			one_op.argop, cb_opcode_name(one_op.argop));
+		    one_op.argop, cb_opcode_name(one_op.argop));
 		if (one_op.argop < cb_num_opcodes) {
 			fmtproc = cb_opcode_info[one_op.argop].dtlarg;
 			if (fmtproc != NULL)
@@ -1268,10 +1265,10 @@
 
 	if ((spec = special_stateid(stateid)) < 0)
 		snprintf(buf, sizeof (buf), "%s%04X:%u", prefix,
-			stateid_hash(stateid), stateid->seqid);
+		    stateid_hash(stateid), stateid->seqid);
 	else
 		snprintf(buf, sizeof (buf), "%s%s", prefix,
-			spec == 0 ? "SPC0" : (spec == 1 ? "SPC1" : "SPC?"));
+		    spec == 0 ? "SPC0" : (spec == 1 ? "SPC1" : "SPC?"));
 	return (buf);
 }
 
@@ -1285,15 +1282,15 @@
 
 	if (spec < 0)
 		sprintf(get_line(0, 0), "%sState ID hash = %04X",
-			prefix, stateid_hash(stateid));
+		    prefix, stateid_hash(stateid));
 	else
 		sprintf(get_line(0, 0), "%sState ID hash = %s",	prefix,
-			spec == 0 ? "SPECIAL_0" :
-				(spec == 1 ? "SPECIAL_1" : "SPECIAL_?"));
+		    spec == 0 ? "SPECIAL_0" :
+		    (spec == 1 ? "SPECIAL_1" : "SPECIAL_?"));
 
 	sprintf(get_line(0, 0), "    len = %u    val = %s",
-		sizeof (stateid->other),
-		tohex(stateid->other, sizeof (stateid->other)));
+	    sizeof (stateid->other),
+	    tohex(stateid->other, sizeof (stateid->other)));
 
 	/*
 	 * If spec 0/1 stateid, print seqid in hex; otherwise,
@@ -1307,7 +1304,7 @@
 		sprintf(seqstr, "%08X", stateid->seqid);
 
 	sprintf(get_line(0, 0), "    %sState ID Sequence ID = %s",
-		prefix, seqstr);
+	    prefix, seqstr);
 }
 
 
@@ -1317,9 +1314,9 @@
 	static char buf[64];
 
 	sprintf(buf, "%s %llu %llu LO=%04X",
-		sum_lock_type_name(denied->locktype),
-		denied->offset, denied->length,
-		owner_hash(&denied->owner.owner));
+	    sum_lock_type_name(denied->locktype),
+	    denied->offset, denied->length,
+	    owner_hash(&denied->owner.owner));
 
 	return (buf);
 }
@@ -1368,7 +1365,7 @@
 detail_createhow4(createhow4 *crtp)
 {
 	sprintf(get_line(0, 0), "Method = %s",
-		createhow4_name(crtp));
+	    createhow4_name(crtp));
 
 	switch (crtp->mode) {
 	case UNCHECKED4:
@@ -1377,8 +1374,8 @@
 		break;
 	case EXCLUSIVE4:
 		sprintf(get_line(0, 0), "  Verifier = %s",
-			tohex(crtp->createhow4_u.createverf,
-				NFS4_VERIFIER_SIZE));
+		    tohex(crtp->createhow4_u.createverf,
+		    NFS4_VERIFIER_SIZE));
 		break;
 	}
 }
@@ -1387,17 +1384,17 @@
 detail_createtype4(createtype4 *crtp)
 {
 	sprintf(get_line(0, 0), "Type = %s",
-		detail_type_name(crtp->type));
+	    detail_type_name(crtp->type));
 	switch (crtp->type) {
 	case NF4LNK:
 		sprintf(get_line(0, 0), "Linkdata = %s",
-			utf8localize(&crtp->createtype4_u.linkdata));
+		    utf8localize(&crtp->createtype4_u.linkdata));
 		break;
 	case NF4BLK:
 	case NF4CHR:
 		sprintf(get_line(0, 0), "Specdata1 = %04x Specdata2 = %04x",
-			crtp->createtype4_u.devdata.specdata1,
-			crtp->createtype4_u.devdata.specdata2);
+		    crtp->createtype4_u.devdata.specdata1,
+		    crtp->createtype4_u.devdata.specdata2);
 		break;
 	default:
 		break;
@@ -1426,7 +1423,7 @@
 	CLOSE4args *args = (CLOSE4args *)obj;
 
 	snprintf(buf, buflen, "SQ=%u %s",
-		args->seqid, sum_open_stateid(&args->open_stateid));
+	    args->seqid, sum_open_stateid(&args->open_stateid));
 }
 
 static void
@@ -1444,7 +1441,7 @@
 	COMMIT4args *args = (COMMIT4args *)obj;
 
 	snprintf(buf, buflen, "at %llu for %u ", args->offset,
-		args->count);
+	    args->count);
 }
 
 static void
@@ -1478,7 +1475,7 @@
 	CREATE4args *args = (CREATE4args *)obj;
 
 	snprintf(buf, buflen, "%s %s ", component_name(&args->objname),
-		sum_type_name(args->objtype.type));
+	    sum_type_name(args->objtype.type));
 }
 
 static void
@@ -1566,7 +1563,7 @@
 	char *bp = buf;
 
 	snprintf(bp, buflen, "%s %s TR=%s", sum_fh4(&args->fh),
-		sum_stateid(&args->stateid), args->truncate ? "T" : "F");
+	    sum_stateid(&args->stateid), args->truncate ? "T" : "F");
 }
 
 static void
@@ -1577,7 +1574,7 @@
 	detail_fh4(&args->fh);
 	detail_stateid(&args->stateid);
 	sprintf(get_line(0, 0), "Truncate = %s",
-		args->truncate ? "True" : "False");
+	    args->truncate ? "True" : "False");
 }
 
 
@@ -1608,9 +1605,9 @@
 	blen -= len;
 
 	snprintf(bp, blen, " AC=%s DN=%s OO=%04X",
-		sum_open_share_access(args->share_access),
-		sum_open_share_deny(args->share_deny),
-				owner_hash(&args->owner.owner));
+	    sum_open_share_access(args->share_access),
+	    sum_open_share_deny(args->share_deny),
+	    owner_hash(&args->owner.owner));
 }
 
 static void
@@ -1623,9 +1620,9 @@
 	detail_open_owner(&args->owner);
 	sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
 	sprintf(get_line(0, 0), "Access = 0x%x (%s)",
-		args->share_access, sum_open_share_access(args->share_access));
+	    args->share_access, sum_open_share_access(args->share_access));
 	sprintf(get_line(0, 0), "Deny   = 0x%x (%s)",
-		args->share_deny, sum_open_share_access(args->share_deny));
+	    args->share_deny, sum_open_share_access(args->share_deny));
 }
 
 static void
@@ -1634,7 +1631,7 @@
 	OPENATTR4args *args = (OPENATTR4args *)obj;
 
 	snprintf(buf, buflen, "CD=%s",
-		args->createdir ? "T" : "F");
+	    args->createdir ? "T" : "F");
 }
 
 static void
@@ -1643,7 +1640,7 @@
 	OPENATTR4args *args = (OPENATTR4args *)obj;
 
 	sprintf(get_line(0, 0), "CreateDir = %s",
-		args->createdir ? "True" : "False");
+	    args->createdir ? "True" : "False");
 }
 
 static void
@@ -1653,7 +1650,7 @@
 	OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj;
 
 	snprintf(bp, buflen, "SQ=%u %s", args->seqid,
-		sum_open_stateid(&args->open_stateid));
+	    sum_open_stateid(&args->open_stateid));
 }
 
 static void
@@ -1671,9 +1668,9 @@
 	OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj;
 
 	snprintf(buf, buflen, "SQ=%u %s AC=%s DN=%s",
-		args->seqid, sum_open_stateid(&args->open_stateid),
-		sum_open_share_access(args->share_access),
-		sum_open_share_deny(args->share_deny));
+	    args->seqid, sum_open_stateid(&args->open_stateid),
+	    sum_open_share_access(args->share_access),
+	    sum_open_share_deny(args->share_deny));
 }
 
 static void
@@ -1684,9 +1681,9 @@
 	sprintf(get_line(0, 0), "Open Sequence ID = %u", args->seqid);
 	detail_open_stateid(&args->open_stateid);
 	sprintf(get_line(0, 0), "Access = 0x%x (%s)",
-		args->share_access, sum_open_share_access(args->share_access));
+	    args->share_access, sum_open_share_access(args->share_access));
 	sprintf(get_line(0, 0), "Deny   = 0x%x (%s)",
-		args->share_deny, sum_open_share_access(args->share_deny));
+	    args->share_deny, sum_open_share_access(args->share_deny));
 }
 
 static void
@@ -1719,22 +1716,22 @@
 	LINK4args *args = (LINK4args *)obj;
 
 	sprintf(get_line(0, 0), "New name = %s",
-		component_name(&args->newname));
+	    component_name(&args->newname));
 }
 
 static void
 sum_open_to_lock_owner(char *buf, int buflen, open_to_lock_owner4 *own)
 {
 	snprintf(buf, buflen, " OSQ=%u %s LSQ=%u LO=%04X", own->open_seqid,
-		sum_open_stateid(&own->open_stateid), own->lock_seqid,
-		owner_hash(&own->lock_owner.owner));
+	    sum_open_stateid(&own->open_stateid), own->lock_seqid,
+	    owner_hash(&own->lock_owner.owner));
 }
 
 static void
 sum_exist_lock_owner(char *buf, int buflen, exist_lock_owner4 *own)
 {
 	snprintf(buf, buflen, " LSQ=%u %s", own->lock_seqid,
-		sum_lock_stateid(&own->lock_stateid));
+	    sum_lock_stateid(&own->lock_stateid));
 }
 
 static void
@@ -1779,9 +1776,9 @@
 	char *bp = buf;
 
 	snprintf(buf, buflen, "%s%s%llu:%llu",
-		sum_lock_type_name(args->locktype),
-		args->reclaim ? " reclaim " : " ",
-		args->offset, args->length);
+	    sum_lock_type_name(args->locktype),
+	    args->reclaim ? " reclaim " : " ",
+	    args->offset, args->length);
 
 	bp += strlen(buf);
 	sum_locker(bp, buflen - (bp - buf), &args->locker);
@@ -1819,7 +1816,7 @@
 
 	sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
 	sprintf(get_line(0, 0), "Reclaim = %s",
-		args->reclaim ? "TRUE" : "FALSE");
+	    args->reclaim ? "TRUE" : "FALSE");
 	sprintf(get_line(0, 0), "Offset = %llu", args->offset);
 	sprintf(get_line(0, 0), "Length = %llu", args->length);
 	detail_locker(&args->locker);
@@ -1831,7 +1828,7 @@
 	LOCKT4args *args = (LOCKT4args *)obj;
 
 	snprintf(buf, buflen, "R=%llu:%llu",
-		args->offset, args->length);
+	    args->offset, args->length);
 }
 
 static void
@@ -1851,8 +1848,8 @@
 	LOCKU4args *args = (LOCKU4args *)obj;
 
 	snprintf(buf, buflen, "R=%llu:%llu LSQ=%u %s",
-		args->offset, args->length, args->seqid,
-		sum_lock_stateid(&args->lock_stateid));
+	    args->offset, args->length, args->seqid,
+	    sum_lock_stateid(&args->lock_stateid));
 }
 
 
@@ -1890,7 +1887,7 @@
 	READ4args *args = (READ4args *)obj;
 
 	snprintf(buf, buflen, "%s at %llu for %u",
-		sum_stateid(&args->stateid), args->offset, args->count);
+	    sum_stateid(&args->stateid), args->offset, args->count);
 }
 
 static void
@@ -1909,8 +1906,8 @@
 	READDIR4args *args = (READDIR4args *)obj;
 
 	snprintf(buf, buflen, "Cookie=%llu (%s) for %u/%u",
-		args->cookie, tohex(args->cookieverf, NFS4_VERIFIER_SIZE),
-		args->dircount, args->maxcount);
+	    args->cookie, tohex(args->cookieverf, NFS4_VERIFIER_SIZE),
+	    args->dircount, args->maxcount);
 }
 
 static void
@@ -1920,7 +1917,7 @@
 
 	sprintf(get_line(0, 0), "Cookie = %llu", args->cookie);
 	sprintf(get_line(0, 0), "Verifier = %s",
-		tohex(args->cookieverf, NFS4_VERIFIER_SIZE));
+	    tohex(args->cookieverf, NFS4_VERIFIER_SIZE));
 	sprintf(get_line(0, 0), "Dircount = %u", args->dircount);
 	sprintf(get_line(0, 0), "Maxcount = %u", args->maxcount);
 	detail_attr_bitmap("", &args->attr_request, NULL);
@@ -1949,8 +1946,8 @@
 	RENAME4args *args = (RENAME4args *)obj;
 
 	snprintf(buf, buflen, "%s to %s",
-		component_name(&args->oldname),
-		component_name(&args->newname));
+	    component_name(&args->oldname),
+	    component_name(&args->newname));
 }
 
 static void
@@ -1959,9 +1956,9 @@
 	RENAME4args *args = (RENAME4args *)obj;
 
 	sprintf(get_line(0, 0), "Old name = %s",
-		component_name(&args->oldname));
+	    component_name(&args->oldname));
 	sprintf(get_line(0, 0), "New name = %s",
-		component_name(&args->newname));
+	    component_name(&args->newname));
 }
 
 static void
@@ -1985,7 +1982,7 @@
 	SECINFO4args *args = (SECINFO4args *)obj;
 
 	snprintf(buf, buflen, "%s",
-		component_name(&args->name));
+	    component_name(&args->name));
 }
 
 static void
@@ -1994,7 +1991,7 @@
 	SECINFO4args *args = (SECINFO4args *)obj;
 
 	sprintf(get_line(0, 0), "Name = %s",
-		component_name(&args->name));
+	    component_name(&args->name));
 }
 
 static void
@@ -2020,9 +2017,9 @@
 	SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
 
 	snprintf(buf, buflen, "Prog=%u ID=%s Addr=%s CBID=%u",
-		args->callback.cb_program,
-		args->callback.cb_location.r_netid,
-		args->callback.cb_location.r_addr, args->callback_ident);
+	    args->callback.cb_program,
+	    args->callback.cb_location.r_netid,
+	    args->callback.cb_location.r_addr, args->callback_ident);
 }
 
 static void
@@ -2031,17 +2028,17 @@
 	SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
 
 	sprintf(get_line(0, 0), "Verifier=%s",
-		tohex(args->client.verifier, NFS4_VERIFIER_SIZE));
+	    tohex(args->client.verifier, NFS4_VERIFIER_SIZE));
 	sprintf(get_line(0, 0), "ID = (%d) %s",
-		args->client.id.id_len,
-		tohex(args->client.id.id_val, args->client.id.id_len));
+	    args->client.id.id_len,
+	    tohex(args->client.id.id_val, args->client.id.id_len));
 
 	sprintf(get_line(0, 0), "Callback Program = %u",
-		args->callback.cb_program);
+	    args->callback.cb_program);
 	sprintf(get_line(0, 0), "Callback Net ID = %s",
-		args->callback.cb_location.r_netid);
+	    args->callback.cb_location.r_netid);
 	sprintf(get_line(0, 0), "Callback Addr = %s",
-		args->callback.cb_location.r_addr);
+	    args->callback.cb_location.r_addr);
 	sprintf(get_line(0, 0), "Callback Ident = %u", args->callback_ident);
 }
 
@@ -2051,7 +2048,7 @@
 	SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj;
 
 	snprintf(buf, buflen, "%s CFV=%s", sum_clientid(args->clientid),
-		tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
+	    tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
 }
 
 static void
@@ -2061,7 +2058,7 @@
 
 	detail_clientid(args->clientid);
 	sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
-		tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
+	    tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
 }
 
 
@@ -2079,7 +2076,7 @@
 	WRITE4args *args = (WRITE4args *)obj;
 
 	snprintf(buf, buflen, "%s at %llu for %u",
-		sum_stateid(&args->stateid), args->offset, args->data.data_len);
+	    sum_stateid(&args->stateid), args->offset, args->data.data_len);
 }
 
 static void
@@ -2134,7 +2131,7 @@
 	jmp_buf old_errbuf;
 
 	xdrmem_create(&attrxdr, attrp->attr_vals.attrlist4_val,
-		    attrp->attr_vals.attrlist4_len, XDR_DECODE);
+	    attrp->attr_vals.attrlist4_len, XDR_DECODE);
 
 	bcopy(xdr_err, old_errbuf, sizeof (old_errbuf));
 	if (setjmp(xdr_err)) {
@@ -2171,8 +2168,8 @@
 		bp = buf + curlen;
 		remaining = buflen - curlen;
 		snprintf(bp, remaining,
-			num_words == 0 ? "%x" : " %x",
-			mapp->bitmap4_val[num_words]);
+		    num_words == 0 ? "%x" : " %x",
+		    mapp->bitmap4_val[num_words]);
 	}
 }
 
@@ -2216,7 +2213,7 @@
 					    attr_name(attrnum + bit));
 					if (unpacked != NULL)
 						unpacked->map[attrnum + bit] =
-							1;
+						    1;
 				}
 			}
 		}
@@ -2238,7 +2235,7 @@
 		longjmp(xdr_err, 1);
 
 	sprintf(line, "(%.20s) %s %s", utf8localize(&tag),
-		status_name(status), sumres_fn());
+	    status_name(status), sumres_fn());
 
 	xdr_free(xdr_utf8string, (char *)&tag);
 }
@@ -2263,7 +2260,7 @@
 	if (setjmp(xdr_err)) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf),
-			nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
+		    nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
 		return (buf);
 	}
 
@@ -2280,7 +2277,7 @@
 		}
 
 		snprintf(bp, buflen - (bp - buf), "%s ",
-			opcode_name(one_res.resop));
+		    opcode_name(one_res.resop));
 		bp += strlen(bp);
 
 		result = sum_result(&one_res);
@@ -2325,7 +2322,7 @@
 	if (setjmp(xdr_err)) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
-			" RPC>");
+		    " RPC>");
 		return (buf);
 	}
 
@@ -2338,8 +2335,8 @@
 			longjmp(xdr_err, 1);
 		}
 		snprintf(bp, buflen - (bp - buf), "%s %s ",
-					cb_opcode_name(one_res.resop),
-					sum_cb_result(&one_res));
+		    cb_opcode_name(one_res.resop),
+		    sum_cb_result(&one_res));
 		bp += strlen(bp);
 
 		xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
@@ -2410,7 +2407,7 @@
 {
 	sprintf(get_line(0, 0), "%s:", msg);
 	sprintf(get_line(0, 0), "  Atomic = %s",
-		infop->atomic ? "TRUE" : "FALSE");
+	    infop->atomic ? "TRUE" : "FALSE");
 	detail_fattr4_change("  Before", infop->before);
 	detail_fattr4_change("  After", infop->after);
 }
@@ -2436,7 +2433,7 @@
 	nfsstat4 status = *(nfsstat4 *)obj;
 
 	sprintf(get_line(0, 0), "Status = %d (%s)", status,
-		status_name(status));
+	    status_name(status));
 }
 
 static void
@@ -2475,9 +2472,9 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		detail_access4("Supported Attributes",
-			    res->ACCESS4res_u.resok4.supported);
+		    res->ACCESS4res_u.resok4.supported);
 		detail_access4("Allowed Attributes",
-			    res->ACCESS4res_u.resok4.access);
+		    res->ACCESS4res_u.resok4.access);
 	}
 }
 
@@ -2488,7 +2485,7 @@
 
 	if (res->status == NFS4_OK)
 		snprintf(buf, buflen, "%s",
-			sum_open_stateid(&res->CLOSE4res_u.open_stateid));
+		    sum_open_stateid(&res->CLOSE4res_u.open_stateid));
 }
 
 static void
@@ -2509,8 +2506,8 @@
 
 	if (res->status == NFS4_OK)
 		snprintf(buf, buflen, "Verf=%s",
-			tohex(res->COMMIT4res_u.resok4.writeverf,
-				NFS4_VERIFIER_SIZE));
+		    tohex(res->COMMIT4res_u.resok4.writeverf,
+		    NFS4_VERIFIER_SIZE));
 }
 
 static void
@@ -2521,8 +2518,8 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		sprintf(get_line(0, 0), "Verifier = %s",
-			tohex(res->COMMIT4res_u.resok4.writeverf,
-				NFS4_VERIFIER_SIZE));
+		    tohex(res->COMMIT4res_u.resok4.writeverf,
+		    NFS4_VERIFIER_SIZE));
 	}
 }
 
@@ -2534,9 +2531,9 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		dtl_change_info("Change Information",
-				&res->CREATE4res_u.resok4.cinfo);
+		    &res->CREATE4res_u.resok4.cinfo);
 		detail_attr_bitmap("", &res->CREATE4res_u.resok4.attrset,
-				NULL);
+		    NULL);
 	}
 }
 
@@ -2589,7 +2586,7 @@
 	if (res->status == NFS4_OK) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s",
-			sum_fh4(&res->GETFH4res_u.resok4.object));
+		    sum_fh4(&res->GETFH4res_u.resok4.object));
 	}
 }
 
@@ -2612,7 +2609,7 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		dtl_change_info("Change Information",
-				&res->LINK4res_u.resok4.cinfo);
+		    &res->LINK4res_u.resok4.cinfo);
 	}
 }
 
@@ -2626,12 +2623,12 @@
 	if (res->status == NFS4_OK) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s",
-			sum_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid));
+		    sum_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid));
 	}
 	if (res->status == NFS4ERR_DENIED) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s",
-			sum_lock_denied(&res->LOCK4res_u.denied));
+		    sum_lock_denied(&res->LOCK4res_u.denied));
 	}
 }
 
@@ -2659,7 +2656,7 @@
 	if (res->status == NFS4ERR_DENIED) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s",
-			sum_lock_denied(&res->LOCKT4res_u.denied));
+		    sum_lock_denied(&res->LOCKT4res_u.denied));
 	}
 }
 
@@ -2684,7 +2681,7 @@
 	bp = buf + strlen(buf);
 	if (res->status == NFS4_OK)
 		snprintf(bp, buflen - (bp - buf), " %s",
-			sum_lock_stateid(&res->LOCKU4res_u.lock_stateid));
+		    sum_lock_stateid(&res->LOCKU4res_u.lock_stateid));
 }
 
 static void
@@ -2712,7 +2709,7 @@
 		blen -= len;
 
 		snprintf(bp, blen, " %s",
-			sum_stateid(&res->OPEN4res_u.resok4.stateid));
+		    sum_stateid(&res->OPEN4res_u.resok4.stateid));
 		bp += (len = strlen(bp));
 		blen -= len;
 
@@ -2735,12 +2732,12 @@
 	if (res->status == NFS4_OK) {
 		detail_stateid(&res->OPEN4res_u.resok4.stateid);
 		dtl_change_info("Change Information",
-			&res->OPEN4res_u.resok4.cinfo);
+		    &res->OPEN4res_u.resok4.cinfo);
 		sprintf(get_line(0, 0), "Flags = 0x%x (%s)",
-			res->OPEN4res_u.resok4.rflags,
-			detail_open_rflags(res->OPEN4res_u.resok4.rflags));
+		    res->OPEN4res_u.resok4.rflags,
+		    detail_open_rflags(res->OPEN4res_u.resok4.rflags));
 		detail_attr_bitmap("", &res->OPEN4res_u.resok4.attrset,
-				NULL);
+		    NULL);
 		detail_delegation(&res->OPEN4res_u.resok4.delegation);
 	}
 }
@@ -2755,8 +2752,8 @@
 	if (res->status == NFS4_OK) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s",
-			sum_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
-					open_stateid));
+		    sum_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
+		    open_stateid));
 	}
 }
 
@@ -2768,7 +2765,7 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		detail_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
-				    open_stateid);
+		    open_stateid);
 	}
 }
 
@@ -2782,8 +2779,8 @@
 	if (res->status == NFS4_OK) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s",
-			sum_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
-					open_stateid));
+		    sum_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
+		    open_stateid));
 	}
 }
 
@@ -2795,7 +2792,7 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		detail_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
-				    open_stateid);
+		    open_stateid);
 	}
 }
 
@@ -2809,8 +2806,8 @@
 	if (res->status == NFS4_OK) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " (%u bytes) %s",
-			res->READ4res_u.resok4.data.data_len,
-			res->READ4res_u.resok4.eof ? "EOF" : "");
+		    res->READ4res_u.resok4.data.data_len,
+		    res->READ4res_u.resok4.eof ? "EOF" : "");
 	}
 }
 
@@ -2822,9 +2819,9 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		sprintf(get_line(0, 0), "Count = %u bytes read",
-			res->READ4res_u.resok4.data.data_len);
+		    res->READ4res_u.resok4.data.data_len);
 		sprintf(get_line(0, 0), "End of file = %s",
-			res->READ4res_u.resok4.eof ? "TRUE" : "FALSE");
+		    res->READ4res_u.resok4.eof ? "TRUE" : "FALSE");
 	}
 }
 
@@ -2844,9 +2841,9 @@
 			num_entries++;
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %d entries (%s)",
-			num_entries,
-			res->READDIR4res_u.resok4.reply.eof
-			? "No more" : "More");
+		    num_entries,
+		    res->READDIR4res_u.resok4.reply.eof
+		    ? "No more" : "More");
 	}
 }
 
@@ -2864,12 +2861,12 @@
 		    ep = ep->nextentry) {
 			num_entries++;
 			sprintf(get_line(0, 0),
-				"------------------ entry #%d",
-				num_entries);
+			    "------------------ entry #%d",
+			    num_entries);
 			sprintf(get_line(0, 0), "Cookie = %llu",
-				ep->cookie);
+			    ep->cookie);
 			sprintf(get_line(0, 0), "Name = %s",
-				component_name(&ep->name));
+			    component_name(&ep->name));
 			detail_fattr4(&ep->attrs);
 		}
 		if (num_entries == 0)
@@ -2877,8 +2874,8 @@
 		sprintf(get_line(0, 0), "EOF = %s",
 		    res->READDIR4res_u.resok4.reply.eof ? "TRUE" : "FALSE");
 		sprintf(get_line(0, 0), "Verifer = %s",
-			tohex(res->READDIR4res_u.resok4.cookieverf,
-				NFS4_VERIFIER_SIZE));
+		    tohex(res->READDIR4res_u.resok4.cookieverf,
+		    NFS4_VERIFIER_SIZE));
 	}
 }
 
@@ -2892,7 +2889,7 @@
 	if (res->status == NFS4_OK) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s",
-			linktext_name(&res->READLINK4res_u.resok4.link));
+		    linktext_name(&res->READLINK4res_u.resok4.link));
 	}
 }
 
@@ -2904,7 +2901,7 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		sprintf(get_line(0, 0), "Link = %s",
-			linktext_name(&res->READLINK4res_u.resok4.link));
+		    linktext_name(&res->READLINK4res_u.resok4.link));
 	}
 }
 
@@ -2916,7 +2913,7 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		dtl_change_info("Change Information",
-				&res->REMOVE4res_u.resok4.cinfo);
+		    &res->REMOVE4res_u.resok4.cinfo);
 	}
 }
 
@@ -2928,9 +2925,9 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		dtl_change_info("Source Change Information",
-				&res->RENAME4res_u.resok4.source_cinfo);
+		    &res->RENAME4res_u.resok4.source_cinfo);
 		dtl_change_info("Target Change Information",
-				&res->RENAME4res_u.resok4.target_cinfo);
+		    &res->RENAME4res_u.resok4.target_cinfo);
 	}
 }
 
@@ -2950,7 +2947,7 @@
 		    numinfo != 0;
 		    infop++, numinfo--) {
 			snprintf(bp, buflen - (bp - buf), " %s",
-				flavor_name(infop->flavor));
+			    flavor_name(infop->flavor));
 			bp += strlen(bp);
 		}
 	}
@@ -2964,7 +2961,7 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		uint_t numinfo =
-			res->SECINFO4res_u.resok4.SECINFO4resok_len;
+		    res->SECINFO4res_u.resok4.SECINFO4resok_len;
 		secinfo4 *infop;
 
 		for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val;
@@ -3004,15 +3001,15 @@
 	case NFS_OK:
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %s CFV=%s",
-			sum_clientid(res->SETCLIENTID4res_u.resok4.clientid),
-			tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
-				NFS4_VERIFIER_SIZE));
+		    sum_clientid(res->SETCLIENTID4res_u.resok4.clientid),
+		    tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
+		    NFS4_VERIFIER_SIZE));
 		break;
 	case NFS4ERR_CLID_INUSE:
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " ID=%s Addr=%s",
-			res->SETCLIENTID4res_u.client_using.r_netid,
-			res->SETCLIENTID4res_u.client_using.r_addr);
+		    res->SETCLIENTID4res_u.client_using.r_netid,
+		    res->SETCLIENTID4res_u.client_using.r_addr);
 		break;
 	}
 }
@@ -3027,14 +3024,14 @@
 	case NFS_OK:
 		detail_clientid(res->SETCLIENTID4res_u.resok4.clientid);
 		sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
-			tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
-				NFS4_VERIFIER_SIZE));
+		    tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
+		    NFS4_VERIFIER_SIZE));
 		break;
 	case NFS4ERR_CLID_INUSE:
 		sprintf(get_line(0, 0), "Used by Net ID = %s",
-			res->SETCLIENTID4res_u.client_using.r_netid);
+		    res->SETCLIENTID4res_u.client_using.r_netid);
 		sprintf(get_line(0, 0), "Used by Addr = %s",
-			res->SETCLIENTID4res_u.client_using.r_addr);
+		    res->SETCLIENTID4res_u.client_using.r_addr);
 		break;
 	}
 }
@@ -3049,8 +3046,8 @@
 	if (res->status == NFS4_OK) {
 		bp = buf + strlen(buf);
 		snprintf(bp, buflen - (bp - buf), " %u (%s)",
-			res->WRITE4res_u.resok4.count,
-			stable_how4_name(res->WRITE4res_u.resok4.committed));
+		    res->WRITE4res_u.resok4.count,
+		    stable_how4_name(res->WRITE4res_u.resok4.committed));
 	}
 }
 
@@ -3062,12 +3059,12 @@
 	dtl_nfsstat4(obj);
 	if (res->status == NFS4_OK) {
 		sprintf(get_line(0, 0), "Count = %u bytes written",
-			res->WRITE4res_u.resok4.count);
+		    res->WRITE4res_u.resok4.count);
 		sprintf(get_line(0, 0), "Stable = %s",
-			stable_how4_name(res->WRITE4res_u.resok4.committed));
+		    stable_how4_name(res->WRITE4res_u.resok4.committed));
 		sprintf(get_line(0, 0), "Verifier = %s",
-			tohex(res->WRITE4res_u.resok4.writeverf,
-				NFS4_VERIFIER_SIZE));
+		    tohex(res->WRITE4res_u.resok4.writeverf,
+		    NFS4_VERIFIER_SIZE));
 	}
 }
 
@@ -3084,7 +3081,7 @@
 
 	numres = getxdr_long();
 	(void) sprintf(get_line(0, 0), "Number of results = %d",
-		    numres);
+	    numres);
 
 	while (numres-- > 0) {
 		bzero(&one_res, sizeof (one_res));
@@ -3096,7 +3093,7 @@
 
 		get_line(0, 0);		/* blank line to separate ops */
 		sprintf(get_line(0, 0), "Op = %d (%s)",
-			one_res.resop, opcode_name(one_res.resop));
+		    one_res.resop, opcode_name(one_res.resop));
 		if (one_res.resop < num_opcodes)
 			fmtproc = opcode_info[one_res.resop].dtlres;
 		else if (one_res.resop == OP_ILLEGAL)
@@ -3129,7 +3126,7 @@
 
 	numres = getxdr_long();
 	(void) sprintf(get_line(0, 0), "Number of results = %d",
-		    numres);
+	    numres);
 
 	while (numres-- > 0) {
 		bzero(&one_res, sizeof (one_res));
@@ -3138,7 +3135,7 @@
 
 		get_line(0, 0);		/* blank line to separate ops */
 		sprintf(get_line(0, 0), "Op = %d (%s)",
-			one_res.resop, cb_opcode_name(one_res.resop));
+		    one_res.resop, cb_opcode_name(one_res.resop));
 		if (one_res.resop < cb_num_opcodes)
 			fmtproc = cb_opcode_info[one_res.resop].dtlres;
 		else if (one_res.resop == OP_CB_ILLEGAL)
@@ -3256,17 +3253,17 @@
 	sprintf(get_line(0, 0), "%s = 0x%08x", descrip, bits);
 
 	(void) sprintf(get_line(0, 0), "	%s",
-		getflag(bits, ACCESS4_READ, "Read", "(no read)"));
+	    getflag(bits, ACCESS4_READ, "Read", "(no read)"));
 	(void) sprintf(get_line(0, 0), "	%s",
-		getflag(bits, ACCESS4_LOOKUP, "Lookup", "(no lookup)"));
+	    getflag(bits, ACCESS4_LOOKUP, "Lookup", "(no lookup)"));
 	(void) sprintf(get_line(0, 0), "	%s",
-		getflag(bits, ACCESS4_MODIFY, "Modify", "(no modify)"));
+	    getflag(bits, ACCESS4_MODIFY, "Modify", "(no modify)"));
 	(void) sprintf(get_line(0, 0), "	%s",
-		getflag(bits, ACCESS4_EXTEND, "Extend", "(no extend)"));
+	    getflag(bits, ACCESS4_EXTEND, "Extend", "(no extend)"));
 	(void) sprintf(get_line(0, 0), "	%s",
-		getflag(bits, ACCESS4_DELETE, "Delete", "(no delete)"));
+	    getflag(bits, ACCESS4_DELETE, "Delete", "(no delete)"));
 	(void) sprintf(get_line(0, 0), "	%s",
-		getflag(bits, ACCESS4_EXECUTE, "Execute", "(no execute)"));
+	    getflag(bits, ACCESS4_EXECUTE, "Execute", "(no execute)"));
 }
 
 
@@ -3281,19 +3278,19 @@
 	switch (claim->claim) {
 	case CLAIM_NULL:
 		snprintf(bp, buflen, "%s ",
-			component_name(&claim->open_claim4_u.file));
+		    component_name(&claim->open_claim4_u.file));
 		break;
 	case CLAIM_PREVIOUS:
 		break;
 	case CLAIM_DELEGATE_CUR:
 		snprintf(bp, buflen, "%s ",
-			component_name(&claim->open_claim4_u.
-					delegate_cur_info.file));
+		    component_name(&claim->open_claim4_u.
+		    delegate_cur_info.file));
 		break;
 	case CLAIM_DELEGATE_PREV:
 		snprintf(bp, buflen, "%s ",
-			component_name(&claim->open_claim4_u.
-					file_delegate_prev));
+		    component_name(&claim->open_claim4_u.
+		    file_delegate_prev));
 		break;
 	}
 }
@@ -3312,12 +3309,12 @@
 		break;
 	case CLAIM_PREVIOUS:
 		snprintf(bp, buflen, " CT=P DT=%s",
-			get_deleg_typestr(claim->open_claim4_u.delegate_type));
+		    get_deleg_typestr(claim->open_claim4_u.delegate_type));
 		break;
 	case CLAIM_DELEGATE_CUR:
 		snprintf(bp, buflen, " CT=DC %s",
-			sum_deleg_stateid(&claim->open_claim4_u.
-				delegate_cur_info.delegate_stateid));
+		    sum_deleg_stateid(&claim->open_claim4_u.
+		    delegate_cur_info.delegate_stateid));
 		break;
 	case CLAIM_DELEGATE_PREV:
 		snprintf(bp, buflen, " CT=DP");
@@ -3358,7 +3355,7 @@
 detail_claim(open_claim4 *claim)
 {
 	sprintf(get_line(0, 0), "Claim Type = %d (%s)",
-		claim->claim, claim_name(claim->claim));
+	    claim->claim, claim_name(claim->claim));
 
 	switch (claim->claim) {
 	case CLAIM_NULL:
@@ -3366,13 +3363,13 @@
 		break;
 	case CLAIM_PREVIOUS:
 		sprintf(get_line(0, 0), "Delegate Type = %s (val = %d)",
-			get_deleg_typestr(claim->open_claim4_u.delegate_type),
-			claim->open_claim4_u.delegate_type);
+		    get_deleg_typestr(claim->open_claim4_u.delegate_type),
+		    claim->open_claim4_u.delegate_type);
 		break;
 	case CLAIM_DELEGATE_CUR:
 		detail_compname4(&claim->open_claim4_u.delegate_cur_info.file);
 		detail_deleg_stateid(&claim->open_claim4_u.delegate_cur_info.
-				    delegate_stateid);
+		    delegate_stateid);
 		break;
 	case CLAIM_DELEGATE_PREV:
 		detail_compname4(&claim->open_claim4_u.file_delegate_prev);
@@ -3415,15 +3412,15 @@
 		break;
 	case OPEN_DELEGATE_READ:
 		snprintf(buf, buflen, " DT=R %s",
-			sum_deleg_stateid(&delp->open_delegation4_u.write.
-					stateid));
+		    sum_deleg_stateid(&delp->open_delegation4_u.write.
+		    stateid));
 		break;
 	case OPEN_DELEGATE_WRITE:
 		snprintf(buf, buflen, " DT=W %s %s",
-			sum_deleg_stateid(&delp->open_delegation4_u.write.
-					stateid),
-			sum_space_limit(&delp->open_delegation4_u.write.
-					space_limit));
+		    sum_deleg_stateid(&delp->open_delegation4_u.write.
+		    stateid),
+		    sum_space_limit(&delp->open_delegation4_u.write.
+		    space_limit));
 		break;
 	default:
 		snprintf(buf, buflen, " DT=?");
@@ -3435,8 +3432,8 @@
 detail_delegation(open_delegation4 *delp)
 {
 	sprintf(get_line(0, 0), "Delegation Type = %d (%s)",
-		delp->delegation_type,
-		delegation_type_name(delp->delegation_type));
+	    delp->delegation_type,
+	    delegation_type_name(delp->delegation_type));
 
 	switch (delp->delegation_type) {
 	case OPEN_DELEGATE_NONE:
@@ -3445,17 +3442,17 @@
 	case OPEN_DELEGATE_READ:
 		detail_deleg_stateid(&delp->open_delegation4_u.read.stateid);
 		sprintf(get_line(0, 0), "Recall = %s",
-			delp->open_delegation4_u.read.recall ?
-			"TRUE" : "FALSE");
+		    delp->open_delegation4_u.read.recall ?
+		    "TRUE" : "FALSE");
 		sprintf(get_line(0, 0), "[nfsacl4]");
 		break;
 	case OPEN_DELEGATE_WRITE:
 		detail_deleg_stateid(&delp->open_delegation4_u.write.stateid);
 		sprintf(get_line(0, 0), "Recall = %s",
-			delp->open_delegation4_u.write.recall ?
-			"TRUE" : "FALSE");
+		    delp->open_delegation4_u.write.recall ?
+		    "TRUE" : "FALSE");
 		detail_space_limit(&delp->open_delegation4_u.write.
-				space_limit);
+		    space_limit);
 		sprintf(get_line(0, 0), "[nfsacl4]");
 		break;
 	}
@@ -3466,10 +3463,10 @@
 detail_open_owner(open_owner4 *owner)
 {
 	sprintf(get_line(0, 0), "Open Owner hash = [%04X] ",
-		owner_hash(&owner->owner));
+	    owner_hash(&owner->owner));
 	sprintf(get_line(0, 0), "    len = %u   val = %s ",
-		owner->owner.owner_len,
-		tohex(owner->owner.owner_val, owner->owner.owner_len));
+	    owner->owner.owner_len,
+	    tohex(owner->owner.owner_val, owner->owner.owner_len));
 	detail_clientid(owner->clientid);
 }
 
@@ -3477,10 +3474,10 @@
 detail_lock_owner(lock_owner4 *owner)
 {
 	sprintf(get_line(0, 0), "Lock Owner hash = [%04X] ",
-		owner_hash(&owner->owner));
+	    owner_hash(&owner->owner));
 	sprintf(get_line(0, 0), "    len = %u   val = %s ",
-		owner->owner.owner_len,
-		tohex(owner->owner.owner_val, owner->owner.owner_len));
+	    owner->owner.owner_len,
+	    tohex(owner->owner.owner_val, owner->owner.owner_len));
 	detail_clientid(owner->clientid);
 }
 
@@ -3500,7 +3497,7 @@
 			break;
 		default:
 			snprintf(bufp, buflen, "OT=CR(?:%d)",
-				flagp->openflag4_u.how.mode);
+			    flagp->openflag4_u.how.mode);
 			break;
 		}
 	} else
@@ -3511,7 +3508,7 @@
 detail_openflag(openflag4 *flagp)
 {
 	sprintf(get_line(0, 0), "Open Type = %s",
-		flagp->opentype == OPEN4_CREATE ? "CREATE" : "NOCREATE");
+	    flagp->opentype == OPEN4_CREATE ? "CREATE" : "NOCREATE");
 	if (flagp->opentype == OPEN4_CREATE)
 		detail_createhow4(&flagp->openflag4_u.how);
 }
@@ -3528,8 +3525,8 @@
 	for (component = 0; component < pathp->pathname4_len;
 	    component++) {
 		snprintf(bp, buflen - (bp - buf),
-			component == 0 ? "%s" : "/%s",
-			component_name(&pathp->pathname4_val[component]));
+		    component == 0 ? "%s" : "/%s",
+		    component_name(&pathp->pathname4_val[component]));
 		bp += strlen(bp);
 	}
 }
@@ -3547,17 +3544,17 @@
 }
 
 static void
-detail_pathname4(pathname4 *pathp)
+detail_pathname4(pathname4 *pathp, char *what)
 {
 	char *bp = get_line(0, 0);
 	uint_t component;
 
-	sprintf(bp, "File name = ");
+	sprintf(bp, what);
 	bp += strlen(bp);
 
 	for (component = 0; component < pathp->pathname4_len; component++) {
 		sprintf(bp, component == 0 ? "%s" : "/%s",
-			component_name(&pathp->pathname4_val[component]));
+		    component_name(&pathp->pathname4_val[component]));
 		bp += strlen(bp);
 	}
 }
@@ -3571,10 +3568,10 @@
 detail_rpcsec_gss(rpcsec_gss_info *info)
 {
 	sprintf(get_line(0, 0), "OID = %s",
-		tohex(info->oid.sec_oid4_val, info->oid.sec_oid4_len));
+	    tohex(info->oid.sec_oid4_val, info->oid.sec_oid4_len));
 	sprintf(get_line(0, 0), "QOP = %u", info->qop);
 	sprintf(get_line(0, 0), "Service = %d (%s)",
-		info->service, gss_svc_name(info->service));
+	    info->service, gss_svc_name(info->service));
 }
 
 /*
@@ -3585,7 +3582,7 @@
 detail_secinfo4(secinfo4 *infop)
 {
 	sprintf(get_line(0, 0), "Flavor = %d (%s)",
-		infop->flavor, flavor_name(infop->flavor));
+	    infop->flavor, flavor_name(infop->flavor));
 	switch (infop->flavor) {
 	case RPCSEC_GSS:
 		detail_rpcsec_gss(&infop->secinfo4_u.flavor_info);
@@ -3608,12 +3605,12 @@
 	switch (limitp->limitby) {
 	case NFS_LIMIT_SIZE:
 		snprintf(buf, buflen, "LB=SZ(%llu)",
-			limitp->nfs_space_limit4_u.filesize);
+		    limitp->nfs_space_limit4_u.filesize);
 		break;
 	case NFS_LIMIT_BLOCKS:
 		snprintf(buf, buflen, "LB=BL(%u*%u)",
-			limitp->nfs_space_limit4_u.mod_blocks.num_blocks,
-			limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
+		    limitp->nfs_space_limit4_u.mod_blocks.num_blocks,
+		    limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
 		break;
 	default:
 		snprintf(buf, buflen, "LB=?(%d)", limitp->limitby);
@@ -3631,19 +3628,19 @@
 detail_space_limit(nfs_space_limit4 *limitp)
 {
 	sprintf(get_line(0, 0), "LimitBy = %d (%s)",
-		limitp->limitby,
-		limitby_name(limitp->limitby));
+	    limitp->limitby,
+	    limitby_name(limitp->limitby));
 
 	switch (limitp->limitby) {
 	case NFS_LIMIT_SIZE:
 		sprintf(get_line(0, 0), "Bytes = %llu",
-			limitp->nfs_space_limit4_u.filesize);
+		    limitp->nfs_space_limit4_u.filesize);
 		break;
 	case NFS_LIMIT_BLOCKS:
 		sprintf(get_line(0, 0), "Blocks = %u",
-			limitp->nfs_space_limit4_u.mod_blocks.num_blocks);
+		    limitp->nfs_space_limit4_u.mod_blocks.num_blocks);
 		sprintf(get_line(0, 0), "Bytes Per Block = %u",
-			limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
+		    limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
 		break;
 	}
 }
@@ -3927,8 +3924,8 @@
 	case NFS4ERR_RESOURCE:	p = "NFS4ERR_RESOURCE"; break;
 	case NFS4ERR_MOVED:	p = "NFS4ERR_MOVED"; break;
 	case NFS4ERR_NOFILEHANDLE: p = "NFS4ERR_NOFILEHANDLE"; break;
-	case NFS4ERR_MINOR_VERS_MISMATCH: p =
-				"NFS4ERR_MINOR_VERS_MISMATCH"; break;
+	case NFS4ERR_MINOR_VERS_MISMATCH: p = "NFS4ERR_MINOR_VERS_MISMATCH";
+	break;
 	case NFS4ERR_STALE_CLIENTID: p = "NFS4ERR_STALE_CLIENTID"; break;
 	case NFS4ERR_STALE_STATEID: p = "NFS4ERR_STALE_STATEID"; break;
 	case NFS4ERR_OLD_STATEID: p = "NFS4ERR_OLD_STATEID"; break;
@@ -4007,7 +4004,7 @@
 
 	sprintf(buf, "Filehandle expire type = ");
 	if ((val & (FH4_NOEXPIRE_WITH_OPEN | FH4_VOLATILE_ANY |
-			FH4_VOL_MIGRATION | FH4_VOL_RENAME)) == 0) {
+	    FH4_VOL_MIGRATION | FH4_VOL_RENAME)) == 0) {
 		strcat(buf, "Persistent");
 		return;
 	}
@@ -4067,7 +4064,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Link Support = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4078,7 +4075,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Symlink Support = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4089,7 +4086,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Has Named Attributes = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4100,7 +4097,7 @@
 	if (!xdr_fsid4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "FS ID: Major = %llx, Minor = %llx",
-		val.major, val.minor);
+	    val.major, val.minor);
 }
 
 static void
@@ -4111,7 +4108,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Unique Handles = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4132,7 +4129,7 @@
 	if (!xdr_nfsstat4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Rdattr Error = %u (%s)",
-		val, status_name(val));
+	    val, status_name(val));
 }
 
 static void
@@ -4253,7 +4250,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Archived = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4264,7 +4261,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Server Can Set Time = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4275,7 +4272,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Case Insensitive Lookups = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4286,7 +4283,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Case Preserving = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4297,7 +4294,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Chown Is Restricted = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4362,14 +4359,29 @@
 }
 
 static void
+prt_fs_location(fs_location4 *fsl)
+{
+	int i;
+
+	for (i = 0; i < fsl->server.server_len; i++)
+		sprintf(get_line(0, 0), "server: %s",
+		    utf8localize(&fsl->server.server_val[i]));
+
+	detail_pathname4(&fsl->rootpath, "rootpath: ");
+}
+
+static void
 prt_fs_locations(XDR *xdr)
 {
 	static fs_locations4 val;
+	int i;
 
 	if (!xdr_fs_locations4(xdr, &val))
 		longjmp(xdr_err, 1);
-	sprintf(get_line(0, 0), "[fs_locations]"); /* XXX */
-	detail_pathname4(&val.fs_root);
+	sprintf(get_line(0, 0), "[fs_locations]");
+	detail_pathname4(&val.fs_root, "fs_root: ");
+	for (i = 0; i < val.locations.locations_len; i++)
+		prt_fs_location(&val.locations.locations_val[i]);
 	xdr_free(xdr_fs_locations4, (char *)&val);
 }
 
@@ -4381,7 +4393,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Hidden = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4392,7 +4404,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "FS Is Homogeneous = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4475,7 +4487,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Long Names Are Error (no_trunc) = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4548,7 +4560,7 @@
 	if (!xdr_specdata4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Raw Device ID = %u, %u",
-		val.specdata1, val.specdata2);
+	    val.specdata1, val.specdata2);
 }
 
 static void
@@ -4599,7 +4611,7 @@
 	if (!xdr_bool(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "System File = %s",
-		val ? "TRUE" : "FALSE");
+	    val ? "TRUE" : "FALSE");
 }
 
 static void
@@ -4610,7 +4622,7 @@
 	if (!xdr_nfstime4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Last Access Time = %s",
-		format_time(val.seconds, val.nseconds));
+	    format_time(val.seconds, val.nseconds));
 }
 
 static void
@@ -4622,8 +4634,8 @@
 		longjmp(xdr_err, 1);
 	if (val.set_it == SET_TO_CLIENT_TIME4) {
 		sprintf(get_line(0, 0), "Access Time = %s (set to client time)",
-			format_time(val.settime4_u.time.seconds,
-			val.settime4_u.time.nseconds));
+		    format_time(val.settime4_u.time.seconds,
+		    val.settime4_u.time.nseconds));
 	} else if (val.set_it == SET_TO_SERVER_TIME4) {
 		sprintf(get_line(0, 0), "Access Time (set to server time)");
 	} else
@@ -4638,7 +4650,7 @@
 	if (!xdr_nfstime4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Last Backup Time = %s",
-		format_time(val.seconds, val.nseconds));
+	    format_time(val.seconds, val.nseconds));
 }
 
 static void
@@ -4649,7 +4661,7 @@
 	if (!xdr_nfstime4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Creation Time = %s",
-		format_time(val.seconds, val.nseconds));
+	    format_time(val.seconds, val.nseconds));
 }
 
 static void
@@ -4660,7 +4672,7 @@
 	if (!xdr_nfstime4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Server Time Granularity = %lld.%09d sec",
-		val.seconds, val.nseconds);
+	    val.seconds, val.nseconds);
 }
 
 static void
@@ -4671,7 +4683,7 @@
 	if (!xdr_nfstime4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Last Metadata Change Time = %s",
-		format_time(val.seconds, val.nseconds));
+	    format_time(val.seconds, val.nseconds));
 }
 
 static void
@@ -4682,7 +4694,7 @@
 	if (!xdr_nfstime4(xdr, &val))
 		longjmp(xdr_err, 1);
 	sprintf(get_line(0, 0), "Last Modification Time = %s",
-		format_time(val.seconds, val.nseconds));
+	    format_time(val.seconds, val.nseconds));
 }
 
 static void
@@ -4694,12 +4706,12 @@
 		longjmp(xdr_err, 1);
 	if (val.set_it == SET_TO_CLIENT_TIME4) {
 		sprintf(get_line(0, 0),
-			"Modification Time = %s (set to client time)",
-			format_time(val.settime4_u.time.seconds,
-			val.settime4_u.time.nseconds));
+		    "Modification Time = %s (set to client time)",
+		    format_time(val.settime4_u.time.seconds,
+		    val.settime4_u.time.nseconds));
 	} else if (val.set_it == SET_TO_SERVER_TIME4) {
 		sprintf(get_line(0, 0),
-			"Modification Time (set to server time)");
+		    "Modification Time (set to server time)");
 	} else
 		longjmp(xdr_err, 1);
 }
@@ -4760,7 +4772,7 @@
 	}
 	if (newsize != oldsize) {
 		utf_buf[cur_utf_buf] = realloc(utf_buf[cur_utf_buf],
-					    newsize);
+		    newsize);
 		if (utf_buf[cur_utf_buf] == NULL) {
 			pr_err("out of memory\n");
 			utf_buflen[cur_utf_buf] = 0;
--- a/usr/src/cmd/fs.d/autofs/Makefile	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/autofs/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -71,8 +71,8 @@
 		autod_parse.o autod_mount.o autod_nfs.o nfs_cast.o \
 		autod_autofs.o autod_xdr.o autod_readdir.o autod_lookup.o
 
-TYPEOBJS=	$(LOCAL) $(COMMON) replica.o nfs_sec.o nfs_subr.o $(FSLIB) \
-		webnfs_xdr.o webnfs_client.o selfcheck.o
+TYPEOBJS=	$(LOCAL) $(COMMON) replica.o nfs_sec.o nfs_resolve.o nfs_subr.o \
+		$(FSLIB) webnfs_xdr.o webnfs_client.o selfcheck.o
 
 SHAREOBJS=	$(SHARESRCS:%.c=%.o)
 
@@ -87,7 +87,7 @@
 $(TYPEPROG) :=	LDLIBS += -lrpcsvc -lsocket -lnsl -lsldap -lkstat
 
 CFLAGS +=	$(CCVERBOSE) -D_FILE_OFFSET_BITS=64
-CPPFLAGS=	-I. -I.. -I../nfs/lib $(CPPFLAGS.master) -D_REENTRANT \
+CPPFLAGS=	-I. -I.. -I../nfs/lib $(CPPFLAGS.master) -D_REENTRANT  \
 			$(MALLOC_DEBUG)
 OBJS=		$(AUTOOBJS) $(MOUNTOBJS) $(TYPEOBJS) \
 		$(SHAREOBJS) $(UNSHAREOBJS)
@@ -96,7 +96,8 @@
 MOUNTSRCS=	mount.c $(FSLIBSRC)
 TYPESRCS=	$(LOCAL:%.o=%.c) $(COMMON:%.o=%.c) \
 		../nfs/lib/replica.c ../nfs/lib/nfs_sec.c \
-		../nfs/lib/nfs_subr.c $(FSLIBSRC) ../nfs/lib/selfcheck.c
+		../nfs/lib/nfs_subr.c $(FSLIBSRC) ../nfs/lib/selfcheck.c \
+		../nfs/lib/nfs_resolve.c
 SHARESRCS=	$(SHARE:%=%.c)
 UNSHARESRCS=	$(UNSHARE:%=%.c)
 DFSHARESSRCS=	$(DFSHARES:%=%.sh)
@@ -168,6 +169,8 @@
 selfcheck.o:	../nfs/lib/selfcheck.c
 		$(COMPILE.c) ../nfs/lib/selfcheck.c
 
+nfs_resolve.o:	../nfs/lib/nfs_resolve.c
+		$(COMPILE.c) ../nfs/lib/nfs_resolve.c
 
 webnfs_xdr.c:	webnfs.x
 	$(RPCGEN) -M -C -c -o $@ webnfs.x
--- a/usr/src/cmd/fs.d/autofs/autod_nfs.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/autofs/autod_nfs.c	Wed Dec 09 17:27:22 2009 -0600
@@ -74,9 +74,9 @@
 #include "replica.h"
 #include "nfs_subr.h"
 #include "webnfs.h"
+#include "nfs_resolve.h"
 #include <sys/sockio.h>
 #include <net/if.h>
-#include <assert.h>
 #include <rpcsvc/daemon_utils.h>
 #include <pwd.h>
 #include <strings.h>
@@ -91,11 +91,6 @@
 
 #define	MAXHOSTS	512
 
-/* number of transports to try */
-#define	MNT_PREF_LISTLEN	2
-#define	FIRST_TRY		1
-#define	SECOND_TRY		2
-
 #define	MNTTYPE_CACHEFS "cachefs"
 
 /*
@@ -134,9 +129,7 @@
 	action_list *);
 static int is_nfs_port(char *);
 
-void netbuf_free(struct netbuf *);
-struct knetconfig *get_knconf(struct netconfig *);
-void free_knconf(struct knetconfig *);
+static void netbuf_free(struct netbuf *);
 static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int);
 static struct mapfs *enum_servers(struct mapent *, char *);
 static struct mapfs *get_mysubnet_servers(struct mapfs *);
@@ -156,12 +149,18 @@
 	SERVER_FH = 2
 };
 
-void *get_server_stuff(enum type_of_stuff, char *, rpcprog_t,
+static void *get_server_netinfo(enum type_of_stuff, char *, rpcprog_t,
 	rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t,
 	struct t_info *, caddr_t *, bool_t, char *, enum clnt_stat *);
-
-void *get_the_stuff(enum type_of_stuff, char *, rpcprog_t,
-	rpcvers_t, mfs_snego_t *, struct netconfig *, ushort_t, struct t_info *,
+static void *get_netconfig_info(enum type_of_stuff, char *, rpcprog_t,
+	rpcvers_t, struct netconfig *, ushort_t, struct t_info *,
+	struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *,
+	mfs_snego_t *);
+static void *get_server_addrorping(char *, rpcprog_t, rpcvers_t,
+	struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
+	caddr_t *, bool_t, char *, enum clnt_stat *, int);
+static void *get_server_fh(char *, rpcprog_t, rpcvers_t, mfs_snego_t *,
+	struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
 	caddr_t *, bool_t, char *, enum clnt_stat *);
 
 struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **);
@@ -1877,13 +1876,14 @@
 
 			/*
 			 * For NFSv4, we want to avoid rpcbind, so call
-			 * get_server_stuff() directly to tell it that
+			 * get_server_netinfo() directly to tell it that
 			 * we want to go "direct_to_server".  Otherwise,
 			 * do what has always been done.
 			 */
 			if (nfsvers == NFS_V4) {
 				enum clnt_stat cstat;
-				argp->addr = get_server_stuff(SERVER_ADDR,
+
+				argp->addr = get_server_netinfo(SERVER_ADDR,
 				    host, NFS_PROGRAM, nfsvers, NULL,
 				    &nconf, nfs_proto, thisport, NULL,
 				    NULL, TRUE, NULL, &cstat);
@@ -2010,7 +2010,7 @@
 		 *
 		 * Eventurally, we want to move this code to nfs_clnt_secdata()
 		 * when autod_nfs.c and mount.c can share the same
-		 * get_the_addr/get_the_stuff routine.
+		 * get_the_addr/get_netconfig_info routine.
 		 */
 		secflags = 0;
 		syncaddr = NULL;
@@ -2024,9 +2024,10 @@
 		 */
 			if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
 			    nfsvers != NFS_V4) {
-			syncaddr = get_the_stuff(SERVER_ADDR, host, RPCBPROG,
-			    RPCBVERS, NULL, nconf, 0, NULL, NULL, FALSE,
-			    NULL, NULL);
+				enum clnt_stat cstat;
+				syncaddr = get_server_netinfo(SERVER_ADDR,
+				    host, RPCBPROG, RPCBVERS, NULL, &nconf,
+				    NULL, 0, NULL, NULL, FALSE, NULL, &cstat);
 			}
 
 			if (syncaddr != NULL) {
@@ -2527,50 +2528,6 @@
 	return (RET_OK);
 }
 
-struct knetconfig *
-get_knconf(nconf)
-	struct netconfig *nconf;
-{
-	struct stat stbuf;
-	struct knetconfig *k;
-
-	if (stat(nconf->nc_device, &stbuf) < 0) {
-		syslog(LOG_ERR, "get_knconf: stat %s: %m", nconf->nc_device);
-		return (NULL);
-	}
-	k = (struct knetconfig *)malloc(sizeof (*k));
-	if (k == NULL)
-		goto nomem;
-	k->knc_semantics = nconf->nc_semantics;
-	k->knc_protofmly = strdup(nconf->nc_protofmly);
-	if (k->knc_protofmly == NULL)
-		goto nomem;
-	k->knc_proto = strdup(nconf->nc_proto);
-	if (k->knc_proto == NULL)
-		goto nomem;
-	k->knc_rdev = stbuf.st_rdev;
-
-	return (k);
-
-nomem:
-	syslog(LOG_ERR, "get_knconf: no memory");
-	free_knconf(k);
-	return (NULL);
-}
-
-void
-free_knconf(k)
-	struct knetconfig *k;
-{
-	if (k == NULL)
-		return;
-	if (k->knc_protofmly)
-		free(k->knc_protofmly);
-	if (k->knc_proto)
-		free(k->knc_proto);
-	free(k);
-}
-
 void
 netbuf_free(nb)
 	struct netbuf *nb;
@@ -2833,393 +2790,6 @@
 }
 
 /*
- * Get the network address on "hostname" for program "prog"
- * with version "vers" by using the nconf configuration data
- * passed in.
- *
- * If the address of a netconfig pointer is null then
- * information is not sufficient and no netbuf will be returned.
- *
- * tinfo argument is for matching the get_the_addr() defined in
- * ../nfs/mount/mount.c
- */
-void *
-get_the_stuff(
-	enum type_of_stuff type_of_stuff,
-	char *hostname,
-	rpcprog_t prog,
-	rpcprog_t vers,
-	mfs_snego_t *mfssnego,
-	struct netconfig *nconf,
-	ushort_t port,
-	struct t_info *tinfo,
-	caddr_t *fhp,
-	bool_t direct_to_server,
-	char *fspath,
-	enum clnt_stat *cstat)
-
-{
-	struct netbuf *nb = NULL;
-	struct t_bind *tbind = NULL;
-	int fd = -1;
-	enum clnt_stat cs = RPC_TIMEDOUT;
-	CLIENT *cl = NULL;
-	struct timeval tv;
-	AUTH *ah = NULL;
-	AUTH *new_ah = NULL;
-	struct snego_t snego;
-
-	if (nconf == NULL) {
-		goto done;
-	}
-
-	if (prog == NFS_PROGRAM && vers == NFS_V4)
-		if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
-			goto done;
-
-	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) {
-		goto done;
-	}
-
-	/* LINTED pointer alignment */
-	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
-		    == NULL) {
-			goto done;
-	}
-
-	if (direct_to_server == TRUE) {
-		struct nd_hostserv hs;
-		struct nd_addrlist *retaddrs;
-		hs.h_host = hostname;
-
-		if (trace > 1)
-			trace_prt(1, "	get_the_stuff: %s call "
-				"direct to server %s\n",
-				type_of_stuff == SERVER_FH ? "pub fh" :
-				type_of_stuff == SERVER_ADDR ? "get address" :
-				type_of_stuff == SERVER_PING ? "ping" :
-				"unknown", hostname);
-		if (port == 0)
-			hs.h_serv = "nfs";
-		else
-			hs.h_serv = NULL;
-
-		if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK) {
-			goto done;
-		}
-		memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
-			retaddrs->n_addrs->len);
-		tbind->addr.len = retaddrs->n_addrs->len;
-		netdir_free((void *)retaddrs, ND_ADDRLIST);
-		if (port) {
-			/* LINTED pointer alignment */
-
-			if (strcmp(nconf->nc_protofmly, NC_INET) == NULL)
-				((struct sockaddr_in *)
-				tbind->addr.buf)->sin_port =
-					htons((ushort_t)port);
-			else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL)
-				((struct sockaddr_in6 *)
-				tbind->addr.buf)->sin6_port =
-					htons((ushort_t)port);
-		}
-
-		if (type_of_stuff == SERVER_FH) {
-			if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd,
-				NULL) == -1)
-				if (trace > 1)
-					trace_prt(1, "\tget_the_stuff: "
-						"ND_SET_RESERVEDPORT(%s) "
-						"failed\n", hostname);
-		}
-
-		cl = clnt_tli_create(fd, nconf, &tbind->addr, prog,
-			vers, 0, 0);
-
-		if (trace > 1)
-			trace_prt(1, "	get_the_stuff: clnt_tli_create(%s) "
-				"returned %p\n", hostname, cl);
-		if (cl == NULL)
-			goto done;
-#ifdef MALLOC_DEBUG
-		add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
-		add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
-			__FILE__, __LINE__);
-#endif
-
-		switch (type_of_stuff) {
-		case SERVER_FH:
-		    {
-		    enum snego_stat sec;
-
-		    ah = authsys_create_default();
-		    if (ah != NULL) {
-#ifdef MALLOC_DEBUG
-			drop_alloc("AUTH_HANDLE", cl->cl_auth,
-				__FILE__, __LINE__);
-#endif
-			AUTH_DESTROY(cl->cl_auth);
-			cl->cl_auth = ah;
-#ifdef MALLOC_DEBUG
-			add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
-				__FILE__, __LINE__);
-#endif
-		    }
-
-		    if (!mfssnego->snego_done && vers != NFS_V4) {
-			/*
-			 * negotiate sec flavor.
-			 */
-			snego.cnt = 0;
-			if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
-				SNEGO_SUCCESS) {
-			    int jj;
-
-			/*
-			 * check if server supports the one
-			 * specified in the sec= option.
-			 */
-			    if (mfssnego->sec_opt) {
-				for (jj = 0; jj < snego.cnt; jj++) {
-				    if (snego.array[jj] ==
-					mfssnego->nfs_sec.sc_nfsnum) {
-					mfssnego->snego_done = TRUE;
-					break;
-				    }
-				}
-			    }
-
-			/*
-			 * find a common sec flavor
-			 */
-			    if (!mfssnego->snego_done) {
-				for (jj = 0; jj < snego.cnt; jj++) {
-				    if (!nfs_getseconfig_bynumber(
-					snego.array[jj], &mfssnego->nfs_sec)) {
-					mfssnego->snego_done = TRUE;
-					break;
-				    }
-				}
-			    }
-			    if (!mfssnego->snego_done)
-				return (NULL);
-
-			/*
-			 * Now that the flavor has been
-			 * negotiated, get the fh.
-			 *
-			 * First, create an auth handle using the negotiated
-			 * sec flavor in the next lookup to
-			 * fetch the filehandle.
-			 */
-			    new_ah = nfs_create_ah(cl, hostname,
-					&mfssnego->nfs_sec);
-			    if (new_ah == NULL)
-				goto done;
-#ifdef MALLOC_DEBUG
-			    drop_alloc("AUTH_HANDLE", cl->cl_auth,
-				__FILE__, __LINE__);
-#endif
-			    AUTH_DESTROY(cl->cl_auth);
-			    cl->cl_auth = new_ah;
-#ifdef MALLOC_DEBUG
-			    add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
-				__FILE__, __LINE__);
-#endif
-			} else if (sec == SNEGO_ARRAY_TOO_SMALL ||
-			    sec == SNEGO_FAILURE) {
-			    goto done;
-			}
-			/*
-			 * Note that if sec == SNEGO_DEF_VALID
-			 * the default sec flavor is acceptable.
-			 * Use it to get the filehandle.
-			 */
-		    }
-		    }
-
-		    switch (vers) {
-		    case NFS_VERSION:
-			    {
-			    wnl_diropargs arg;
-			    wnl_diropres res;
-
-			    memset((char *)&arg.dir, 0, sizeof (wnl_fh));
-			    memset((char *)&res, 0, sizeof (wnl_diropres));
-			    arg.name = fspath;
-			    if (wnlproc_lookup_2(&arg, &res, cl) !=
-				RPC_SUCCESS || res.status != NFS_OK)
-				    goto done;
-			    *fhp = malloc(sizeof (wnl_fh));
-
-			    if (*fhp == NULL) {
-				    syslog(LOG_ERR, "no memory\n");
-				    goto done;
-			    }
-
-			    memcpy((char *)*fhp,
-			    (char *)&res.wnl_diropres_u.wnl_diropres.file,
-				sizeof (wnl_fh));
-			    cs = RPC_SUCCESS;
-			    }
-			    break;
-		    case NFS_V3:
-			    {
-			    WNL_LOOKUP3args arg;
-			    WNL_LOOKUP3res res;
-			    nfs_fh3 *fh3p;
-
-			    memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
-			    memset((char *)&res, 0, sizeof (WNL_LOOKUP3res));
-			    arg.what.name = fspath;
-			    if (wnlproc3_lookup_3(&arg, &res, cl) !=
-				RPC_SUCCESS || res.status != NFS3_OK)
-				    goto done;
-
-			    fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
-
-			    if (fh3p == NULL) {
-				    syslog(LOG_ERR, "no memory\n");
-				    goto done;
-			    }
-
-			    fh3p->fh3_length = res.
-				WNL_LOOKUP3res_u.res_ok.object.data.data_len;
-			    memcpy(fh3p->fh3_u.data, res.
-				WNL_LOOKUP3res_u.res_ok.object.data.data_val,
-				fh3p->fh3_length);
-
-			    *fhp = (caddr_t)fh3p;
-
-			    cs = RPC_SUCCESS;
-			    }
-			    break;
-		    case NFS_V4:
-			    tv.tv_sec = 10;
-			    tv.tv_usec = 0;
-			    cs = clnt_call(cl, NULLPROC, xdr_void, 0,
-				xdr_void, 0, tv);
-			    if (cs != RPC_SUCCESS)
-				    goto done;
-			    *fhp = strdup(fspath);
-			    break;
-		    }
-		    break;
-		case SERVER_ADDR:
-		case SERVER_PING:
-			tv.tv_sec = 10;
-			tv.tv_usec = 0;
-			cs = clnt_call(cl, NULLPROC, xdr_void, 0,
-				xdr_void, 0, tv);
-			if (trace > 1)
-				trace_prt(1,
-					"get_the_stuff: clnt_call(%s) "
-					"returned %s\n",
-				hostname,
-					cs == RPC_SUCCESS ? "success" :
-					"failure");
-
-			if (cs != RPC_SUCCESS)
-				goto done;
-			break;
-		}
-
-	} else if (type_of_stuff != SERVER_FH) {
-
-		if (type_of_stuff == SERVER_ADDR) {
-			if (get_cached_srv_addr(hostname, prog, vers, nconf,
-			    &tbind->addr) == 0)
-				goto done;
-		}
-
-		if (port) {
-			/* LINTED pointer alignment */
-			if (strcmp(nconf->nc_protofmly, NC_INET) == NULL)
-				((struct sockaddr_in *)
-				tbind->addr.buf)->sin_port =
-					htons((ushort_t)port);
-			else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL)
-				((struct sockaddr_in6 *)
-				tbind->addr.buf)->sin6_port =
-					htons((ushort_t)port);
-			cl = clnt_tli_create(fd, nconf, &tbind->addr,
-				prog, vers, 0, 0);
-			if (cl == NULL)
-				goto done;
-#ifdef MALLOC_DEBUG
-			add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
-			add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
-				__FILE__, __LINE__);
-#endif
-			tv.tv_sec = 10;
-			tv.tv_usec = 0;
-			cs = clnt_call(cl, NULLPROC, xdr_void, 0, xdr_void,
-				0, tv);
-			if (cs != RPC_SUCCESS)
-				goto done;
-		}
-
-	} else {
-		/* can't happen */
-		goto done;
-	}
-
-	if (type_of_stuff != SERVER_PING) {
-
-		cs = RPC_SYSTEMERROR;
-
-		/*
-		 * Make a copy of the netbuf to return
-		 */
-		nb = (struct netbuf *)malloc(sizeof (struct netbuf));
-		if (nb == NULL) {
-			syslog(LOG_ERR, "no memory\n");
-			goto done;
-		}
-		*nb = tbind->addr;
-		nb->buf = (char *)malloc(nb->maxlen);
-		if (nb->buf == NULL) {
-			syslog(LOG_ERR, "no memory\n");
-			free(nb);
-			nb = NULL;
-			goto done;
-		}
-		(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
-
-		cs = RPC_SUCCESS;
-	}
-
-done:
-	if (cl != NULL) {
-		if (ah != NULL) {
-#ifdef MALLOC_DEBUG
-			drop_alloc("AUTH_HANDLE", cl->cl_auth,
-				__FILE__, __LINE__);
-#endif
-			AUTH_DESTROY(cl->cl_auth);
-			cl->cl_auth = NULL;
-		}
-#ifdef MALLOC_DEBUG
-		drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
-#endif
-		clnt_destroy(cl);
-	}
-
-	if (tbind) {
-		t_free((char *)tbind, T_BIND);
-		tbind = NULL;
-	}
-
-	if (fd >= 0)
-		(void) t_close(fd);
-
-	if (cstat != NULL)
-		*cstat = cs;
-
-	return (nb);
-}
-
-/*
  * Get a network address on "hostname" for program "prog"
  * with version "vers".  If the port number is specified (non zero)
  * then try for a TCP/UDP transport and set the port number of the
@@ -3242,7 +2812,7 @@
 {
 	enum clnt_stat cstat;
 
-	return (get_server_stuff(SERVER_ADDR, hostname, prog, vers, NULL,
+	return (get_server_netinfo(SERVER_ADDR, hostname, prog, vers, NULL,
 		nconfp, proto, port, tinfo, NULL, FALSE, NULL, &cstat));
 }
 
@@ -3253,7 +2823,7 @@
 {
 	enum clnt_stat cstat;
 
-	return (get_server_stuff(SERVER_FH, hostname, NFS_PROGRAM, vers,
+	return (get_server_netinfo(SERVER_FH, hostname, NFS_PROGRAM, vers,
 	    mfssnego, nconfp, proto, port, tinfo, fhp, get_pubfh, fspath,
 	    &cstat));
 }
@@ -3264,14 +2834,15 @@
 {
 	enum clnt_stat cstat;
 
-	(void) get_server_stuff(SERVER_PING, hostname, prog, vers, NULL, nconfp,
-	    NULL, port, NULL, NULL, direct_to_server, NULL, &cstat);
+	(void) get_server_netinfo(SERVER_PING, hostname, prog,
+	    vers, NULL, nconfp, NULL, port, NULL, NULL,
+	    direct_to_server, NULL, &cstat);
 
 	return (cstat);
 }
 
 void *
-get_server_stuff(
+get_server_netinfo(
 	enum type_of_stuff type_of_stuff,
 	char *hostname,
 	rpcprog_t prog,
@@ -3289,13 +2860,16 @@
 	struct netbuf *nb = NULL;
 	struct netconfig *nconf = NULL;
 	NCONF_HANDLE *nc = NULL;
+	int error = 0;
+	int fd = 0;
+	struct t_bind *tbind = NULL;
 	int nthtry = FIRST_TRY;
 
-	if (nconfp && *nconfp)
-		return (get_the_stuff(type_of_stuff, hostname, prog, vers,
-		    mfssnego, *nconfp, port, tinfo, fhp, direct_to_server,
-		    fspath, cstatp));
-
+	if (nconfp && *nconfp) {
+		return (get_netconfig_info(type_of_stuff, hostname,
+		    prog, vers, nconf, port, tinfo, tbind, fhp,
+		    direct_to_server, fspath, cstatp, mfssnego));
+	}
 
 	/*
 	 * No nconf passed in.
@@ -3314,8 +2888,7 @@
 	 * otherwise try COTS first, if failed, then try CLTS.
 	 */
 	if (proto) {
-
-		while (nconf = getnetpath(nc)) {
+		while ((nconf = getnetpath(nc)) != NULL) {
 			if (strcmp(nconf->nc_proto, proto))
 				continue;
 			/*
@@ -3329,24 +2902,20 @@
 				    strcmp(nconf->nc_proto, NC_UDP)))
 					continue;
 			}
-
-			nb = get_the_stuff(type_of_stuff, hostname, prog, vers,
-			    mfssnego, nconf, port, tinfo, fhp,
-			    direct_to_server, fspath, cstatp);
-
+			nb = get_netconfig_info(type_of_stuff, hostname,
+			    prog, vers, nconf, port, tinfo, tbind, fhp,
+			    direct_to_server, fspath, cstatp, mfssnego);
 			if (*cstatp == RPC_SUCCESS)
 				break;
 
 			assert(nb == NULL);
 
-		} /* end of while */
-
+		}
 		if (nconf == NULL)
 			goto done;
-
 	} else {
 retry:
-		while (nconf = getnetpath(nc)) {
+		while ((nconf = getnetpath(nc)) != NULL) {
 			if (nconf->nc_flag & NC_VISIBLE) {
 				if (nthtry == FIRST_TRY) {
 					if ((nconf->nc_semantics ==
@@ -3379,7 +2948,8 @@
 					}
 				}
 			}
-		} /* while */
+		}
+
 		if (nconf == NULL) {
 			if (++nthtry <= MNT_PREF_LISTLEN) {
 				endnetpath(nc);
@@ -3389,9 +2959,9 @@
 			} else
 				goto done;
 		} else {
-			nb = get_the_stuff(type_of_stuff, hostname, prog, vers,
-			    mfssnego, nconf, port, tinfo, fhp, direct_to_server,
-			    fspath, cstatp);
+			nb = get_netconfig_info(type_of_stuff, hostname,
+			    prog, vers, nconf, port, tinfo, tbind, fhp,
+			    direct_to_server, fspath, cstatp, mfssnego);
 			if (*cstatp != RPC_SUCCESS)
 				/*
 				 * Continue the same search path in the
@@ -3400,17 +2970,20 @@
 				 */
 				goto retry;
 		}
-	} /* if !proto */
+	}
 
 	/*
 	 * Got nconf and nb.  Now dup the netconfig structure (nconf)
 	 * and return it thru nconfp.
 	 */
-	*nconfp = getnetconfigent(nconf->nc_netid);
-	if (*nconfp == NULL) {
-		syslog(LOG_ERR, "no memory\n");
-		free(nb);
-		nb = NULL;
+	if (nconf != NULL) {
+		if ((*nconfp = getnetconfigent(nconf->nc_netid)) == NULL) {
+			syslog(LOG_ERR, "no memory\n");
+			free(nb);
+			nb = NULL;
+		}
+	} else {
+		*nconfp = NULL;
 	}
 done:
 	if (nc)
@@ -3418,6 +2991,221 @@
 	return (nb);
 }
 
+void *
+get_server_fh(char *hostname, rpcprog_t	prog, rpcvers_t	vers,
+	mfs_snego_t *mfssnego, struct netconfig *nconf, ushort_t port,
+	struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp,
+	bool_t direct_to_server, char *fspath, enum clnt_stat *cstat)
+{
+	AUTH *ah = NULL;
+	AUTH *new_ah = NULL;
+	struct snego_t	snego;
+	enum clnt_stat cs = RPC_TIMEDOUT;
+	struct timeval tv;
+	bool_t file_handle = 1;
+	enum snego_stat sec;
+	CLIENT *cl = NULL;
+	int fd = -1;
+	struct netbuf *nb = NULL;
+
+	if (direct_to_server != TRUE)
+		return (NULL);
+
+	if (prog == NFS_PROGRAM && vers == NFS_V4)
+		if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
+			goto done;
+
+	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0)
+		goto done;
+
+	/* LINTED pointer alignment */
+	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
+		goto done;
+
+	if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd,
+	    direct_to_server, port, prog, vers, file_handle) < 0) {
+		goto done;
+	}
+
+	cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
+	if (cl == NULL)
+		goto done;
+
+	ah = authsys_create_default();
+	if (ah != NULL) {
+#ifdef MALLOC_DEBUG
+		drop_alloc("AUTH_HANDLE", cl->cl_auth,
+		    __FILE__, __LINE__);
+#endif
+		AUTH_DESTROY(cl->cl_auth);
+		cl->cl_auth = ah;
+#ifdef MALLOC_DEBUG
+		add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
+		    __FILE__, __LINE__);
+#endif
+	}
+
+	if (!mfssnego->snego_done && vers != NFS_V4) {
+		/*
+		 * negotiate sec flavor.
+		 */
+		snego.cnt = 0;
+		if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
+		    SNEGO_SUCCESS) {
+			int jj;
+
+			/*
+			 * check if server supports the one
+			 * specified in the sec= option.
+			 */
+			if (mfssnego->sec_opt) {
+				for (jj = 0; jj < snego.cnt; jj++) {
+					if (snego.array[jj] ==
+					    mfssnego->nfs_sec.sc_nfsnum) {
+						mfssnego->snego_done = TRUE;
+						break;
+					}
+				}
+			}
+
+			/*
+			 * find a common sec flavor
+			 */
+			if (!mfssnego->snego_done) {
+				for (jj = 0; jj < snego.cnt; jj++) {
+					if (!nfs_getseconfig_bynumber(
+					    snego.array[jj],
+					    &mfssnego->nfs_sec)) {
+						mfssnego->snego_done = TRUE;
+						break;
+					}
+				}
+			}
+			if (!mfssnego->snego_done)
+				goto done;
+			/*
+			 * Now that the flavor has been
+			 * negotiated, get the fh.
+			 *
+			 * First, create an auth handle using the negotiated
+			 * sec flavor in the next lookup to
+			 * fetch the filehandle.
+			 */
+			new_ah = nfs_create_ah(cl, hostname,
+			    &mfssnego->nfs_sec);
+			if (new_ah  == NULL)
+				goto done;
+#ifdef MALLOC_DEBUG
+			drop_alloc("AUTH_HANDLE", cl->cl_auth,
+			    __FILE__, __LINE__);
+#endif
+			AUTH_DESTROY(cl->cl_auth);
+			cl->cl_auth = new_ah;
+#ifdef MALLOC_DEBUG
+			add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
+			    __FILE__, __LINE__);
+#endif
+		} else if (sec == SNEGO_ARRAY_TOO_SMALL ||
+		    sec == SNEGO_FAILURE) {
+			goto done;
+		}
+	}
+
+	switch (vers) {
+	case NFS_VERSION:
+		{
+		wnl_diropargs arg;
+		wnl_diropres res;
+
+		memset((char *)&arg.dir, 0, sizeof (wnl_fh));
+		memset((char *)&res, 0, sizeof (wnl_diropres));
+		arg.name = fspath;
+		if (wnlproc_lookup_2(&arg, &res, cl) !=
+		    RPC_SUCCESS || res.status != NFS_OK)
+			goto done;
+		*fhp = malloc(sizeof (wnl_fh));
+
+		if (*fhp == NULL) {
+			syslog(LOG_ERR, "no memory\n");
+			goto done;
+		}
+
+		memcpy((char *)*fhp,
+		    (char *)&res.wnl_diropres_u.wnl_diropres.file,
+		    sizeof (wnl_fh));
+		cs = RPC_SUCCESS;
+		}
+		break;
+	case NFS_V3:
+		{
+		WNL_LOOKUP3args arg;
+		WNL_LOOKUP3res res;
+		nfs_fh3 *fh3p;
+
+		memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
+		memset((char *)&res, 0, sizeof (WNL_LOOKUP3res));
+		arg.what.name = fspath;
+		if (wnlproc3_lookup_3(&arg, &res, cl) !=
+		    RPC_SUCCESS || res.status != NFS3_OK)
+			goto done;
+
+		fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
+
+		if (fh3p == NULL) {
+			syslog(LOG_ERR, "no memory\n");
+			goto done;
+		}
+
+		fh3p->fh3_length =
+		    res.WNL_LOOKUP3res_u.res_ok.object.data.data_len;
+		memcpy(fh3p->fh3_u.data,
+		    res.WNL_LOOKUP3res_u.res_ok.object.data.data_val,
+		    fh3p->fh3_length);
+
+		*fhp = (caddr_t)fh3p;
+
+		cs = RPC_SUCCESS;
+		}
+		break;
+	case NFS_V4:
+		tv.tv_sec = 10;
+		tv.tv_usec = 0;
+		cs = clnt_call(cl, NULLPROC, xdr_void, 0,
+		    xdr_void, 0, tv);
+		if (cs != RPC_SUCCESS)
+			goto done;
+
+		*fhp = strdup(fspath);
+		if (fhp == NULL) {
+			cs = RPC_SYSTEMERROR;
+			goto done;
+		}
+		break;
+	}
+	nb = (struct netbuf *)malloc(sizeof (struct netbuf));
+	if (nb == NULL) {
+		syslog(LOG_ERR, "no memory\n");
+		cs = RPC_SYSTEMERROR;
+		goto done;
+	}
+	nb->buf = (char *)malloc(tbind->addr.maxlen);
+	if (nb->buf == NULL) {
+		syslog(LOG_ERR, "no memory\n");
+		free(nb);
+		nb = NULL;
+		cs = RPC_SYSTEMERROR;
+		goto done;
+	}
+	(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
+	nb->len = tbind->addr.len;
+	nb->maxlen = tbind->addr.maxlen;
+done:
+	if (cstat != NULL)
+		*cstat = cs;
+	destroy_auth_client_handle(cl);
+	cleanup_tli_parms(tbind, fd);
+	return (nb);
+}
 
 /*
  * Sends a null call to the remote host's (NFS program, versp). versp
@@ -4535,3 +4323,121 @@
 		free(oldp);
 	}
 }
+
+void *
+get_netconfig_info(enum type_of_stuff type_of_stuff, char *hostname,
+	rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
+	ushort_t port, struct t_info *tinfo, struct t_bind *tbind,
+	caddr_t *fhp, bool_t direct_to_server, char *fspath,
+	enum clnt_stat *cstat, mfs_snego_t *mfssnego)
+{
+	struct netconfig *nb = NULL;
+	int ping_server = 0;
+
+
+	if (nconf == NULL)
+		return (NULL);
+
+	switch (type_of_stuff) {
+	case SERVER_FH:
+		nb = get_server_fh(hostname, prog, vers, mfssnego,
+		    nconf, port, tinfo, tbind, fhp, direct_to_server,
+		    fspath, cstat);
+		break;
+	case SERVER_PING:
+		ping_server = 1;
+	case SERVER_ADDR:
+		nb = get_server_addrorping(hostname, prog, vers,
+		    nconf, port, tinfo, tbind, fhp, direct_to_server,
+		    fspath, cstat, ping_server);
+		break;
+	default:
+		assert(nb != NULL);
+	}
+	return (nb);
+}
+
+/*
+ * Get the server address or can we ping it or not.
+ * Check the portmap cache first for server address.
+ * If no entries there, ping the server with a NULLPROC rpc.
+ */
+void *
+get_server_addrorping(char *hostname, rpcprog_t prog, rpcvers_t vers,
+	struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
+	struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server,
+	char *fspath, enum clnt_stat *cstat, int ping_server)
+{
+	struct timeval tv;
+	enum clnt_stat cs = RPC_TIMEDOUT;
+	struct netbuf *nb = NULL;
+	CLIENT *cl = NULL;
+	int fd = -1;
+
+	if (prog == NFS_PROGRAM && vers == NFS_V4)
+		if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
+			goto done;
+
+	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) {
+		goto done;
+	}
+
+	/* LINTED pointer alignment */
+	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
+	    == NULL) {
+		goto done;
+	}
+
+	if (direct_to_server != TRUE) {
+		if (!ping_server) {
+			if (get_cached_srv_addr(hostname, prog, vers,
+			    nconf, &tbind->addr) == 0)
+				goto done;
+		} else {
+			if (port == 0)
+				goto done;
+		}
+	}
+	if (setup_nb_parms(nconf, tbind, tinfo, hostname,
+	    fd, direct_to_server, port, prog, vers, 0) < 0)
+		goto done;
+
+	if (port || (direct_to_server == TRUE)) {
+		tv.tv_sec = 10;
+		tv.tv_usec = 0;
+		cl = clnt_tli_create(fd, nconf, &tbind->addr,
+		    prog, vers, 0, 0);
+		if (cl == NULL)
+			goto done;
+
+		cs = clnt_call(cl, NULLPROC, xdr_void, 0,
+		    xdr_void, 0, tv);
+		if (cs != RPC_SUCCESS) {
+			syslog(LOG_ERR, "error is %d", cs);
+			goto done;
+		}
+	}
+	if (!ping_server) {
+		nb = (struct netbuf *)malloc(sizeof (struct netbuf));
+		if (nb == NULL) {
+			syslog(LOG_ERR, "no memory\n");
+			goto done;
+		}
+		nb->buf = (char *)malloc(tbind->addr.maxlen);
+		if (nb->buf == NULL) {
+			syslog(LOG_ERR, "no memory\n");
+			free(nb);
+			nb = NULL;
+			goto done;
+		}
+		(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
+		nb->len = tbind->addr.len;
+		nb->maxlen = tbind->addr.maxlen;
+		cs = RPC_SUCCESS;
+	}
+done:
+	destroy_auth_client_handle(cl);
+	cleanup_tli_parms(tbind, fd);
+	*cstat = cs;
+	return (nb);
+}
--- a/usr/src/cmd/fs.d/autofs/autod_xdr.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/autofs/autod_xdr.c	Wed Dec 09 17:27:22 2009 -0600
@@ -21,12 +21,10 @@
 /*
  * autod_xdr.c
  *
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * This file can not be automatically generated by rpcgen from
  * autofs_prot.x because of the xdr routines that provide readdir
@@ -37,6 +35,8 @@
 #include <sys/sysmacros.h>		/* includes roundup() */
 #include <string.h>
 #include <rpcsvc/autofs_prot.h>
+#include <nfs/nfs4.h>
+#include <rpcsvc/nfs4_prot.h>
 #include <rpc/xdr.h>
 #include <stdlib.h>
 #include <strings.h>
--- a/usr/src/cmd/fs.d/nfs/Makefile	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/nfs/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -19,9 +19,8 @@
 # CDDL HEADER END
 #
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # cmd/fs.d/nfs/Makefile
@@ -36,7 +35,8 @@
 		statd nfsstat mountd dfshares \
 		nfsfind nfs4cbd share
 SUBDIR2=	clear_locks umount showmount \
-		mount dfmounts nfslog nfsmapid
+		mount dfmounts nfslog nfsmapid \
+		nfsref rp_basic
 
 SUBDIR3=	etc svc
 SUBDIRS=	$(SUBDIR1) $(SUBDIR2) $(SUBDIR3)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.c	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,386 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Helper routines for  nfsmapid and autod daemon
+ * to translate hostname to IP address and Netinfo.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <signal.h>
+#include <libintl.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <string.h>
+#include <memory.h>
+#include <pwd.h>
+#include <grp.h>
+#include <door.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <deflt.h>
+#include <netdir.h>
+#include <nfs/nfs4.h>
+#include <nfs/nfssys.h>
+#include <nfs/nfsid_map.h>
+#include <nfs/mapid.h>
+#include <nfs/nfs_sec.h>
+#include <sys/sdt.h>
+#include <sys/idmap.h>
+#include <idmap.h>
+#include <sys/fs/autofs.h>
+#include "nfs_resolve.h"
+
+void
+free_knconf(struct knetconfig *k)
+{
+	if (k == NULL)
+		return;
+	if (k->knc_protofmly)
+		free(k->knc_protofmly);
+	if (k->knc_proto)
+		free(k->knc_proto);
+	free(k);
+}
+
+struct knetconfig *
+get_knconf(struct netconfig *nconf)
+{
+	struct stat stbuf;
+	struct knetconfig *k = NULL;
+	int len;
+
+	if (stat(nconf->nc_device, &stbuf) < 0) {
+		syslog(LOG_ERR, "get_knconf: stat %s: %m", nconf->nc_device);
+		return (NULL);
+	}
+	k = (struct knetconfig *)malloc(sizeof (*k));
+	if (k == NULL)
+		goto nomem;
+	k->knc_semantics = nconf->nc_semantics;
+
+	len = strlen(nconf->nc_protofmly);
+	if (len <= 0)
+		goto err_out;
+	k->knc_protofmly = malloc(KNC_STRSIZE);
+	if (k->knc_protofmly == NULL)
+		goto nomem;
+	bzero(k->knc_protofmly, KNC_STRSIZE);
+	bcopy(nconf->nc_protofmly, k->knc_protofmly, len);
+
+	len = strlen(nconf->nc_proto);
+	if (len <= 0)
+		goto err_out;
+	k->knc_proto = malloc(KNC_STRSIZE);
+	if (k->knc_proto == NULL)
+		goto nomem;
+	bzero(k->knc_proto, KNC_STRSIZE);
+	bcopy(nconf->nc_proto, k->knc_proto, len);
+
+	k->knc_rdev = stbuf.st_rdev;
+
+	return (k);
+
+nomem:
+	syslog(LOG_ERR, "get_knconf: no memory");
+err_out:
+	if (k != NULL)
+		(void) free_knconf(k);
+	return (NULL);
+}
+
+/*
+ * Get the information needed for an NFSv4.x referral. This
+ * information includes the netbuf, netname and knconfig.
+ */
+struct nfs_fsl_info *
+get_nfs4ref_info(char *host, int port, int nfsver)
+{
+	char netname[MAXNETNAMELEN + 1];
+	enum clnt_stat cstat;
+	struct nfs_fsl_info *fsl_retp = NULL;
+	struct netconfig *netconf = NULL;
+	char *nametemp, *namex = NULL;
+	struct netbuf *nb = NULL;
+	NCONF_HANDLE *nc = NULL;
+
+	fsl_retp = calloc(1, sizeof (struct nfs_fsl_info));
+	if (fsl_retp == NULL) {
+		syslog(LOG_ERR, "get_nfs4ref_info: no memory\n");
+		return (NULL);
+	}
+
+	nametemp = malloc(MAXNETNAMELEN + 1);
+	if (nametemp == NULL) {
+		free(fsl_retp);
+		return (NULL);
+	}
+	host2netname(nametemp, host, NULL);
+	namex = calloc(1, strlen(nametemp) + 1);
+	if (namex == NULL) {
+		free(nametemp);
+		free(fsl_retp);
+		return (NULL);
+	}
+	strncpy(namex, nametemp, strlen(nametemp));
+	free(nametemp);
+	fsl_retp->netname = namex;
+	fsl_retp->netnm_len = strlen(namex) + 1;
+
+	fsl_retp->addr = resolve_netconf(host, NFS_PROGRAM, nfsver,
+	    &netconf, port, NULL, NULL, TRUE, NULL, &cstat);
+
+	if (netconf == NULL || fsl_retp->addr == NULL)
+		goto done;
+
+	fsl_retp->knconf = get_knconf(netconf);
+	if (fsl_retp->knconf == NULL)
+		goto done;
+	fsl_retp->knconf_len = (sizeof (struct knetconfig) +
+	    (KNC_STRSIZE * 2));
+	fsl_retp->netbuf_len = (sizeof (struct netbuf) +
+	    fsl_retp->addr->maxlen);
+	return (fsl_retp);
+done:
+	free_nfs4ref_info(fsl_retp);
+	return (NULL);
+}
+
+void
+free_nfs4ref_info(struct nfs_fsl_info *fsl_retp)
+{
+	if (fsl_retp == NULL)
+		return;
+	free_knconf(fsl_retp->knconf);
+	free(fsl_retp->netname);
+	if (fsl_retp->addr != NULL) {
+		free(fsl_retp->addr->buf);
+		free(fsl_retp->addr);
+	}
+	free(fsl_retp);
+}
+
+void
+cleanup_tli_parms(struct t_bind *tbind, int fd)
+{
+	if (tbind != NULL) {
+		t_free((char *)tbind, T_BIND);
+		tbind = NULL;
+	}
+	if (fd >= 0)
+		(void) t_close(fd);
+	fd = -1;
+}
+
+struct netbuf *
+resolve_netconf(char *host, rpcprog_t prog, rpcvers_t nfsver,
+    struct netconfig **netconf, ushort_t port,
+    struct t_info *tinfo, caddr_t *fhp, bool_t direct_to_server,
+    char *fspath, enum clnt_stat *cstatp)
+{
+	NCONF_HANDLE *nc;
+	struct netconfig *nconf = NULL;
+	int nthtry = FIRST_TRY;
+	struct netbuf *nb;
+	enum clnt_stat cstat;
+
+	nc = setnetpath();
+	if (nc == NULL)
+		goto done;
+retry:
+	while (nconf = getnetpath(nc)) {
+		if (nconf->nc_flag & NC_VISIBLE) {
+			if (nthtry == FIRST_TRY) {
+				if ((nconf->nc_semantics ==
+				    NC_TPI_COTS_ORD) ||
+				    (nconf->nc_semantics ==
+				    NC_TPI_COTS)) {
+					if (port == 0)
+						break;
+					if ((strcmp(nconf->nc_protofmly,
+					    NC_INET) == 0 ||
+					    strcmp(nconf->nc_protofmly,
+					    NC_INET6) == 0) &&
+					    (strcmp(nconf->nc_proto,
+					    NC_TCP) == 0))
+						break;
+				}
+			}
+			if (nthtry == SECOND_TRY) {
+				if (nconf->nc_semantics ==
+				    NC_TPI_CLTS) {
+					if (port == 0)
+						break;
+					if ((strcmp(nconf->nc_protofmly,
+					    NC_INET) == 0 ||
+					    strcmp(nconf->nc_protofmly,
+					    NC_INET6) == 0) &&
+					    (strcmp(nconf->nc_proto,
+					    NC_UDP) == 0))
+						break;
+					}
+			}
+		}
+	} /* while */
+	if (nconf == NULL) {
+		if (++nthtry <= MNT_PREF_LISTLEN) {
+			endnetpath(nc);
+			if ((nc = setnetpath()) == NULL)
+				goto done;
+			goto retry;
+		} else
+			return (NULL);
+	} else {
+		nb = get_server_addr(host, NFS_PROGRAM, nfsver,
+		    nconf, port, NULL, NULL, TRUE, NULL, &cstat);
+		if (cstat != RPC_SUCCESS)
+			goto retry;
+	}
+done:
+	*netconf = nconf;
+	*cstatp = cstat;
+	if (nc)
+		endnetpath(nc);
+	return (nb);
+}
+
+int
+setup_nb_parms(struct netconfig *nconf, struct t_bind *tbind,
+    struct t_info *tinfo, char *hostname, int fd, bool_t direct_to_server,
+    ushort_t port, rpcprog_t prog, rpcvers_t vers, bool_t file_handle)
+{
+	if (nconf == NULL) {
+		return (-1);
+	}
+	if (direct_to_server == TRUE) {
+		struct nd_hostserv hs;
+		struct nd_addrlist *retaddrs;
+		hs.h_host = hostname;
+
+		if (port == 0)
+			hs.h_serv = "nfs";
+		else
+			hs.h_serv = NULL;
+
+		if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK) {
+			return (-1);
+		}
+		memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
+		    retaddrs->n_addrs->len);
+		tbind->addr.len = retaddrs->n_addrs->len;
+		tbind->addr.maxlen = retaddrs->n_addrs->maxlen;
+		netdir_free((void *)retaddrs, ND_ADDRLIST);
+		if (port) {
+			/* LINTED pointer alignment */
+			if (strcmp(nconf->nc_protofmly, NC_INET) == NULL)
+				((struct sockaddr_in *)
+				    tbind->addr.buf)->sin_port =
+				    htons((ushort_t)port);
+			else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL)
+				((struct sockaddr_in6 *)
+				    tbind->addr.buf)->sin6_port =
+				    htons((ushort_t)port);
+		}
+
+		if (file_handle) {
+			if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd,
+			    NULL) == -1)
+				return (-1);
+		}
+	} else if (!file_handle) {
+		if (port) {
+			/* LINTED pointer alignment */
+			if (strcmp(nconf->nc_protofmly, NC_INET) == NULL)
+				((struct sockaddr_in *)
+				    tbind->addr.buf)->sin_port =
+				    htons((ushort_t)port);
+			else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL)
+				((struct sockaddr_in6 *)
+				    tbind->addr.buf)->sin6_port =
+				    htons((ushort_t)port);
+		}
+	} else {
+		return (-1);
+	}
+	return (1);
+}
+
+/*
+ * Sets up TLI interface and finds the address withe netdir_getbyname().
+ * returns the address returned from the call.
+ * Caller frees up the memory allocated here.
+ */
+struct netbuf *
+get_server_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
+    struct netconfig *nconf, ushort_t port,
+    struct t_info *tinfo, caddr_t *fhp, bool_t direct_to_server,
+    char *fspath, enum clnt_stat *cstat)
+{
+	int fd = -1;
+	struct t_bind *tbind = NULL;
+	enum clnt_stat cs = RPC_SYSTEMERROR;
+	struct netbuf *nb = NULL;
+	int ret = -1;
+
+	if (prog == NFS_PROGRAM && vers == NFS_V4)
+		if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
+			goto done;
+
+	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0)
+		goto done;
+
+	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
+		goto done;
+
+	if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd, direct_to_server,
+	    port, prog, vers, 0) < 0)
+		goto done;
+
+	nb = (struct netbuf *)malloc(sizeof (struct netbuf));
+	if (nb == NULL) {
+		syslog(LOG_ERR, "no memory\n");
+		goto done;
+	}
+	nb->buf = (char *)malloc(tbind->addr.maxlen);
+	if (nb->buf == NULL) {
+		syslog(LOG_ERR, "no memory\n");
+		free(nb);
+		nb = NULL;
+		goto done;
+	}
+	(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
+	nb->len = tbind->addr.len;
+	nb->maxlen = tbind->addr.maxlen;
+	cs = RPC_SUCCESS;
+done:
+	*cstat = cs;
+	cleanup_tli_parms(tbind, fd);
+	return (nb);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.h	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_NFS_RESOLVE_H
+#define	_NFS_RESOLVE_H
+
+/* number of transports to try */
+#define	MNT_PREF_LISTLEN	2
+#define	FIRST_TRY		1
+#define	SECOND_TRY		2
+
+extern struct knetconfig *get_knconf(struct netconfig *);
+extern void free_knconf(struct knetconfig *);
+extern bool_t xdr_nfs_fsl_info(XDR *, struct nfs_fsl_info *);
+extern struct netconfig *get_netconfig(NCONF_HANDLE *, ushort_t, char *);
+extern int setup_nb_parms(struct netconfig *, struct t_bind *, struct t_info *,
+    char *, int, bool_t, ushort_t, rpcprog_t, rpcvers_t, int);
+extern void cleanup_tli_parms(struct t_bind *, int);
+extern struct nfs_fsl_info *get_nfs4ref_info(char *, int, int);
+extern void free_nfs4ref_info(struct nfs_fsl_info *);
+extern struct netbuf *get_server_addr(char *, rpcprog_t, rpcvers_t,
+    struct netconfig *, ushort_t, struct t_info *, caddr_t *,
+    bool_t, char *, enum clnt_stat *);
+extern struct netbuf *resolve_netconf(char *, rpcprog_t, rpcvers_t,
+    struct netconfig **, ushort_t, struct t_info *,
+    caddr_t *, bool_t, char *, enum clnt_stat *);
+
+#endif /* _NFS_RESOLVE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/lib/ref_subr.c	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,311 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <limits.h>
+#include <libnvpair.h>
+#include <dlfcn.h>
+#include <link.h>
+#include <rp_plugin.h>
+#include <fcntl.h>
+#include <uuid/uuid.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <sys/param.h>
+#include <nfs/nfs4.h>
+#include <rpcsvc/nfs4_prot.h>
+
+/*
+ * str_to_utf8 - converts a null-terminated C string to a utf8 string
+ */
+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;
+		return (NULL);
+	}
+
+	len = strlen(nm);
+
+	str->utf8string_val = malloc(len);
+	if (str->utf8string_val == NULL) {
+		str->utf8string_len = 0;
+		return (NULL);
+	}
+	str->utf8string_len = len;
+	bcopy(nm, str->utf8string_val, len);
+
+	return (str);
+}
+
+/*
+ * Converts a utf8 string to a C string.
+ * kmem_allocs a new string if not supplied
+ */
+char *
+utf8_to_str(utf8string *str, uint_t *lenp, char *s)
+{
+	char	*sp;
+	char	*u8p;
+	int	len;
+	int	 i;
+
+	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 = malloc(len + 1);
+	if (sp == NULL)
+		return (NULL);
+
+	/*
+	 * At least check for embedded nulls
+	 */
+	for (i = 0; i < len; i++) {
+		sp[i] = u8p[i];
+		if (u8p[i] == '\0') {
+			if (s == NULL)
+				free(sp);
+			return (NULL);
+		}
+	}
+	sp[len] = '\0';
+	*lenp = len + 1;
+
+	return (sp);
+}
+
+void
+print_referral_summary(fs_locations4 *fsl)
+{
+	int i, j;
+	uint_t l;
+	char *s;
+	fs_location4 *fs;
+
+	if (fsl == NULL) {
+		printf("NULL\n");
+		return;
+	}
+
+	for (i = 0; i < fsl->locations.locations_len; i++) {
+		if (i > 0)
+			printf("\n");
+		fs = &fsl->locations.locations_val[i];
+		for (j = 0; j < fs->server.server_len; j++) {
+			s = utf8_to_str(&fs->server.server_val[j], &l, NULL);
+			if (j > 0)
+				printf(",");
+			printf("%s", s ? s : "");
+			if (s)
+				free(s);
+		}
+		printf(":");
+		for (j = 0; j < fs->rootpath.pathname4_len; j++) {
+			s = utf8_to_str(&fs->rootpath.pathname4_val[j],
+			    &l, NULL);
+			printf("/%s", s ? s : "");
+			if (s)
+				free(s);
+		}
+		if (fs->rootpath.pathname4_len == 0)
+			printf("/");
+	}
+	printf("\n");
+}
+
+/*
+ * There is a kernel copy of this routine in nfs4_srv.c.
+ * Changes should be kept in sync.
+ */
+static int
+nfs4_create_components(char *path, component4 *comp4)
+{
+	int slen, plen, ncomp;
+	char *ori_path, *nxtc, buf[MAXNAMELEN];
+
+	if (path == NULL)
+		return (0);
+
+	plen = strlen(path) + 1;	/* include the terminator */
+	ori_path = path;
+	ncomp = 0;
+
+	/* count number of components in the path */
+	for (nxtc = path; nxtc < ori_path + plen; nxtc++) {
+		if (*nxtc == '/' || *nxtc == '\0' || *nxtc == '\n') {
+			if ((slen = nxtc - path) == 0) {
+				path = nxtc + 1;
+				continue;
+			}
+
+			if (comp4 != NULL) {
+				bcopy(path, buf, slen);
+				buf[slen] = '\0';
+				if (str_to_utf8(buf, &comp4[ncomp]) == NULL)
+					return (NULL);
+			}
+
+			ncomp++;	/* 1 valid component */
+			path = nxtc + 1;
+		}
+		if (*nxtc == '\0' || *nxtc == '\n')
+			break;
+	}
+
+	return (ncomp);
+}
+
+/*
+ * There is a kernel copy of this routine in nfs4_srv.c.
+ * Changes should be kept in sync.
+ */
+int
+make_pathname4(char *path, pathname4 *pathname)
+{
+	int ncomp;
+	component4 *comp4;
+
+	if (pathname == NULL)
+		return (0);
+
+	if (path == NULL) {
+		pathname->pathname4_val = NULL;
+		pathname->pathname4_len = 0;
+		return (0);
+	}
+
+	/* count number of components to alloc buffer */
+	if ((ncomp = nfs4_create_components(path, NULL)) == 0) {
+		pathname->pathname4_val = NULL;
+		pathname->pathname4_len = 0;
+		return (0);
+	}
+	comp4 = calloc(ncomp * sizeof (component4), 1);
+	if (comp4 == NULL) {
+		pathname->pathname4_val = NULL;
+		pathname->pathname4_len = 0;
+		return (0);
+	}
+
+	/* copy components into allocated buffer */
+	ncomp = nfs4_create_components(path, comp4);
+
+	pathname->pathname4_val = comp4;
+	pathname->pathname4_len = ncomp;
+
+	return (ncomp);
+}
+
+bool_t
+xdr_component4(register XDR *xdrs, component4 *objp)
+{
+
+	if (!xdr_utf8string(xdrs, objp))
+		return (FALSE);
+	return (TRUE);
+}
+
+bool_t
+xdr_utf8string(register XDR *xdrs, utf8string *objp)
+{
+
+	if (xdrs->x_op != XDR_FREE)
+		return (xdr_bytes(xdrs, (char **)&objp->utf8string_val,
+		    (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING));
+	return (TRUE);
+}
+
+bool_t
+xdr_pathname4(register XDR *xdrs, pathname4 *objp)
+{
+
+	if (!xdr_array(xdrs, (char **)&objp->pathname4_val,
+	    (uint_t *)&objp->pathname4_len, NFS4_MAX_PATHNAME4,
+	    sizeof (component4), (xdrproc_t)xdr_component4))
+		return (FALSE);
+	return (TRUE);
+}
+
+bool_t
+xdr_fs_location4(register XDR *xdrs, fs_location4 *objp)
+{
+
+	if (xdrs->x_op == XDR_DECODE) {
+		objp->server.server_val = NULL;
+		objp->rootpath.pathname4_val = NULL;
+	}
+	if (!xdr_array(xdrs, (char **)&objp->server.server_val,
+	    (uint_t *)&objp->server.server_len, ~0,
+	    sizeof (utf8string), (xdrproc_t)xdr_utf8string))
+		return (FALSE);
+	if (!xdr_pathname4(xdrs, &objp->rootpath))
+		return (FALSE);
+	return (TRUE);
+}
+
+bool_t
+xdr_fs_locations4(register XDR *xdrs, fs_locations4 *objp)
+{
+
+	if (xdrs->x_op == XDR_DECODE) {
+		objp->fs_root.pathname4_len = 0;
+		objp->fs_root.pathname4_val = NULL;
+		objp->locations.locations_val = NULL;
+	}
+	if (!xdr_pathname4(xdrs, &objp->fs_root))
+		return (FALSE);
+	if (!xdr_array(xdrs, (char **)&objp->locations.locations_val,
+	    (uint_t *)&objp->locations.locations_len, ~0,
+	    sizeof (fs_location4), (xdrproc_t)xdr_fs_location4))
+		return (FALSE);
+	return (TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/lib/ref_subr.h	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_REF_SUBR_H
+#define	_REF_SUBR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <nfs/nfs4.h>
+#include <rpcsvc/nfs4_prot.h>
+
+extern utf8string *str_to_utf8(char *, utf8string *);
+extern char *utf8_to_str(utf8string *, uint_t *, char *);
+extern void print_referral_summary(fs_locations4 *);
+extern int make_pathname4(char *, pathname4 *);
+extern bool_t xdr_component4(register XDR *, component4 *);
+extern bool_t xdr_utf8string(register XDR *, utf8string *);
+extern bool_t xdr_pathname4(register XDR *, pathname4 *);
+extern bool_t xdr_fs_location4(register XDR *, fs_location4 *);
+extern bool_t xdr_fs_locations4(register XDR *, fs_locations4 *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _REF_SUBR_H */
--- a/usr/src/cmd/fs.d/nfs/nfsmapid/Makefile	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/nfs/nfsmapid/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -32,12 +32,14 @@
 
 LDLIBS   +=	-L$(ROOT)/usr/lib/nfs -R/usr/lib/nfs
 LDLIBS   +=	-lnsl -lmapid -ldtrace -lidmap
-SRCS	  =	nfsmapid.c nfsmapid_server.c
+COMMON    =	nfs_resolve.o
+SRCS	  =	nfsmapid.c ../lib/nfs_resolve.c nfsmapid_server.c
 DSRC	  =	nfsmapid_dt.d
 DOBJ	  =	$(DSRC:%.d=%.o)
-OBJS	  =	$(SRCS:%.c=%.o)
+OBJS	  =	nfsmapid.o nfsmapid_server.o $(COMMON)
 CPPFLAGS +=	-I../lib -D_POSIX_PTHREAD_SEMANTICS
 
+
 all:		$(TYPEPROG) $(TESTPROG)
 
 $(TYPEPROG):	$(OBJS) $(DSRC)
@@ -45,6 +47,9 @@
 		$(LINK.c) $(ZIGNORE) -o $@ $(DOBJ) $(OBJS) $(LDLIBS)
 		$(POST_PROCESS)
 
+nfs_resolve.o:	../lib/nfs_resolve.c
+		$(COMPILE.c) ../lib/nfs_resolve.c
+
 TESTSRCS  =	nfsmapid_test.c
 TESTOBJS  =	$(TESTSRCS:%.c=%.o)
 TEST_OBJS =	$(TESTOBJS)
--- a/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c	Wed Dec 09 17:27:22 2009 -0600
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -53,6 +53,9 @@
 #include <sys/sdt.h>
 #include <sys/idmap.h>
 #include <idmap.h>
+#include <sys/fs/autofs.h>
+#include <sys/mkdev.h>
+#include "nfs_resolve.h"
 
 #define		UID_MAX_STR_LEN		11	/* Digits in UID_MAX + 1 */
 #define		DIAG_FILE		"/var/run/nfs4_domain"
@@ -585,6 +588,97 @@
 	}
 }
 
+void
+nfsmapid_server_netinfo(refd_door_args_t *referral_args, size_t arg_size)
+{
+	char *res;
+	int res_size;
+	int error;
+	int srsz = 0;
+	char host[MAXHOSTNAMELEN];
+	utf8string	*nfsfsloc_args;
+	refd_door_res_t	*door_res;
+	refd_door_res_t	failed_res;
+	struct nfs_fsl_info *nfs_fsloc_res;
+
+	if (arg_size < sizeof (refd_door_args_t)) {
+		failed_res.res_status = EINVAL;
+		res = (char *)&failed_res;
+		res_size = sizeof (refd_door_res_t);
+		syslog(LOG_ERR,
+		    "nfsmapid_server_netinfo failed: Invalid data\n");
+		goto send_response;
+	}
+
+	if (decode_args(xdr_utf8string, (refd_door_args_t *)referral_args,
+	    (caddr_t *)&nfsfsloc_args, sizeof (utf8string))) {
+		syslog(LOG_ERR, "cannot allocate memory");
+		failed_res.res_status = ENOMEM;
+		failed_res.xdr_len = 0;
+		res = (caddr_t)&failed_res;
+		res_size = sizeof (refd_door_res_t);
+		goto send_response;
+	}
+
+	if (nfsfsloc_args->utf8string_len >= MAXHOSTNAMELEN) {
+		syslog(LOG_ERR, "argument too large");
+		failed_res.res_status = EOVERFLOW;
+		failed_res.xdr_len = 0;
+		res = (caddr_t)&failed_res;
+		res_size = sizeof (refd_door_res_t);
+		goto send_response;
+	}
+
+	snprintf(host, nfsfsloc_args->utf8string_len + 1,
+	    "%s", nfsfsloc_args->utf8string_val);
+
+	nfs_fsloc_res =
+	    get_nfs4ref_info(host, NFS_PORT, NFS_V4);
+
+	xdr_free(xdr_utf8string, (char *)&nfsfsloc_args);
+
+	if (nfs_fsloc_res) {
+		error = 0;
+		error = encode_res(xdr_nfs_fsl_info, &door_res,
+		    (caddr_t)nfs_fsloc_res, &res_size);
+		free_nfs4ref_info(nfs_fsloc_res);
+		if (error != 0) {
+			syslog(LOG_ERR,
+			    "error allocating fs_locations "
+			    "results buffer");
+			failed_res.res_status = error;
+			failed_res.xdr_len = srsz;
+			res = (caddr_t)&failed_res;
+			res_size = sizeof (refd_door_res_t);
+		} else {
+			door_res->res_status = 0;
+			res = (caddr_t)door_res;
+		}
+	} else {
+		failed_res.res_status = EINVAL;
+		failed_res.xdr_len = 0;
+		res = (caddr_t)&failed_res;
+		res_size = sizeof (refd_door_res_t);
+	}
+
+send_response:
+	srsz = res_size;
+	errno = 0;
+
+	error = door_return(res, res_size, NULL, 0);
+	if (errno == E2BIG) {
+		failed_res.res_status = EOVERFLOW;
+		failed_res.xdr_len = srsz;
+		res = (caddr_t)&failed_res;
+		res_size = sizeof (refd_door_res_t);
+	} else {
+		res = NULL;
+		res_size = 0;
+	}
+
+	door_return(res, res_size, NULL, 0);
+}
+
 /* ARGSUSED */
 void
 nfsmapid_func(void *cookie, char *argp, size_t arg_size,
@@ -592,6 +686,7 @@
 {
 	struct mapid_arg	*mapargp;
 	struct mapid_res	mapres;
+	refd_door_args_t	*referral_args;
 
 	/*
 	 * Make sure we have a valid argument
@@ -606,6 +701,7 @@
 
 	/* LINTED pointer cast */
 	mapargp = (struct mapid_arg *)argp;
+	referral_args = (refd_door_args_t *)argp;
 	switch (mapargp->cmd) {
 	case NFSMAPID_STR_UID:
 		nfsmapid_str_uid(mapargp, arg_size);
@@ -619,6 +715,8 @@
 	case NFSMAPID_GID_STR:
 		nfsmapid_gid_str(mapargp, arg_size);
 		return;
+	case NFSMAPID_SRV_NETINFO:
+		nfsmapid_server_netinfo(referral_args, arg_size);
 	default:
 		break;
 	}
@@ -796,3 +894,190 @@
 
 	return (NULL);
 }
+
+bool_t
+xdr_utf8string(XDR *xdrs, utf8string *objp)
+{
+	if (xdrs->x_op != XDR_FREE)
+		return (xdr_bytes(xdrs, (char **)&objp->utf8string_val,
+		    (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING));
+	return (TRUE);
+}
+
+
+int
+decode_args(xdrproc_t xdrfunc, refd_door_args_t *argp, caddr_t *xdrargs,
+    int size)
+{
+	XDR xdrs;
+
+	caddr_t tmpargs = (caddr_t)&((refd_door_args_t *)argp)->xdr_arg;
+	size_t arg_size = ((refd_door_args_t *)argp)->xdr_len;
+
+	xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE);
+
+	*xdrargs = calloc(1, size);
+	if (*xdrargs == NULL) {
+		syslog(LOG_ERR, "error allocating arguments buffer");
+		return (ENOMEM);
+	}
+
+	if (!(*xdrfunc)(&xdrs, *xdrargs)) {
+		free(*xdrargs);
+		*xdrargs = NULL;
+		syslog(LOG_ERR, "error decoding arguments");
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+int
+encode_res(
+	xdrproc_t xdrfunc,
+	refd_door_res_t **results,
+	caddr_t resp,
+	int *size)
+{
+	XDR xdrs;
+
+	*size = xdr_sizeof((*xdrfunc), resp);
+	*results = malloc(sizeof (refd_door_res_t) + *size);
+	if (*results == NULL) {
+		return (ENOMEM);
+	}
+	(*results)->xdr_len = *size;
+	*size = sizeof (refd_door_res_t) + (*results)->xdr_len;
+	xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res),
+	    (*results)->xdr_len, XDR_ENCODE);
+	if (!(*xdrfunc)(&xdrs, resp)) {
+		(*results)->res_status = EINVAL;
+		syslog(LOG_ERR, "error encoding results");
+		return ((*results)->res_status);
+	}
+	(*results)->res_status = 0;
+	return ((*results)->res_status);
+}
+
+
+bool_t
+xdr_knetconfig(XDR *xdrs, struct knetconfig *objp)
+{
+	rpc_inline_t *buf;
+	int i;
+	u_longlong_t dev64;
+#if !defined(_LP64)
+	uint32_t major, minor;
+#endif
+
+	if (!xdr_u_int(xdrs, &objp->knc_semantics))
+		return (FALSE);
+	if (!xdr_opaque(xdrs, objp->knc_protofmly, KNC_STRSIZE))
+		return (FALSE);
+	if (!xdr_opaque(xdrs, objp->knc_proto, KNC_STRSIZE))
+		return (FALSE);
+
+	/*
+	 * For interoperability between 32-bit daemon and 64-bit kernel,
+	 * we always treat dev_t as 64-bit number and do the expanding
+	 * or compression of dev_t as needed.
+	 * We have to hand craft the conversion since there is no available
+	 * function in ddi.c. Besides ddi.c is available only in the kernel
+	 * and we want to keep both user and kernel of xdr_knetconfig() the
+	 * same for consistency.
+	 */
+
+	if (xdrs->x_op == XDR_ENCODE) {
+#if defined(_LP64)
+		dev64 = objp->knc_rdev;
+#else
+		major = (objp->knc_rdev >> NBITSMINOR32) & MAXMAJ32;
+		minor = objp->knc_rdev & MAXMIN32;
+		dev64 = (((unsigned long long)major) << NBITSMINOR64) | minor;
+#endif
+		if (!xdr_u_longlong_t(xdrs, &dev64))
+			return (FALSE);
+	}
+	if (xdrs->x_op == XDR_DECODE) {
+#if defined(_LP64)
+		if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&objp->knc_rdev))
+			return (FALSE);
+#else
+		if (!xdr_u_longlong_t(xdrs, &dev64))
+			return (FALSE);
+
+		major = (dev64 >> NBITSMINOR64) & L_MAXMAJ32;
+		minor = dev64 & L_MAXMIN32;
+		objp->knc_rdev = (major << L_BITSMINOR32) | minor;
+#endif
+	}
+
+	if (xdrs->x_op == XDR_ENCODE) {
+		buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
+			    sizeof (uint_t), (xdrproc_t)xdr_u_int))
+				return (FALSE);
+		} else {
+			uint_t *genp;
+
+			for (i = 0, genp = objp->knc_unused;
+			    i < 8; i++) {
+#if defined(_LP64) || defined(_KERNEL)
+				IXDR_PUT_U_INT32(buf, *genp++);
+#else
+				IXDR_PUT_U_LONG(buf, *genp++);
+#endif
+			}
+		}
+		return (TRUE);
+	} else if (xdrs->x_op == XDR_DECODE) {
+		buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
+			    sizeof (uint_t), (xdrproc_t)xdr_u_int))
+				return (FALSE);
+		} else {
+			uint_t *genp;
+
+			for (i = 0, genp = objp->knc_unused;
+			    i < 8; i++) {
+#if defined(_LP64) || defined(_KERNEL)
+				*genp++ = IXDR_GET_U_INT32(buf);
+#else
+				*genp++ = IXDR_GET_U_LONG(buf);
+#endif
+			}
+		}
+		return (TRUE);
+	}
+
+	if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
+	    sizeof (uint_t), (xdrproc_t)xdr_u_int))
+		return (FALSE);
+	return (TRUE);
+}
+
+/*
+ * used by NFSv4 referrals to get info needed for NFSv4 referral mount.
+ */
+bool_t
+xdr_nfs_fsl_info(XDR *xdrs, struct nfs_fsl_info *objp)
+{
+
+	if (!xdr_u_int(xdrs, &objp->netbuf_len))
+		return (FALSE);
+	if (!xdr_u_int(xdrs, &objp->netnm_len))
+		return (FALSE);
+	if (!xdr_u_int(xdrs, &objp->knconf_len))
+		return (FALSE);
+	if (!xdr_string(xdrs, &objp->netname, ~0))
+		return (FALSE);
+	if (!xdr_pointer(xdrs, (char **)&objp->addr, objp->netbuf_len,
+	    (xdrproc_t)xdr_netbuf))
+		return (FALSE);
+	if (!xdr_pointer(xdrs, (char **)&objp->knconf,
+	    objp->knconf_len, (xdrproc_t)xdr_knetconfig))
+		return (FALSE);
+	return (TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/nfsref/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/fs.d/nfs/nfsref/Makefile
+
+PROG=		nfsref
+
+POFILE=		nfsref.po
+
+include		../../../Makefile.cmd
+
+CFLAGS +=	$(CCVERBOSE) -I../lib
+OBJS=	nfsref.o ref_subr.o
+SRCS=	nfsref.c
+
+LDLIBS += -lreparse -lnvpair -lnsl -lumem
+
+$(PROG):	$(OBJS)
+		$(LINK.c) -o $@ $(LDLIBS) $(OBJS)
+		$(POST_PROCESS)
+
+FILEMODE=       0555
+
+.KEEP_STATE:
+
+all:		$(PROG)
+
+ref_subr.o:	../lib/ref_subr.c
+	$(COMPILE.c) ../lib/ref_subr.c
+
+install:	all $(ROOTUSRSBINPROG)
+
+catalog: $(POFILE)
+
+$(POFILE): $(SRCS)
+	$(RM) $@
+	$(COMPILE.cpp) $(SRCS)   > $(POFILE).i
+	$(XGETTEXT)     $(XGETFLAGS) $(POFILE).i
+	sed "/^domain/d"        messages.po     > $@
+	$(RM) $(POFILE).i messages.po
+
+lint:
+	$(LINT.c) $(SRCS) $(LDLIBS)
+
+clean:     
+	$(RM) $(OBJS)
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/nfsref/nfsref.c	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,401 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <limits.h>
+#include <libnvpair.h>
+#include <locale.h>
+#include <sys/stat.h>
+#include <sys/fs_reparse.h>
+#include <rp_plugin.h>
+#include <uuid/uuid.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <priv.h>
+#include <nfs/nfs4.h>
+#include <rpcsvc/nfs4_prot.h>
+#include "ref_subr.h"
+
+#ifndef TEXT_DOMAIN
+#define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
+#endif /* TEXT_DOMAIN */
+
+extern int errno;
+
+void
+usage()
+{
+	fprintf(stderr, gettext("Usage:\n"));
+	fprintf(stderr,
+	    gettext("\tnfsref [-t type] add path location [location ...]\n"));
+	fprintf(stderr, gettext("\tnfsref [-t type] remove path\n"));
+	fprintf(stderr, gettext("\tnfsref [-t type] lookup path\n"));
+}
+
+/*
+ * Copy a string from source to destination, escaping space
+ * with a backslash and escaping the escape character as well.
+ */
+int
+add_escape(char *src, char *dest, int limit)
+{
+	char *sp, *dp;
+	int destlen = 0;
+
+	sp = src;
+	dp = dest;
+
+	while (*sp && destlen < limit) {
+		if (*sp == '\\') {
+			*dp++ = '\\';
+			*dp++ = '\\';
+			destlen++;
+		} else if (*sp == ' ') {
+			*dp++ = '\\';
+			*dp++ = ' ';
+			destlen++;
+		} else
+			*dp++ = *sp;
+		destlen++;
+		sp++;
+	}
+	if (limit <= 0)
+		return (-1);
+	return (destlen);
+}
+
+int
+addref(char *sl_path, char *svc_type, int optind, int argc, char *argv[])
+{
+	int err, fd, i, len, oldlen, notfound = 0;
+	char *text, *location;
+	nvlist_t *nvl = NULL;
+	char buf[SYMLINK_MAX];
+	struct stat sbuf;
+
+	/* Get an nvlist */
+	nvl = reparse_init();
+	if (nvl == NULL)
+		return (ENOMEM);
+
+	/* Get the reparse point data, if the RP exists */
+	err = readlink(sl_path, buf, SYMLINK_MAX);
+	if (err == -1) {
+		if (errno == ENOENT) {
+			notfound = 1;
+			err = 0;
+		} else {
+			reparse_free(nvl);
+			return (errno);
+		}
+	} else {
+		buf[err] = '\0';
+	}
+
+	/* Get any data into nvlist */
+	if (notfound == 0)
+		err = reparse_parse(buf, nvl);
+	if (err != 0) {
+		reparse_free(nvl);
+		return (err);
+	}
+
+	/*
+	 * Accumulate multiple locations on the command line into 'buf'
+	 */
+	oldlen = len = 0;
+	location = NULL;
+	for (i = optind; i < argc; i++) {
+		bzero(buf, sizeof (buf));
+		len += add_escape(argv[i], buf, SYMLINK_MAX) + 2;
+		location = realloc(location, len);
+		location[oldlen] = '\0';
+		oldlen = len;
+		strlcat(location, buf, len);
+		strlcat(location, " ", len);
+	}
+	location[len - 2] = '\0';
+
+	/* Add to the list */
+	err = reparse_add(nvl, svc_type, location);
+	if (err) {
+		reparse_free(nvl);
+		return (err);
+	}
+
+	/* Get the new or modified symlink contents */
+	err = reparse_unparse(nvl, &text);
+	reparse_free(nvl);
+	if (err)
+		return (err);
+
+	/* Delete first if found */
+	if (notfound == 0) {
+		err =  reparse_delete(sl_path);
+		if (err) {
+			free(text);
+			return (err);
+		}
+	}
+
+	/* Finally, write out the reparse point */
+	err = reparse_create(sl_path, text);
+	free(text);
+	if (err)
+		return (err);
+
+	err = lstat(sl_path, &sbuf);
+	if (err == 0 && strcasecmp(sbuf.st_fstype, "ZFS") != 0)
+		printf(gettext(
+		    "Warning: referrals do not work on this filesystem\n"));
+
+	if (notfound)
+		printf(gettext("Created reparse point %s\n"), sl_path);
+	else
+		printf(gettext("Added to reparse point %s\n"), sl_path);
+
+	return (0);
+}
+
+int
+delref(char *sl_path, char *svc_type)
+{
+	char *cp;
+	char *svc_data;
+	int err;
+	nvlist_t *nvl;
+	nvpair_t *curr;
+	char buf[SYMLINK_MAX];
+	int fd, fd2;
+	FILE *fp, *fp2;
+	char uuid[UUID_PRINTABLE_STRING_LENGTH], path[256], loc[2048];
+
+	/* Get an nvlist */
+	if (!(nvl = reparse_init()))
+		return (ENOMEM);
+
+	/* Get the symlink data (should be there) */
+	err = readlink(sl_path, buf, SYMLINK_MAX);
+	if (err == -1) {
+		reparse_free(nvl);
+		return (errno);
+	}
+	buf[err] = '\0';
+
+	/* Get the records into the nvlist */
+	err = reparse_parse(buf, nvl);
+	if (err) {
+		reparse_free(nvl);
+		return (err);
+	}
+
+	/* Remove from nvlist */
+	err = reparse_remove(nvl, svc_type);
+	if (err) {
+		reparse_free(nvl);
+		return (err);
+	}
+
+	/* Any list entries left? If so, turn nvlist back to string. */
+	curr = nvlist_next_nvpair(nvl, NULL);
+	if (curr != NULL) {
+		err = reparse_unparse(nvl, &cp);
+		reparse_free(nvl);
+		if (err)
+			return (err);
+	} else {
+		reparse_free(nvl);
+		cp = NULL;
+	}
+
+	/* Finally, delete and perhaps recreate the reparse point */
+	err = reparse_delete(sl_path);
+	if (err) {
+		free(cp);
+		return (err);
+	}
+
+	if (cp != NULL) {
+		err = reparse_create(sl_path, cp);
+		free(cp);
+		if (err)
+			return (err);
+	}
+	printf(gettext("Removed svc_type '%s' from %s\n"), svc_type, sl_path);
+	return (err);
+}
+
+int
+lookup(char *sl_path, char *svc_type, int type_set)
+{
+	int err;
+	size_t bufsize;
+	char buf[1024];
+	char *type, *svc_data;
+	nvlist_t *nvl;
+	nvpair_t *curr;
+	fs_locations4 fsl;
+	XDR xdr;
+
+	if (!(nvl = reparse_init()))
+		return (-1);
+
+	/* Get reparse point data */
+	err = readlink(sl_path, buf, SYMLINK_MAX);
+	if (err == -1)
+		return (errno);
+	buf[err] = '\0';
+
+	/* Parse it to an nvlist */
+	err = reparse_parse(buf, nvl);
+	if (err) {
+		reparse_free(nvl);
+		return (err);
+	}
+
+	/* Look for entries of the requested service type */
+	curr = NULL;
+	while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) {
+		type = nvpair_name(curr);
+		if (type_set && strcasecmp(type, svc_type) == 0)
+			break;
+		if (!type_set && strncasecmp(type, "nfs", 3) == 0)
+			break;
+	}
+	if (curr == NULL) {
+		reparse_free(nvl);
+		return (ENOENT);
+	}
+
+	/* Get the service data and look it up */
+	nvpair_value_string(curr, &svc_data);
+
+	bufsize = sizeof (buf);
+	err = reparse_deref(type, svc_data, buf, &bufsize);
+	reparse_free(nvl);
+	if (err)
+		return (err);
+
+	xdrmem_create(&xdr, buf, bufsize, XDR_DECODE);
+	err = xdr_fs_locations4(&xdr, &fsl);
+	XDR_DESTROY(&xdr);
+	if (err != TRUE)
+		return (ENOENT);
+	printf(gettext("%s points to: "), sl_path);
+	print_referral_summary(&fsl);
+	return (0);
+}
+
+extern char *optarg;
+extern int optind, optopt;
+
+int
+main(int argc, char *argv[])
+{
+	char c, *command, *sl_path, *svc_type;
+	int type_set, err;
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	svc_type = "nfs-basic";		/* Default from SMF some day */
+	type_set = 0;			/* Lookup any nfs type */
+
+	/* Look for options (just the service type now) */
+	while ((c = getopt(argc, argv, "t:")) != -1) {
+		switch (c) {
+		case 't':
+			svc_type = optarg;
+			type_set = 1;
+			break;
+
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	/* Make sure there's at least a command and one argument */
+	if (optind + 1 >= argc) {
+		usage();
+		exit(1);
+	}
+
+	err = rp_plugin_init();
+	switch (err) {
+	case RP_OK:
+		break;
+	case RP_NO_PLUGIN_DIR:
+		fprintf(stderr,
+		    gettext("Warning: no plugin directory, continuing...\n"));
+		break;
+	case RP_NO_PLUGIN:
+		fprintf(stderr,
+		    gettext("Warning: no plugin found, continuing...\n"));
+		break;
+	case RP_NO_MEMORY:
+		fprintf(stderr,
+		    gettext("rp_plugin_init failed, no memory\n"));
+		exit(0);
+	default:
+		fprintf(stderr,
+		    gettext("rp_plugin_init failed, error %d\n"), err);
+		exit(0);
+	}
+
+	command = argv[optind++];
+	sl_path = argv[optind++];
+
+	if (strcmp(command, "add") == 0) {
+
+		if (optind >= argc) {
+			usage();
+			exit(1);
+		}
+
+		err = addref(sl_path, svc_type, optind, argc, argv);
+
+	} else if (strcmp(command, "remove") == 0) {
+
+		err = delref(sl_path, svc_type);
+
+	} else if (strcmp(command, "lookup") == 0) {
+
+		err = lookup(sl_path, svc_type, type_set);
+
+	} else {
+		usage();
+		exit(1);
+	}
+	if (err != 0)
+		fprintf(stderr, gettext("Command %s failed: %s\n"), command,
+		    strerror(err));
+	return (err);
+}
--- a/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c	Wed Dec 09 17:27:22 2009 -0600
@@ -117,9 +117,10 @@
 
 /*
  * MI4_MIRRORMOUNT is canonically defined in nfs4_clnt.h, but we cannot
- * include that file here.
+ * include that file here.  Same with MI4_REFERRAL.
  */
 #define	MI4_MIRRORMOUNT 0x4000
+#define	MI4_REFERRAL	0x8000
 #define	NFS_V4		4
 
 static int req_width(kstat_t *, int);
@@ -1241,6 +1242,8 @@
 		if (mik.mik_vers >= NFS_V4) {
 			if (mik.mik_flags & MI4_MIRRORMOUNT)
 				printf(",mirrormount");
+			if (mik.mik_flags & MI4_REFERRAL)
+				printf(",referral");
 		}
 
 		printf(",rsize=%d,wsize=%d,retrans=%d,timeo=%d",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,56 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include $(SRC)/lib/Makefile.lib
+
+SUBDIRS =	$(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+MSGFILES = 	libnfs_basic.c
+POFILE  =	rp_basic.po
+
+all	:=	TARGET= all
+clean	:=	TARGET= clean
+clobber	:=	TARGET= clobber
+install	:=	TARGET= install
+lint	:=	TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint:	$(SUBDIRS)
+
+catalog: $(POFILE)
+
+$(POFILE): 	$(MSGFILES)
+	$(BUILDPO.msgfiles)
+
+_msg:	$(MSGDOMAINPOFILE)
+
+$(SUBDIRS):	FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/Makefile.com	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,65 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY =	libnfs_basic.a
+VERS =		.1
+
+LIBOBJS =	libnfs_basic.o
+COMMON =	ref_subr.o
+OBJECTS =	$(LIBOBJS) $(COMMON)
+
+include $(SRC)/lib/Makefile.lib
+
+lintcheck := SRCS = ../libnfs_basic.c ../../lib/ref_subr.c
+
+ROOTLIBDIR =	$(ROOT)/usr/lib/reparse
+ROOTLIBDIR64 =	$(ROOT)/usr/lib/reparse/$(MACH64)
+
+LIBSRCS = $(LIBOBJS:%.o=$(SRCDIR)/%.c)
+
+LIBS =		$(DYNLIB)
+LDLIBS +=	-lc -lnsl
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-D_REENTRANT -I$(SRC)/cmd/fs.d/nfs/lib
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: $(ROOTLIBDIR) $(ROOTLIBDIR64) all
+
+lint: lintcheck
+
+pics/ref_subr.o:     ../../lib/ref_subr.c
+	$(COMPILE.c) -o pics/ref_subr.o ../../lib/ref_subr.c
+	$(POST_PROCESS_O)
+
+$(ROOTLIBDIR):
+	$(INS.dir)
+
+$(ROOTLIBDIR64):
+	$(INS.dir)
+
+include $(SRC)/lib/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/amd64/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/i386/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/libnfs_basic.c	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,310 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <limits.h>
+#include <libnvpair.h>
+#include <dlfcn.h>
+#include <link.h>
+#include <rp_plugin.h>
+#include <fcntl.h>
+#include <uuid/uuid.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <sys/param.h>
+#include <nfs/nfs4.h>
+#include <rpcsvc/nfs4_prot.h>
+#include "ref_subr.h"
+
+extern int errno;
+
+#define	SERVICE_TYPE	"nfs-basic"
+
+char *nfs_basic_service_type(void);
+boolean_t nfs_basic_supports_svc(const char *);
+int nfs_basic_deref(const char *, const char *, char *, size_t *);
+int nfs_basic_form(const char *, const char *, char *, size_t *);
+
+struct rp_plugin_ops rp_plugin_ops = {
+	RP_PLUGIN_V1,
+	NULL,			/* rpo_init */
+	NULL,			/* rpo_fini */
+	nfs_basic_service_type,
+	nfs_basic_supports_svc,
+	nfs_basic_form,
+	nfs_basic_deref
+};
+
+/*
+ * What service type does this module support?
+ */
+char *
+nfs_basic_service_type()
+{
+	return (SERVICE_TYPE);
+}
+
+/*
+ * Does this module support a particular service type?
+ */
+boolean_t
+nfs_basic_supports_svc(const char *svc_type)
+{
+	if (!svc_type)
+		return (0);
+	return (!strncasecmp(svc_type, SERVICE_TYPE, strlen(SERVICE_TYPE)));
+}
+
+/*
+ * Take a string with a set of locations like this:
+ *   host1:/path1 host2:/path2 host3:/path3
+ * and convert it to an fs_locations4 for the deref routine.
+ */
+static fs_locations4 *
+get_fs_locations(char *buf)
+{
+	fs_locations4 *result = NULL;
+	fs_location4 *fsl_array;
+	int i = 0, fsl_count = 0, gothost = 0, escape = 0, delimiter = 0;
+	int len;
+	char *p, *sp, *dp, buf2[SYMLINK_MAX];
+
+	if (buf == NULL)
+		return (NULL);
+#ifdef DEBUG
+	printf("get_fs_locations: input %s\n", buf);
+#endif
+	/*
+	 * Count fs_location entries by counting spaces.
+	 * Remember that escaped spaces ("\ ") may exist.
+	 * We mark the location boundaries with null bytes.
+	 * Variable use:
+	 *   escape -   set if we have found a backspace,
+	 *		part of either "\ " or "\\"
+	 *   delimiter - set if we have found a space and
+	 *		 used to skip multiple spaces
+	 */
+	for (sp = buf; sp && *sp; sp++) {
+		if (*sp == '\\') {
+			escape = 1;
+			delimiter = 0;
+			continue;
+		}
+		if (*sp == ' ') {
+			if (delimiter == 1)
+				continue;
+			if (escape == 0) {
+				delimiter = 1;
+				fsl_count++;
+				*sp = '\0';
+			} else
+				escape = 0;
+		} else
+			delimiter = 0;
+	}
+	len = sp - buf;
+	sp--;
+	if (escape == 0 && *sp != '\0')
+		fsl_count++;
+#ifdef DEBUG
+	printf("get_fs_locations: fsl_count %d\n", fsl_count);
+#endif
+	if (fsl_count == 0)
+		goto out;
+
+	/* Alloc space for everything */
+	result = malloc(sizeof (fs_locations4));
+	if (result == NULL)
+		goto out;
+	fsl_array = malloc(fsl_count * sizeof (fs_location4));
+	if (fsl_array == NULL) {
+		free(result);
+		result = NULL;
+		goto out;
+	}
+	result->locations.locations_len = fsl_count;
+	result->locations.locations_val = fsl_array;
+	result->fs_root.pathname4_len = 0;
+	result->fs_root.pathname4_val = NULL;
+
+	/*
+	 * Copy input, removing escapes from host:/path/to/my\ files
+	 */
+	sp = buf;
+	dp = buf2;
+	bzero(buf2, sizeof (buf2));
+
+	while ((sp && *sp && (sp - buf < len)) || gothost) {
+
+		if (!gothost) {
+			/* Drop leading spaces */
+			if (*sp == ' ') {
+				sp++;
+				continue;
+			}
+
+			/* Look for the rightmost colon for host */
+			p = strrchr(sp, ':');
+			if (!p) {
+#ifdef DEBUG
+				printf("get_fs_locations: skipping %s\n", sp);
+#endif
+				sp += strlen(sp) + 1;
+			} else {
+				bcopy(sp, dp, p - sp);
+				sp = p + 1;
+#ifdef DEBUG
+				printf("get_fs_locations: host %s\n", buf2);
+#endif
+				fsl_array[i].server.server_len = 1;
+				fsl_array[i].server.server_val =
+				    malloc(sizeof (utf8string));
+				if (fsl_array[i].server.server_val == NULL) {
+					int j;
+
+					free(result);
+					result = NULL;
+					for (j = 0; j < i; j++)
+						free(fsl_array[j].
+						    server.server_val);
+					free(fsl_array);
+					goto out;
+				}
+				str_to_utf8(buf2,
+				    fsl_array[i].server.server_val);
+				gothost = 1;
+				dp = buf2;
+				bzero(buf2, sizeof (buf2));
+			}
+			continue;
+		}
+
+		/* End of string should mean a pathname */
+		if (*sp == '\0' && gothost) {
+#ifdef DEBUG
+			printf("get_fs_locations: path %s\n", buf2);
+#endif
+			(void) make_pathname4(buf2, &fsl_array[i].rootpath);
+			i++;
+			gothost = 0;
+			dp = buf2;
+			bzero(buf2, sizeof (buf2));
+			if (sp - buf < len)
+				sp++;
+			continue;
+		}
+
+		/* Skip a single escape character */
+		if (*sp == '\\')
+			sp++;
+
+		/* Plain char, just copy it */
+		*dp++ = *sp++;
+	}
+
+out:
+	return (result);
+}
+
+/*
+ * Deref function for nfs-basic service type returns an fs_locations4.
+ */
+int
+nfs_basic_deref(const char *svc_type, const char *svc_data, char *buf,
+    size_t *bufsz)
+{
+	int slen, err;
+	fs_locations4 *fsl;
+	XDR xdr;
+
+	if ((!svc_type) || (!svc_data) || (!buf) || (!bufsz) || (*bufsz == 0))
+		return (EINVAL);
+
+	if (strcasecmp(svc_type, SERVICE_TYPE))
+		return (ENOTSUP);
+
+	fsl = get_fs_locations((char *)svc_data);
+	if (fsl == NULL)
+		return (ENOENT);
+#ifdef DEBUG
+	printf("nfs_basic_deref: past get_fs_locations()\n");
+#endif
+	slen = xdr_sizeof(xdr_fs_locations4, (void *)fsl);
+	if (slen > *bufsz) {
+		*bufsz = slen;
+		xdr_free(xdr_fs_locations4, (char *)fsl);
+		return (EOVERFLOW);
+	}
+#ifdef DEBUG
+	printf("nfs_basic_deref: past buffer check\n");
+	print_referral_summary(fsl);
+#endif
+	xdrmem_create(&xdr, buf, *bufsz, XDR_ENCODE);
+	err = xdr_fs_locations4(&xdr, fsl);
+	XDR_DESTROY(&xdr);
+	xdr_free(xdr_fs_locations4, (char *)fsl);
+	if (err != TRUE)
+		return (EINVAL);
+	*bufsz = slen;
+#ifdef DEBUG
+	printf("nfs_basic_deref: past xdr_fs_locations4() and done\n");
+#endif
+	return (0);
+}
+
+/*
+ * Form function for nfs-basic service type.
+ */
+int
+nfs_basic_form(const char *svc_type, const char *svc_data, char *buf,
+    size_t *bufsz)
+{
+	int slen;
+
+	if ((!svc_type) || (!svc_data) || (!buf) || (*bufsz == 0))
+		return (EINVAL);
+
+	if (strcmp(svc_type, SERVICE_TYPE))
+		return (ENOTSUP);
+
+	slen = strlen(svc_data) + 1;
+	if (slen > *bufsz) {
+		*bufsz = slen;
+		return (EOVERFLOW);
+	}
+	*bufsz = slen;
+	strncpy(buf, svc_data, slen);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/mapfile-vers	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate_1.1 {
+    global:
+	rp_plugin_ops;
+    local:
+	*;
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/sparc/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/rp_basic/sparcv9/Makefile	Wed Dec 09 17:27:22 2009 -0600
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
--- a/usr/src/cmd/fs.d/nfs/svc/server.xml	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/cmd/fs.d/nfs/svc/server.xml	Wed Dec 09 17:27:22 2009 -0600
@@ -94,6 +94,13 @@
 		<service_fmri value='svc:/network/shares/group' />
 	</dependency>
 
+	<dependency name='reparse'
+	    grouping='optional_all'
+	    restart_on='none'
+	    type='service'>
+		<service_fmri value='svc:/system/filesystem/reparse' />
+	</dependency>
+
 	<!-- Must have all local filesystems mounted before we share them -->
 	<dependency name='filesystem-local'
 	    grouping='require_all'
--- a/usr/src/head/rpcsvc/daemon_utils.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/head/rpcsvc/daemon_utils.h	Wed Dec 09 17:27:22 2009 -0600
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_DAEMON_UTILS_H
 #define	_DAEMON_UTILS_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/stat.h>
 
 #ifdef	__cplusplus
@@ -43,6 +40,7 @@
 #define	NFS4CBD		"svc:/network/nfs/cbd:default"
 #define	NFSMAPID	"svc:/network/nfs/mapid:default"
 #define	RQUOTAD		"svc:/network/nfs/rquota:default"
+#define	REPARSED	"svc:/system/filesystem/reparse:default"
 
 #define	DAEMON_UID	 1
 #define	DAEMON_GID	12
--- a/usr/src/head/rpcsvc/nfs4_prot.x	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/head/rpcsvc/nfs4_prot.x	Wed Dec 09 17:27:22 2009 -0600
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -28,8 +27,6 @@
  *	nfs4_prot.x
  */
 
-%#pragma ident	"@(#)nfs4_prot.x	1.122"
-
 /*
  * Basic typedefs for RFC 1832 data type definitions
  */
@@ -211,6 +208,16 @@
 	fs_location4	locations<>;
 };
 
+struct nfs_fsl_info {
+	uint_t			netbuf_len;
+	uint_t			netnm_len;
+	uint_t			knconf_len;
+	string			netname<MAXNETNAMELEN>;
+	struct netbuf		*addr;
+	struct knetconfig	*knconf;
+};
+
+
 /*
  * Various Access Control Entry definitions
  */
--- a/usr/src/lib/libshare/nfs/libshare_nfs.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/lib/libshare/nfs/libshare_nfs.c	Wed Dec 09 17:27:22 2009 -0600
@@ -114,9 +114,10 @@
  */
 
 static char *service_list_default[] =
-	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NULL };
+	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
 static char *service_list_logging[] =
-	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, NULL };
+	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED,
+	    NULL };
 
 /*
  * option definitions.  Make sure to keep the #define for the option
@@ -2930,6 +2931,9 @@
 		case SVC_NFSLOGD:
 			service = NFSLOGD;
 			break;
+		case SVC_REPARSED:
+			service = REPARSED;
+			break;
 		default:
 			continue;
 		}
--- a/usr/src/lib/libshare/nfs/libshare_nfs.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/lib/libshare/nfs/libshare_nfs.h	Wed Dec 09 17:27:22 2009 -0600
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -97,6 +97,7 @@
 #define	SVC_NFSMAPID	0x0020
 #define	SVC_RQUOTAD	0x0040
 #define	SVC_NFSLOGD	0x0080
+#define	SVC_REPARSED	0x0100
 
 /*
  * place holder for future service -- will move to daemon_utils.h when
--- a/usr/src/pkgdefs/SUNWnfscu/prototype_com	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/pkgdefs/SUNWnfscu/prototype_com	Wed Dec 09 17:27:22 2009 -0600
@@ -19,11 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
--- a/usr/src/pkgdefs/SUNWnfssu/prototype_com	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/pkgdefs/SUNWnfssu/prototype_com	Wed Dec 09 17:27:22 2009 -0600
@@ -18,11 +18,9 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -53,5 +51,9 @@
 f none usr/lib/nfs/nfsd 555 root bin
 f none usr/lib/nfs/nfslogd 555 root bin
 f none usr/lib/nfs/rquotad 555 root bin
+d none usr/lib/reparse 755 root bin
+f none usr/lib/reparse/libnfs_basic.so.1 755 root bin
+s none usr/lib/reparse/libnfs_basic.so=./libnfs_basic.so.1
 d none usr/sbin 755 root bin
 f none usr/sbin/exportfs 555 root bin
+f none usr/sbin/nfsref 555 root bin
--- a/usr/src/pkgdefs/SUNWnfssu/prototype_i386	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/pkgdefs/SUNWnfssu/prototype_i386	Wed Dec 09 17:27:22 2009 -0600
@@ -18,11 +18,9 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -47,3 +45,6 @@
 #
 d none usr/lib/fs/nfs/amd64 755 root sys
 f none usr/lib/fs/nfs/amd64/libshare_nfs.so.1 755 root bin
+d none usr/lib/reparse/amd64 755 root sys
+f none usr/lib/reparse/amd64/libnfs_basic.so.1 755 root bin
+s none usr/lib/reparse/amd64/libnfs_basic.so=./libnfs_basic.so.1
--- a/usr/src/pkgdefs/SUNWnfssu/prototype_sparc	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/pkgdefs/SUNWnfssu/prototype_sparc	Wed Dec 09 17:27:22 2009 -0600
@@ -18,11 +18,9 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -46,3 +44,6 @@
 #
 d none usr/lib/fs/nfs/sparcv9 755 root sys
 f none usr/lib/fs/nfs/sparcv9/libshare_nfs.so.1 755 root bin
+d none usr/lib/reparse/sparcv9 755 root sys
+f none usr/lib/reparse/sparcv9/libnfs_basic.so.1 755 root bin
+s none usr/lib/reparse/sparcv9/libnfs_basic.so=./libnfs_basic.so.1
--- a/usr/src/uts/common/fs/fs_subr.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/fs_subr.c	Wed Dec 09 17:27:22 2009 -0600
@@ -938,7 +938,7 @@
 reparse_kderef(const char *svc_type, const char *svc_data, char *buf,
     size_t *bufsize)
 {
-	int err, retries, need_free;
+	int err, retries, need_free, retried_doorhd;
 	size_t dlen, res_len;
 	char *darg;
 	door_arg_t door_args;
@@ -975,6 +975,7 @@
 	door_args.rsize = *bufsize;
 
 	/* do the door_call */
+	retried_doorhd = 0;
 	retries = 0;
 	door_ki_hold(rp_door);
 	while ((err = door_ki_upcall_limited(rp_door, &door_args,
@@ -987,10 +988,23 @@
 		} else if (err == EBADF) {
 			/* door server goes away... */
 			reparse_door_reset_handle();
+
+			if (retried_doorhd == 0) {
+				door_ki_rele(rp_door);
+				retried_doorhd++;
+				rp_door = reparse_door_get_handle();
+				if (rp_door != NULL) {
+					door_ki_hold(rp_door);
+					continue;
+				}
+			}
 		}
 		break;
 	}
-	door_ki_rele(rp_door);
+
+	if (rp_door)
+		door_ki_rele(rp_door);
+
 	if (need_free)
 		kmem_free(darg, dlen);		/* done with args buffer */
 
--- a/usr/src/uts/common/fs/nfs/nfs3_srv.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs3_srv.c	Wed Dec 09 17:27:22 2009 -0600
@@ -112,6 +112,10 @@
 	error = rfs4_delegated_getattr(vp, &va, 0, cr);
 
 	if (!error) {
+		/* Lie about the object type for a referral */
+		if (vn_is_nfs_reparse(vp, cr))
+			va.va_type = VLNK;
+
 		/* overflow error if time or size is out of range */
 		error = vattr_to_fattr3(&va, &resp->resok.obj_attributes);
 		if (error)
@@ -792,6 +796,7 @@
 	char *data;
 	struct sockaddr *ca;
 	char *name = NULL;
+	int is_referral = 0;
 
 	vap = NULL;
 
@@ -817,7 +822,11 @@
 	vap = &va;
 #endif
 
-	if (vp->v_type != VLNK) {
+	/* We lied about the object type for a referral */
+	if (vn_is_nfs_reparse(vp, cr))
+		is_referral = 1;
+
+	if (vp->v_type != VLNK && !is_referral) {
 		resp->status = NFS3ERR_INVAL;
 		goto out1;
 	}
@@ -845,16 +854,39 @@
 
 	data = kmem_alloc(MAXPATHLEN + 1, KM_SLEEP);
 
-	iov.iov_base = data;
-	iov.iov_len = MAXPATHLEN;
-	uio.uio_iov = &iov;
-	uio.uio_iovcnt = 1;
-	uio.uio_segflg = UIO_SYSSPACE;
-	uio.uio_extflg = UIO_COPY_CACHED;
-	uio.uio_loffset = 0;
-	uio.uio_resid = MAXPATHLEN;
-
-	error = VOP_READLINK(vp, &uio, cr, NULL);
+	if (is_referral) {
+		char *s;
+		size_t strsz;
+
+		/* Get an artificial symlink based on a referral */
+		s = build_symlink(vp, cr, &strsz);
+		global_svstat_ptr[3][NFS_REFERLINKS].value.ui64++;
+		DTRACE_PROBE2(nfs3serv__func__referral__reflink,
+		    vnode_t *, vp, char *, s);
+		if (s == NULL)
+			error = EINVAL;
+		else {
+			error = 0;
+			(void) strlcpy(data, s, MAXPATHLEN + 1);
+			kmem_free(s, strsz);
+		}
+
+	} else {
+
+		iov.iov_base = data;
+		iov.iov_len = MAXPATHLEN;
+		uio.uio_iov = &iov;
+		uio.uio_iovcnt = 1;
+		uio.uio_segflg = UIO_SYSSPACE;
+		uio.uio_extflg = UIO_COPY_CACHED;
+		uio.uio_loffset = 0;
+		uio.uio_resid = MAXPATHLEN;
+
+		error = VOP_READLINK(vp, &uio, cr, NULL);
+
+		if (!error)
+			*(data + MAXPATHLEN - uio.uio_resid) = '\0';
+	}
 
 #ifdef DEBUG
 	if (rfs3_do_post_op_attr) {
@@ -866,6 +898,9 @@
 	va.va_mask = AT_ALL;
 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
 #endif
+	/* Lie about object type again just to be consistent */
+	if (is_referral && vap != NULL)
+		vap->va_type = VLNK;
 
 #if 0 /* notyet */
 	/*
@@ -884,8 +919,6 @@
 		goto out;
 	}
 
-	*(data + MAXPATHLEN - uio.uio_resid) = '\0';
-
 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
 	name = nfscmd_convname(ca, exi, data, NFSCMD_CONV_OUTBOUND,
 	    MAXPATHLEN + 1);
@@ -3863,6 +3896,10 @@
 		nva.va_mask = AT_ALL;
 		nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL : &nva;
 #endif
+		/* Lie about the object type for a referral */
+		if (vn_is_nfs_reparse(nvp, cr))
+			nvap->va_type = VLNK;
+
 		vattr_to_post_op_attr(nvap, &infop[i].attr);
 
 #ifdef DEBUG
--- a/usr/src/uts/common/fs/nfs/nfs4_attr.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_attr.c	Wed Dec 09 17:27:22 2009 -0600
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.
- * All rights reserved.  Use is subject to license terms.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/time.h>
 #include <sys/systm.h>
 
@@ -107,7 +104,7 @@
 	case AT_ATIME:
 		if ((ntovp->nval != FATTR4_TIME_ACCESS) ||
 		    (*errorp = nfs4_time_vton(&vap->va_ctime,
-					&nap->time_access))) {
+		    &nap->time_access))) {
 			/*
 			 * either asked for FATTR4_TIME_ACCESS_SET -
 			 *	not used for setattr
@@ -119,7 +116,7 @@
 	case AT_MTIME:
 		if ((ntovp->nval != FATTR4_TIME_MODIFY) ||
 		    (*errorp = nfs4_time_vton(&vap->va_mtime,
-					&nap->time_modify))) {
+		    &nap->time_modify))) {
 			/*
 			 * either asked for FATTR4_TIME_MODIFY_SET -
 			 *	not used for setattr
@@ -130,7 +127,7 @@
 		break;
 	case AT_CTIME:
 		if (*errorp = nfs4_time_vton(&vap->va_ctime,
-					&nap->time_metadata)) {
+		    &nap->time_metadata)) {
 			/*
 			 * system time invalid for otw transfers
 			 */
@@ -196,7 +193,7 @@
 	case AT_ATIME:
 		if ((ntovp->nval != FATTR4_TIME_ACCESS_SET) ||
 		    (*errorp = timestruc_to_settime4(&vap->va_atime,
-				&nap->time_access_set, flags))) {
+		    &nap->time_access_set, flags))) {
 			/* FATTR4_TIME_ACCESS - not used for verify */
 			retval = FALSE;
 		}
@@ -204,7 +201,7 @@
 	case AT_MTIME:
 		if ((ntovp->nval != FATTR4_TIME_MODIFY_SET) ||
 		    (*errorp = timestruc_to_settime4(&vap->va_mtime,
-				&nap->time_modify_set, flags))) {
+		    &nap->time_modify_set, flags))) {
 			/* FATTR4_TIME_MODIFY - not used for verify */
 			retval = FALSE;
 		}
@@ -260,7 +257,7 @@
 	fattrp->attrlist4_len = 0;
 	fattrp->attrlist4 = NULL;
 	na = kmem_zalloc(sizeof (union nfs4_attr_u) * nfs4_ntov_map_size,
-			KM_SLEEP);
+	    KM_SLEEP);
 
 	if (op == OP_SETATTR || op == OP_CREATE || op == OP_OPEN) {
 		/*
@@ -341,8 +338,8 @@
 			xdr_size += nfs4_ntov_map[i].xdr_size;
 			if ((nfs4_ntov_map[i].nval == FATTR4_TIME_ACCESS_SET ||
 			    nfs4_ntov_map[i].nval == FATTR4_TIME_MODIFY_SET) &&
-				attrfunc == nfs4_set_fattr4_attr &&
-				!(flags & ATTR_UTIME)) {
+			    attrfunc == nfs4_set_fattr4_attr &&
+			    !(flags & ATTR_UTIME)) {
 				xdr_size -= 3 * BYTES_PER_XDR_UNIT;
 			}
 		} else {
@@ -351,19 +348,19 @@
 			 * are AT_UID, AT_GID and FATTR4_ACL_MASK
 			 */
 			ASSERT(nfs4_ntov_map[i].vbit == AT_UID ||
-				nfs4_ntov_map[i].vbit == AT_GID ||
-				nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK);
+			    nfs4_ntov_map[i].vbit == AT_GID ||
+			    nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK);
 			if (nfs4_ntov_map[i].vbit == AT_UID) {
 				uid_attr = attrcnt;
 				xdr_size += BYTES_PER_XDR_UNIT;	/* length */
 				xdr_size +=
-					RNDUP(na[attrcnt].owner.utf8string_len);
+				    RNDUP(na[attrcnt].owner.utf8string_len);
 			} else if (nfs4_ntov_map[i].vbit == AT_GID) {
 				gid_attr = attrcnt;
 				xdr_size += BYTES_PER_XDR_UNIT;	/* length */
 				xdr_size +=
 				    RNDUP(
-					na[attrcnt].owner_group.utf8string_len);
+				    na[attrcnt].owner_group.utf8string_len);
 			} else if (nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK) {
 				nfsace4 *tmpacl = (nfsace4 *)vsap->vsa_aclentp;
 
@@ -411,7 +408,7 @@
 	for (i = 0; i < attrcnt; i++) {
 		if ((*nfs4_ntov_map[amap[i]].xfunc)(&xdr, &na[i]) == FALSE) {
 			cmn_err(CE_WARN, "vattr_to_fattr4: xdr encode of "
-				"attribute failed\n");
+			    "attribute failed\n");
 			error = EINVAL;
 			break;
 		}
@@ -422,11 +419,11 @@
 	 */
 	if (uid_attr != -1 && na[uid_attr].owner.utf8string_val != NULL) {
 		kmem_free(na[uid_attr].owner.utf8string_val,
-				na[uid_attr].owner.utf8string_len);
+		    na[uid_attr].owner.utf8string_len);
 	}
 	if (gid_attr != -1 && na[gid_attr].owner_group.utf8string_val != NULL) {
 		kmem_free(na[gid_attr].owner_group.utf8string_val,
-				na[gid_attr].owner_group.utf8string_len);
+		    na[gid_attr].owner_group.utf8string_len);
 	}
 
 	/* xdrmem_destroy(&xdrs); */	/* NO-OP */
@@ -770,7 +767,7 @@
 		FATTR4_TIME_MODIFY_SET, 4 * BYTES_PER_XDR_UNIT, xdr_settime4,
 		NULL, "fattr4_time_modify_set" },
 
-	{ FATTR4_MOUNTED_ON_FILEID_MASK, 0, FALSE, FALSE,
+	{ FATTR4_MOUNTED_ON_FILEID_MASK, AT_NODEID, FALSE, FALSE,
 		FATTR4_MOUNTED_ON_FILEID, 2 * BYTES_PER_XDR_UNIT,
 		xdr_u_longlong_t,
 		NULL, "fattr4_mounted_on_fileid" },
--- a/usr/src/uts/common/fs/nfs/nfs4_callback.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_callback.c	Wed Dec 09 17:27:22 2009 -0600
@@ -1493,7 +1493,8 @@
 			(void) nfs4_start_recovery(&e, mi, vp,
 			    NULL, &rp->r_deleg_stateid,
 			    lost_rqst.lr_op == OP_DELEGRETURN ?
-			    &lost_rqst : NULL, OP_DELEGRETURN, NULL);
+			    &lost_rqst : NULL, OP_DELEGRETURN, NULL,
+			    NULL, NULL);
 			nfs4_end_op(mi, vp, NULL, &recov_state, needrecov);
 			break;
 		}
@@ -1517,7 +1518,8 @@
 			(void) nfs4_start_recovery(&e, mi, vp,
 			    NULL, &rp->r_deleg_stateid,
 			    lost_rqst.lr_op == OP_DELEGRETURN ?
-			    &lost_rqst : NULL, OP_DELEGRETURN, NULL);
+			    &lost_rqst : NULL, OP_DELEGRETURN, NULL,
+			    NULL, NULL);
 		} else {
 			nfs4delegreturn_cleanup_impl(rp, NULL, ncg);
 			done = TRUE;
@@ -1956,7 +1958,7 @@
 			 * thread will take it from here.
 			 */
 			(void) nfs4_start_recovery(&e, mi, vp, NULL, NULL,
-			    NULL, OP_OPEN, NULL);
+			    NULL, OP_OPEN, NULL, NULL, NULL);
 			open_stream_rele(osp, rp);
 			*recovp = TRUE;
 			break;
--- a/usr/src/uts/common/fs/nfs/nfs4_client.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_client.c	Wed Dec 09 17:27:22 2009 -0600
@@ -837,7 +837,7 @@
 
 	if (nfs4_needs_recovery(&e, FALSE, vp->v_vfsp)) {
 		if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL,
-		    NULL, OP_GETATTR, NULL) == FALSE)  {
+		    NULL, OP_GETATTR, NULL, NULL, NULL) == FALSE)  {
 			nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_GETATTR,
 			    &recov_state, 1);
 			goto recov_retry;
@@ -1027,7 +1027,7 @@
 		    "nfs4_attr_otw: initiating recovery\n"));
 
 		abort = nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL,
-		    NULL, OP_GETATTR, NULL);
+		    NULL, OP_GETATTR, NULL, NULL, NULL);
 		nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_GETATTR, &recov_state,
 		    needrecov);
 		if (!e.error) {
@@ -3492,7 +3492,7 @@
 		    "nfs4renew: initiating recovery\n"));
 
 		if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL,
-		    OP_RENEW, NULL) == FALSE) {
+		    OP_RENEW, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
 			VFS_RELE(mi->mi_vfsp);
 			if (!e.error)
--- a/usr/src/uts/common/fs/nfs/nfs4_client_debug.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_client_debug.c	Wed Dec 09 17:27:22 2009 -0600
@@ -303,6 +303,17 @@
 		if (rp2 != NULL && rp2->r_svnode.sv_name != NULL)
 			ep->re_char2 = fn_path(rp2->r_svnode.sv_name);
 		break;
+	case RE_REFERRAL:
+		/* server we're being referred to */
+		if (server1 != NULL) {
+			len = strlen(server1);
+			ep->re_char1 = kmem_alloc(len + 1, KM_SLEEP);
+			bcopy(server1, ep->re_char1, len);
+			ep->re_char1[len] = '\0';
+		} else {
+			ep->re_char1 = NULL;
+		}
+		break;
 	default:
 		break;
 	}
@@ -391,6 +402,8 @@
 		case RE_SIGLOST:
 		case RE_SIGLOST_NO_DUMP:
 		case RE_LOST_STATE_BAD_OP:
+		case RE_REFERRAL:
+			/* placeholder */
 			return (0);
 		default:
 			return (0);
@@ -1039,6 +1052,18 @@
 		    ep->re_char1, (void *)ep->re_rp1, ep->re_char2,
 		    (void *)ep->re_rp2);
 		break;
+	case RE_REFERRAL:
+		if (ep->re_char1)
+			zcmn_err(zoneid, CE_NOTE,
+			    "![NFS4][Server: %s][Mntpt: %s]"
+			    "being referred from %s to %s", msg->msg_srv,
+			    msg->msg_mntpt, msg->msg_srv, ep->re_char1);
+		else
+			zcmn_err(zoneid, CE_NOTE,
+			    "![NFS4][Server: %s][Mntpt: %s]"
+			    "NFS4: being referred from %s to unknown server",
+			    msg->msg_srv, msg->msg_mntpt, msg->msg_srv);
+		break;
 	default:
 		zcmn_err(zoneid, CE_WARN,
 		    "!queue_print_event: illegal event %d", ep->re_type);
@@ -1186,6 +1211,7 @@
 	case RE_UNEXPECTED_ERRNO:
 	case RE_UNEXPECTED_STATUS:
 	case RE_LOST_STATE_BAD_OP:
+	case RE_REFERRAL:
 		return (1);
 	default:
 		return (0);
--- a/usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c	Wed Dec 09 17:27:22 2009 -0600
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * NFS Version 4 client side SECINFO code.
  */
@@ -749,7 +747,7 @@
 		    "nfs4secinfo_otw: recovery in a recovery thread\n"));
 
 		abort = nfs4_start_recovery(&e, mi, NULL,
-		    NULL, NULL, NULL, OP_SECINFO, NULL);
+		    NULL, NULL, NULL, OP_SECINFO, NULL, NULL, NULL);
 		if (!e.error) {
 			e.error = geterrno4(res.status);
 			(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
--- a/usr/src/uts/common/fs/nfs/nfs4_common.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_common.c	Wed Dec 09 17:27:22 2009 -0600
@@ -19,13 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-
 /*
  * This is the loadable module wrapper.
  */
@@ -164,28 +161,28 @@
 			mi->mi_acregmin = SEC2HR(ACMINMAX);
 		else
 			mi->mi_acregmin = SEC2HR(MIN(STRUCT_FGET(args,
-							acregmin), ACMINMAX));
+			    acregmin), ACMINMAX));
 	}
 	if (flags & NFSMNT_ACREGMAX) {
 		if (STRUCT_FGET(args, acregmax) < 0)
 			mi->mi_acregmax = SEC2HR(ACMAXMAX);
 		else
 			mi->mi_acregmax = SEC2HR(MIN(STRUCT_FGET(args,
-							acregmax), ACMAXMAX));
+			    acregmax), ACMAXMAX));
 	}
 	if (flags & NFSMNT_ACDIRMIN) {
 		if (STRUCT_FGET(args, acdirmin) < 0)
 			mi->mi_acdirmin = SEC2HR(ACMINMAX);
 		else
 			mi->mi_acdirmin = SEC2HR(MIN(STRUCT_FGET(args,
-							acdirmin), ACMINMAX));
+			    acdirmin), ACMINMAX));
 	}
 	if (flags & NFSMNT_ACDIRMAX) {
 		if (STRUCT_FGET(args, acdirmax) < 0)
 			mi->mi_acdirmax = SEC2HR(ACMAXMAX);
 		else
 			mi->mi_acdirmax = SEC2HR(MIN(STRUCT_FGET(args,
-							acdirmax), ACMAXMAX));
+			    acdirmax), ACMAXMAX));
 	}
 
 	return (0);
@@ -437,6 +434,8 @@
 		return ("NR_LOST_LOCK");
 	case NR_LOST_STATE_RQST:
 		return ("NR_LOST_STATE_RQST");
+	case NR_MOVED:
+		return ("NR_MOVED");
 	default:
 		(void) snprintf(buf, 40, "Unknown, code %d", (int)what);
 		return (buf);
--- a/usr/src/uts/common/fs/nfs/nfs4_idmap.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_idmap.c	Wed Dec 09 17:27:22 2009 -0600
@@ -19,11 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 /*
  * There are well defined policies for mapping uid and gid values to and
@@ -134,7 +133,7 @@
 /*
  * Truly global modular globals
  */
-static zone_key_t		nfsidmap_zone_key;
+zone_key_t			nfsidmap_zone_key;
 static list_t			nfsidmap_globals_list;
 static kmutex_t			nfsidmap_globals_lock;
 static kmem_cache_t		*nfsidmap_cache;
--- a/usr/src/uts/common/fs/nfs/nfs4_recovery.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_recovery.c	Wed Dec 09 17:27:22 2009 -0600
@@ -39,6 +39,11 @@
 #include <sys/disp.h>
 #include <sys/list.h>
 #include <sys/sdt.h>
+#include <sys/mount.h>
+#include <sys/door.h>
+#include <nfs/nfssys.h>
+#include <nfs/nfsid_map.h>
+#include <nfs/nfs4_idmap_impl.h>
 
 extern r4hashq_t *rtable4;
 
@@ -69,6 +74,8 @@
 	nfs4_error_t rc_orig_errors;	/* original errors causing recovery */
 	int rc_error;
 	nfs4_bseqid_entry_t *rc_bseqid_rqst;
+	vnode_t *rc_moved_vp;
+	char *rc_moved_nm;
 } recov_info_t;
 
 /*
@@ -135,6 +142,8 @@
 int nfs4_srvmnt_debug = 0;
 #endif
 
+extern zone_key_t	nfs4clnt_zone_key;
+
 /* forward references, in alphabetic order */
 static void close_after_open_resend(vnode_t *, cred_t *, uint32_t,
 	nfs4_error_t *);
@@ -169,7 +178,7 @@
 	nfs4_server_t *);
 static void save_bseqid_rqst(nfs4_bseqid_entry_t *, recov_info_t *);
 static void start_recovery(recov_info_t *, mntinfo4_t *, vnode_t *, vnode_t *,
-	nfs4_server_t *);
+	nfs4_server_t *, vnode_t *, char *);
 static void start_recovery_action(nfs4_recov_t, bool_t, mntinfo4_t *, vnode_t *,
 	vnode_t *);
 static int wait_for_recovery(mntinfo4_t *, nfs4_op_hint_t);
@@ -330,7 +339,7 @@
 bool_t
 nfs4_start_recovery(nfs4_error_t *ep, mntinfo4_t *mi, vnode_t *vp1,
     vnode_t *vp2, stateid4 *sid, nfs4_lost_rqst_t *lost_rqstp, nfs_opnum4 op,
-    nfs4_bseqid_entry_t *bsep)
+    nfs4_bseqid_entry_t *bsep, vnode_t *moved_vp, char *moved_nm)
 {
 	recov_info_t *recovp;
 	nfs4_server_t *sp;
@@ -371,7 +380,7 @@
 	errs_to_action(recovp, sp, mi, sid, lost_rqstp, gone, op, bsep);
 	if (sp != NULL)
 		mutex_exit(&sp->s_lock);
-	start_recovery(recovp, mi, vp1, vp2, sp);
+	start_recovery(recovp, mi, vp1, vp2, sp, moved_vp, moved_nm);
 	if (sp != NULL)
 		nfs4_server_rele(sp);
 	return (FALSE);
@@ -397,12 +406,13 @@
 	recovp->rc_action = what;
 	recovp->rc_srv_reboot = reboot;
 	recovp->rc_error = EIO;
-	start_recovery(recovp, mi, vp1, vp2, NULL);
+	start_recovery(recovp, mi, vp1, vp2, NULL, NULL, NULL);
 }
 
 static void
 start_recovery(recov_info_t *recovp, mntinfo4_t *mi,
-    vnode_t *vp1, vnode_t *vp2, nfs4_server_t *sp)
+    vnode_t *vp1, vnode_t *vp2, nfs4_server_t *sp,
+    vnode_t *moved_vp, char *moved_nm)
 {
 	NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
 	    "start_recovery: mi %p, what %s", (void*)mi,
@@ -563,7 +573,6 @@
 	case NR_LOST_LOCK:
 		nfs4_enqueue_lost_rqst(recovp, mi);
 		break;
-
 	default:
 		nfs4_queue_event(RE_UNEXPECTED_ACTION, mi, NULL,
 		    recovp->rc_action, NULL, NULL, 0, NULL, 0, TAG_NONE,
@@ -607,6 +616,8 @@
 		ASSERT(VTOMI4(vp2) == mi);
 		VN_HOLD(recovp->rc_vp2);
 	}
+	recovp->rc_moved_vp = moved_vp;
+	recovp->rc_moved_nm = moved_nm;
 
 	(void) zthread_create(NULL, 0, nfs4_recov_thread, recovp, 0,
 	    minclsyspri);
@@ -1937,7 +1948,7 @@
 		needrecov = FALSE;
 	if (needrecov) {
 		(void) nfs4_start_recovery(&e, mi, vp,
-		    NULL, NULL, NULL, OP_LOOKUP, NULL);
+		    NULL, NULL, NULL, OP_LOOKUP, NULL, NULL, NULL);
 	} else if (e.error != EINTR &&
 	    !NFS4_FRC_UNMT_ERR(e.error, mi->mi_vfsp) &&
 	    (e.error != 0 || e.stat != NFS4_OK)) {
@@ -2012,7 +2023,7 @@
 	needrecov = nfs4_needs_recovery(&e, FALSE, vp->v_vfsp);
 	if (needrecov && (e.error != 0 || e.stat != NFS4ERR_STALE)) {
 		(void) nfs4_start_recovery(&e, mi, vp,
-		    NULL, NULL, NULL, OP_GETATTR, NULL);
+		    NULL, NULL, NULL, OP_GETATTR, NULL, NULL, NULL);
 		NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
 		    "recov_stale: error=%d, stat=%d seen on rp %s",
 		    e.error, e.stat, rnode4info(rp)));
@@ -2062,7 +2073,7 @@
 			if (needrecov) {
 				(void) nfs4_start_recovery(&e,
 				    mi, rootvp, NULL, NULL, NULL,
-				    OP_GETATTR, NULL);
+				    OP_GETATTR, NULL, NULL, NULL);
 				NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
 				    "recov_stale: error=%d, stat=%d seen "
 				    "on rp %s", e.error, e.stat,
@@ -2491,7 +2502,7 @@
 		nfs4_remap_root(mi, &e, 0);
 		if (nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp)) {
 			(void) nfs4_start_recovery(&e, mi, NULL,
-			    NULL, NULL, NULL, OP_LOOKUP, NULL);
+			    NULL, NULL, NULL, OP_LOOKUP, NULL, NULL, NULL);
 		}
 	}
 
@@ -2561,7 +2572,7 @@
 				    mi->mi_vfsp)) {
 					(void) nfs4_start_recovery(&e, mi,
 					    rep->re_vp, NULL, NULL, NULL,
-					    OP_OPEN, NULL);
+					    OP_OPEN, NULL, NULL, NULL);
 					break;
 				}
 			}
@@ -2575,7 +2586,7 @@
 			if (nfs4_needs_recovery(&e, TRUE, mi->mi_vfsp))
 				(void) nfs4_start_recovery(&e, mi,
 				    rep->re_vp, NULL, NULL, NULL, OP_LOCK,
-				    NULL);
+				    NULL, NULL, NULL);
 			if (e.error != 0 || e.stat != NFS4_OK)
 				break;
 		}
@@ -2664,7 +2675,7 @@
 			} else {
 				(void) nfs4_start_recovery(&n4e,
 				    mi, lrp->lr_dvp, lrp->lr_vp, NULL, NULL,
-				    lrp->lr_op, NULL);
+				    lrp->lr_op, NULL, NULL, NULL);
 			}
 			return;
 		}
@@ -3122,10 +3133,10 @@
 		case NFS4ERR_LEASE_MOVED:
 			action = xxx;
 			break;
+#endif
 		case NFS4ERR_MOVED:
-			action = xxx;
+			action = NR_MOVED;
 			break;
-#endif
 		case NFS4ERR_BADHANDLE:
 			action = NR_BADHANDLE;
 			break;
--- a/usr/src/uts/common/fs/nfs/nfs4_rnode.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_rnode.c	Wed Dec 09 17:27:22 2009 -0600
@@ -381,11 +381,12 @@
 			 * creation time and it never changes for life
 			 * of the rnode.
 			 *
+			 * This stub will be for a mirror-mount, rather than
+			 * a referral (the latter also sets R4SRVSTUB).
+			 *
 			 * The stub type is also set during RO failover,
 			 * nfs4_remap_file().
 			 *
-			 * This stub will be for a mirror-mount.
-			 *
 			 * We don't bother with taking r_state_lock to
 			 * set the stub type because this is a new rnode
 			 * and we're holding the hash bucket r_lock RW_WRITER.
@@ -1769,6 +1770,52 @@
 }
 
 /*
+ * Given a filesystem id, check to see if any rnodes
+ * within this fsid reside in the rnode cache, other
+ * than one we know about.
+ *
+ * Return 1 if an rnode is found, 0 otherwise
+ */
+int
+r4find_by_fsid(mntinfo4_t *mi, fattr4_fsid *moved_fsid)
+{
+	rnode4_t *rp;
+	vnode_t *vp;
+	vfs_t *vfsp = mi->mi_vfsp;
+	fattr4_fsid *fsid;
+	int index, found = 0;
+
+	for (index = 0; index < rtable4size; index++) {
+		rw_enter(&rtable4[index].r_lock, RW_READER);
+		for (rp = rtable4[index].r_hashf;
+		    rp != (rnode4_t *)(&rtable4[index]);
+		    rp = rp->r_hashf) {
+
+			vp = RTOV4(rp);
+			if (vp->v_vfsp != vfsp)
+				continue;
+
+			/*
+			 * XXX there might be a case where a
+			 * replicated fs may have the same fsid
+			 * across two different servers. This
+			 * check isn't good enough in that case
+			 */
+			fsid = &rp->r_srv_fsid;
+			if (FATTR4_FSID_EQ(moved_fsid, fsid)) {
+				found = 1;
+				break;
+			}
+		}
+		rw_exit(&rtable4[index].r_lock);
+
+		if (found)
+			break;
+	}
+	return (found);
+}
+
+/*
  * Release the list of open instance references.
  */
 
@@ -1898,6 +1945,14 @@
 }
 
 void
+r4_stub_referral(rnode4_t *rp)
+{
+	DTRACE_PROBE1(nfs4clnt__func__referral__moved,
+	    vnode_t *, RTOV4(rp));
+	r4_stub_set(rp, NFS4_STUB_REFERRAL);
+}
+
+void
 r4_stub_none(rnode4_t *rp)
 {
 	r4_stub_set(rp, NFS4_STUB_NONE);
--- a/usr/src/uts/common/fs/nfs/nfs4_srv.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c	Wed Dec 09 17:27:22 2009 -0600
@@ -56,6 +56,8 @@
 #include <sys/ddi.h>
 #include <sys/zone.h>
 
+#include <fs/fs_reparse.h>
+
 #include <rpc/types.h>
 #include <rpc/auth.h>
 #include <rpc/rpcsec_gss.h>
@@ -82,7 +84,6 @@
 #define	RFS4_LOCK_DELAY 10	/* Milliseconds */
 static clock_t  rfs4_lock_delay = RFS4_LOCK_DELAY;
 extern struct svc_ops rdma_svc_ops;
-/* End of Tunables */
 
 static int rdma_setup_read_data4(READ4args *, READ4res *);
 
@@ -243,7 +244,7 @@
 static nfsstat4 check_open_access(uint32_t,
 				struct compound_state *, struct svc_req *);
 nfsstat4 rfs4_client_sysid(rfs4_client_t *, sysid_t *);
-void rfs4_ss_clid(rfs4_client_t *, struct svc_req *);
+void rfs4_ss_clid(rfs4_client_t *);
 
 /*
  * translation table for attrs
@@ -465,6 +466,8 @@
 
 extern size_t   strlcpy(char *dst, const char *src, size_t dstsize);
 
+extern void	rfs4_free_fs_locations4(fs_locations4 *);
+
 #ifdef	nextdp
 #undef nextdp
 #endif
@@ -1609,6 +1612,7 @@
 	resp->attrset = 0;
 
 	sarg.sbp = &sb;
+	sarg.is_referral = B_FALSE;
 	nfs4_ntov_table_init(&ntov);
 
 	status = do_rfs4_set_attrs(&resp->attrset,
@@ -2314,13 +2318,26 @@
 
 	sarg.sbp = &sb;
 	sarg.cs = cs;
+	sarg.is_referral = B_FALSE;
 
 	status = bitmap4_to_attrmask(args->attr_request, &sarg);
 	if (status == NFS4_OK) {
+
 		status = bitmap4_get_sysattrs(&sarg);
-		if (status == NFS4_OK)
+		if (status == NFS4_OK) {
+
+			/* Is this a referral? */
+			if (vn_is_nfs_reparse(cs->vp, cs->cr)) {
+				/* Older V4 Solaris client sees a link */
+				if (client_is_downrev(req))
+					sarg.vap->va_type = VLNK;
+				else
+					sarg.is_referral = B_TRUE;
+			}
+
 			status = do_rfs4_op_getattr(args->attr_request,
 			    &resp->obj_attributes, &sarg);
+		}
 	}
 	*cs->statusp = resp->status = status;
 out:
@@ -2354,6 +2371,25 @@
 		goto out;
 	}
 
+	/* check for reparse point at the share point */
+	if (cs->exi->exi_moved || vn_is_nfs_reparse(cs->exi->exi_vp, cs->cr)) {
+		/* it's all bad */
+		cs->exi->exi_moved = 1;
+		*cs->statusp = resp->status = NFS4ERR_MOVED;
+		DTRACE_PROBE2(nfs4serv__func__referral__shared__moved,
+		    vnode_t *, cs->vp, char *, "rfs4_op_getfh");
+		return;
+	}
+
+	/* check for reparse point at vp */
+	if (vn_is_nfs_reparse(cs->vp, cs->cr) && !client_is_downrev(req)) {
+		/* it's not all bad */
+		*cs->statusp = resp->status = NFS4ERR_MOVED;
+		DTRACE_PROBE2(nfs4serv__func__referral__moved,
+		    vnode_t *, cs->vp, char *, "rfs4_op_getfh");
+		return;
+	}
+
 	resp->object.nfs_fh4_val =
 	    kmem_alloc(cs->fh.nfs_fh4_len, KM_SLEEP);
 	nfs_fh4_copy(&cs->fh, &resp->object);
@@ -3688,6 +3724,7 @@
 	char *data;
 	struct sockaddr *ca;
 	char *name = NULL;
+	int is_referral;
 
 	DTRACE_NFSV4_1(op__readlink__start, struct compound_state *, cs);
 
@@ -3703,14 +3740,25 @@
 		goto out;
 	}
 
-	if (vp->v_type == VDIR) {
-		*cs->statusp = resp->status = NFS4ERR_ISDIR;
-		goto out;
-	}
-
-	if (vp->v_type != VLNK) {
-		*cs->statusp = resp->status = NFS4ERR_INVAL;
-		goto out;
+	/* Is it a referral? */
+	if (vn_is_nfs_reparse(vp, cs->cr) && client_is_downrev(req)) {
+
+		is_referral = 1;
+
+	} else {
+
+		is_referral = 0;
+
+		if (vp->v_type == VDIR) {
+			*cs->statusp = resp->status = NFS4ERR_ISDIR;
+			goto out;
+		}
+
+		if (vp->v_type != VLNK) {
+			*cs->statusp = resp->status = NFS4ERR_INVAL;
+			goto out;
+		}
+
 	}
 
 	va.va_mask = AT_MODE;
@@ -3727,16 +3775,39 @@
 
 	data = kmem_alloc(MAXPATHLEN + 1, KM_SLEEP);
 
-	iov.iov_base = data;
-	iov.iov_len = MAXPATHLEN;
-	uio.uio_iov = &iov;
-	uio.uio_iovcnt = 1;
-	uio.uio_segflg = UIO_SYSSPACE;
-	uio.uio_extflg = UIO_COPY_CACHED;
-	uio.uio_loffset = 0;
-	uio.uio_resid = MAXPATHLEN;
-
-	error = VOP_READLINK(vp, &uio, cs->cr, NULL);
+	if (is_referral) {
+		char *s;
+		size_t strsz;
+
+		/* Get an artificial symlink based on a referral */
+		s = build_symlink(vp, cs->cr, &strsz);
+		global_svstat_ptr[4][NFS_REFERLINKS].value.ui64++;
+		DTRACE_PROBE2(nfs4serv__func__referral__reflink,
+		    vnode_t *, vp, char *, s);
+		if (s == NULL)
+			error = EINVAL;
+		else {
+			error = 0;
+			(void) strlcpy(data, s, MAXPATHLEN + 1);
+			kmem_free(s, strsz);
+		}
+
+	} else {
+
+		iov.iov_base = data;
+		iov.iov_len = MAXPATHLEN;
+		uio.uio_iov = &iov;
+		uio.uio_iovcnt = 1;
+		uio.uio_segflg = UIO_SYSSPACE;
+		uio.uio_extflg = UIO_COPY_CACHED;
+		uio.uio_loffset = 0;
+		uio.uio_resid = MAXPATHLEN;
+
+		error = VOP_READLINK(vp, &uio, cs->cr, NULL);
+
+		if (!error)
+			*(data + MAXPATHLEN - uio.uio_resid) = '\0';
+	}
 
 	if (error) {
 		kmem_free((caddr_t)data, (uint_t)MAXPATHLEN + 1);
@@ -3744,8 +3815,6 @@
 		goto out;
 	}
 
-	*(data + MAXPATHLEN - uio.uio_resid) = '\0';
-
 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
 	name = nfscmd_convname(ca, cs->exi, data, NFSCMD_CONV_OUTBOUND,
 	    MAXPATHLEN  + 1);
@@ -5053,6 +5122,7 @@
 
 	*resp = 0;
 	sarg.sbp = &sb;
+	sarg.is_referral = B_FALSE;
 	nfs4_ntov_table_init(&ntov);
 	status = do_rfs4_set_attrs(resp, fattrp, cs, &sarg, &ntov,
 	    NFS4ATTR_SETIT);
@@ -5347,6 +5417,7 @@
 	}
 
 	sarg.sbp = &sb;
+	sarg.is_referral = B_FALSE;
 	nfs4_ntov_table_init(&ntov);
 	resp->status = do_rfs4_set_attrs(NULL, &args->obj_attributes, cs,
 	    &sarg, &ntov, NFS4ATTR_VERIT);
@@ -5408,6 +5479,7 @@
 		return;
 	}
 	sarg.sbp = &sb;
+	sarg.is_referral = B_FALSE;
 	nfs4_ntov_table_init(&ntov);
 	resp->status = do_rfs4_set_attrs(NULL, &args->obj_attributes, cs,
 	    &sarg, &ntov, NFS4ATTR_VERIT);
@@ -6212,6 +6284,7 @@
 	char *name = NULL;
 
 	sarg.sbp = &sb;
+	sarg.is_referral = B_FALSE;
 
 	dvp = cs->vp;
 
@@ -7806,7 +7879,8 @@
 	SETCLIENTID4args *args = &argop->nfs_argop4_u.opsetclientid;
 	SETCLIENTID4res *res = &resop->nfs_resop4_u.opsetclientid;
 	rfs4_client_t *cp, *newcp, *cp_confirmed, *cp_unconfirmed;
-	bool_t create = TRUE;
+	rfs4_clntip_t *ci;
+	bool_t create;
 	char *addr, *netid;
 	int len;
 
@@ -7816,6 +7890,27 @@
 	newcp = cp_confirmed = cp_unconfirmed = NULL;
 
 	/*
+	 * Save the caller's IP address
+	 */
+	args->client.cl_addr =
+	    (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
+
+	/*
+	 * Record if it is a Solaris client that cannot handle referrals.
+	 */
+	if (strstr(args->client.id_val, "Solaris") &&
+	    !strstr(args->client.id_val, "+referrals")) {
+		/* Add a "yes, it's downrev" record */
+		create = TRUE;
+		ci = rfs4_find_clntip(args->client.cl_addr, &create);
+		ASSERT(ci != NULL);
+		rfs4_dbe_rele(ci->ri_dbe);
+	} else {
+		/* Remove any previous record */
+		rfs4_invalidate_clntip(args->client.cl_addr);
+	}
+
+	/*
 	 * In search of an EXISTING client matching the incoming
 	 * request to establish a new client identifier at the server
 	 */
@@ -8063,7 +8158,7 @@
 	 * Record clientid in stable storage.
 	 * Must be done after server instance has been assigned.
 	 */
-	rfs4_ss_clid(cp, req);
+	rfs4_ss_clid(cp);
 
 	rfs4_dbe_unlock(cp->rc_dbe);
 
@@ -9363,3 +9458,283 @@
 	rok->wlist = wcl;
 	return (TRUE);
 }
+
+/* tunable to disable server referrals */
+int rfs4_no_referrals = 0;
+
+/*
+ * Find an NFS record in reparse point data.
+ * Returns 0 for success and <0 or an errno value on failure.
+ */
+int
+vn_find_nfs_record(vnode_t *vp, nvlist_t **nvlp, char **svcp, char **datap)
+{
+	int err;
+	char *stype, *val;
+	nvlist_t *nvl;
+	nvpair_t *curr;
+
+	if ((nvl = reparse_init()) == NULL)
+		return (-1);
+
+	if ((err = reparse_vnode_parse(vp, nvl)) != 0) {
+		reparse_free(nvl);
+		return (err);
+	}
+
+	curr = NULL;
+	while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) {
+		if ((stype = nvpair_name(curr)) == NULL) {
+			reparse_free(nvl);
+			return (-2);
+		}
+		if (strncasecmp(stype, "NFS", 3) == 0)
+			break;
+	}
+
+	if ((curr == NULL) ||
+	    (nvpair_value_string(curr, &val))) {
+		reparse_free(nvl);
+		return (-3);
+	}
+	*nvlp = nvl;
+	*svcp = stype;
+	*datap = val;
+	return (0);
+}
+
+int
+vn_is_nfs_reparse(vnode_t *vp, cred_t *cr)
+{
+	nvlist_t *nvl;
+	char *s, *d;
+
+	if (rfs4_no_referrals != 0)
+		return (B_FALSE);
+
+	if (vn_is_reparse(vp, cr, NULL) == B_FALSE)
+		return (B_FALSE);
+
+	if (vn_find_nfs_record(vp, &nvl, &s, &d) != 0)
+		return (B_FALSE);
+
+	reparse_free(nvl);
+
+	return (B_TRUE);
+}
+
+/*
+ * There is a user-level copy of this routine in ref_subr.c.
+ * Changes should be kept in sync.
+ */
+static int
+nfs4_create_components(char *path, component4 *comp4)
+{
+	int slen, plen, ncomp;
+	char *ori_path, *nxtc, buf[MAXNAMELEN];
+
+	if (path == NULL)
+		return (0);
+
+	plen = strlen(path) + 1;	/* include the terminator */
+	ori_path = path;
+	ncomp = 0;
+
+	/* count number of components in the path */
+	for (nxtc = path; nxtc < ori_path + plen; nxtc++) {
+		if (*nxtc == '/' || *nxtc == '\0' || *nxtc == '\n') {
+			if ((slen = nxtc - path) == 0) {
+				path = nxtc + 1;
+				continue;
+			}
+
+			if (comp4 != NULL) {
+				bcopy(path, buf, slen);
+				buf[slen] = '\0';
+				str_to_utf8(buf, &comp4[ncomp]);
+			}
+
+			ncomp++;	/* 1 valid component */
+			path = nxtc + 1;
+		}
+		if (*nxtc == '\0' || *nxtc == '\n')
+			break;
+	}
+
+	return (ncomp);
+}
+
+/*
+ * There is a user-level copy of this routine in ref_subr.c.
+ * Changes should be kept in sync.
+ */
+static int
+make_pathname4(char *path, pathname4 *pathname)
+{
+	int ncomp;
+	component4 *comp4;
+
+	if (pathname == NULL)
+		return (0);
+
+	if (path == NULL) {
+		pathname->pathname4_val = NULL;
+		pathname->pathname4_len = 0;
+		return (0);
+	}
+
+	/* count number of components to alloc buffer */
+	if ((ncomp = nfs4_create_components(path, NULL)) == 0) {
+		pathname->pathname4_val = NULL;
+		pathname->pathname4_len = 0;
+		return (0);
+	}
+	comp4 = kmem_zalloc(ncomp * sizeof (component4), KM_SLEEP);
+
+	/* copy components into allocated buffer */
+	ncomp = nfs4_create_components(path, comp4);
+
+	pathname->pathname4_val = comp4;
+	pathname->pathname4_len = ncomp;
+
+	return (ncomp);
+}
+
+#define	xdr_fs_locations4 xdr_fattr4_fs_locations
+
+fs_locations4 *
+fetch_referral(vnode_t *vp, cred_t *cr)
+{
+	nvlist_t *nvl;
+	char *stype, *sdata;
+	fs_locations4 *result;
+	char buf[1024];
+	size_t bufsize;
+	XDR xdr;
+	int err;
+
+	/*
+	 * Check attrs to ensure it's a reparse point
+	 */
+	if (vn_is_reparse(vp, cr, NULL) == B_FALSE)
+		return (NULL);
+
+	/*
+	 * Look for an NFS record and get the type and data
+	 */
+	if (vn_find_nfs_record(vp, &nvl, &stype, &sdata) != 0)
+		return (NULL);
+
+	/*
+	 * With the type and data, upcall to get the referral
+	 */
+	bufsize = sizeof (buf);
+	bzero(buf, sizeof (buf));
+	err = reparse_kderef((const char *)stype, (const char *)sdata,
+	    buf, &bufsize);
+	reparse_free(nvl);
+
+	DTRACE_PROBE4(nfs4serv__func__referral__upcall,
+	    char *, stype, char *, sdata, char *, buf, int, err);
+	if (err) {
+		cmn_err(CE_NOTE,
+		    "reparsed daemon not running: unable to get referral (%d)",
+		    err);
+		return (NULL);
+	}
+
+	/*
+	 * We get an XDR'ed record back from the kderef call
+	 */
+	xdrmem_create(&xdr, buf, bufsize, XDR_DECODE);
+	result = kmem_alloc(sizeof (fs_locations4), KM_SLEEP);
+	err = xdr_fs_locations4(&xdr, result);
+	XDR_DESTROY(&xdr);
+	if (err != TRUE) {
+		DTRACE_PROBE1(nfs4serv__func__referral__upcall__xdrfail,
+		    int, err);
+		return (NULL);
+	}
+
+	/*
+	 * Look at path to recover fs_root, ignoring the leading '/'
+	 */
+	(void) make_pathname4(vp->v_path, &result->fs_root);
+
+	return (result);
+}
+
+char *
+build_symlink(vnode_t *vp, cred_t *cr, size_t *strsz)
+{
+	fs_locations4 *fsl;
+	fs_location4 *fs;
+	char *server, *path, *symbuf;
+	static char *prefix = "/net/";
+	int i, size, npaths;
+	uint_t len;
+
+	/* Get the referral */
+	if ((fsl = fetch_referral(vp, cr)) == NULL)
+		return (NULL);
+
+	/* Deal with only the first location and first server */
+	fs = &fsl->locations_val[0];
+	server = utf8_to_str(&fs->server_val[0], &len, NULL);
+	if (server == NULL) {
+		rfs4_free_fs_locations4(fsl);
+		kmem_free(fsl, sizeof (fs_locations4));
+		return (NULL);
+	}
+
+	/* Figure out size for "/net/" + host + /path/path/path + NULL */
+	size = strlen(prefix) + len;
+	for (i = 0; i < fs->rootpath.pathname4_len; i++)
+		size += fs->rootpath.pathname4_val[i].utf8string_len + 1;
+
+	/* Allocate the symlink buffer and fill it */
+	symbuf = kmem_zalloc(size, KM_SLEEP);
+	(void) strcat(symbuf, prefix);
+	(void) strcat(symbuf, server);
+	kmem_free(server, len);
+
+	npaths = 0;
+	for (i = 0; i < fs->rootpath.pathname4_len; i++) {
+		path = utf8_to_str(&fs->rootpath.pathname4_val[i], &len, NULL);
+		if (path == NULL)
+			continue;
+		(void) strcat(symbuf, "/");
+		(void) strcat(symbuf, path);
+		npaths++;
+		kmem_free(path, len);
+	}
+
+	rfs4_free_fs_locations4(fsl);
+	kmem_free(fsl, sizeof (fs_locations4));
+
+	if (strsz != NULL)
+		*strsz = size;
+	return (symbuf);
+}
+
+/*
+ * Check to see if we have a downrev Solaris client, so that we
+ * can send it a symlink instead of a referral.
+ */
+int
+client_is_downrev(struct svc_req *req)
+{
+	struct sockaddr *ca;
+	rfs4_clntip_t *ci;
+	bool_t create = FALSE;
+	int is_downrev;
+
+	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
+	ASSERT(ca);
+	ci = rfs4_find_clntip(ca, &create);
+	if (ci == NULL)
+		return (0);
+	is_downrev = ci->ri_no_referrals;
+	rfs4_dbe_rele(ci->ri_dbe);
+	return (is_downrev);
+}
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c	Wed Dec 09 17:27:22 2009 -0600
@@ -29,6 +29,9 @@
 #include <nfs/export.h>
 #include <nfs/nfs4.h>
 #include <sys/ddi.h>
+#include <sys/door.h>
+#include <sys/sdt.h>
+#include <nfs/nfssys.h>
 
 void	rfs4_init_compound_state(struct compound_state *);
 
@@ -141,6 +144,7 @@
 	sarg.flag = 0;
 	sarg.rdattr_error = NFS4_OK;
 	sarg.rdattr_error_req = FALSE;
+	sarg.is_referral = B_FALSE;
 
 	rfs4_ntov_init();
 
@@ -634,7 +638,10 @@
 			error = EINVAL;
 		break;		/* this attr is supported */
 	case NFS4ATTR_GETIT:
-		if (sarg->cs->exi->exi_volatile_dev) {
+		if (sarg->is_referral) {
+			na->fsid.major = 1;
+			na->fsid.minor = 0;
+		} else if (sarg->cs->exi->exi_volatile_dev) {
 			pmaj[0] = sarg->cs->exi->exi_fsid.val[0];
 			pmaj[1] = sarg->cs->exi->exi_fsid.val[1];
 			na->fsid.minor = 0;
@@ -647,7 +654,11 @@
 		error = EINVAL;
 		break;
 	case NFS4ATTR_VERIT:
-		if (sarg->cs->exi->exi_volatile_dev) {
+		if (sarg->is_referral) {
+			if (na->fsid.major != 1 ||
+			    na->fsid.minor != 0)
+				error = -1;
+		} else if (sarg->cs->exi->exi_volatile_dev) {
 			if (pmaj[0] != sarg->cs->exi->exi_fsid.val[0] ||
 			    pmaj[1] != sarg->cs->exi->exi_fsid.val[1] ||
 			    na->fsid.minor != 0)
@@ -1495,12 +1506,109 @@
 	return (error);
 }
 
+static void
+rfs4_free_pathname4(pathname4 *pn4)
+{
+	int i, len;
+	utf8string *utf8s;
+
+	if (pn4 == NULL || (len = pn4->pathname4_len) == 0 ||
+	    (utf8s = pn4->pathname4_val) == NULL)
+		return;
+
+	for (i = 0; i < len; i++, utf8s++) {
+		if (utf8s->utf8string_val == NULL ||
+		    utf8s->utf8string_len == 0)
+			continue;
+
+		kmem_free(utf8s->utf8string_val, utf8s->utf8string_len);
+		utf8s->utf8string_val = NULL;
+	}
+
+	kmem_free(pn4->pathname4_val,
+	    sizeof (utf8string) * pn4->pathname4_len);
+	pn4->pathname4_val = 0;
+}
+
+static void
+rfs4_free_fs_location4(fs_location4 *fsl4)
+{
+	if (fsl4 == NULL)
+		return;
+
+	rfs4_free_pathname4((pathname4 *)&fsl4->server_len);
+	rfs4_free_pathname4(&fsl4->rootpath);
+}
+
+void
+rfs4_free_fs_locations4(fs_locations4 *fsls4)
+{
+	int i, len;
+	fs_location4 *fsl4;
+
+	if (fsls4 == NULL)
+		return;
+
+	/* free fs_root */
+	rfs4_free_pathname4(&fsls4->fs_root);
+
+	if ((len = fsls4->locations_len) == 0 ||
+	    (fsl4 = fsls4->locations_val) == NULL)
+		return;
+
+	/* free fs_location4 */
+	for (i = 0; i < len; i++) {
+		rfs4_free_fs_location4(fsl4);
+		fsl4++;
+	}
+
+	kmem_free(fsls4->locations_val, sizeof (fs_location4) * len);
+	fsls4->locations_val = NULL;
+}
+
 /* ARGSUSED */
 static int
 rfs4_fattr4_fs_locations(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
 	union nfs4_attr_u *na)
 {
-	return (ENOTSUP);
+	int error = 0;
+	fs_locations4 *fsl;
+
+	if (RFS4_MANDATTR_ONLY)
+		return (ENOTSUP);
+
+	switch (cmd) {
+	case NFS4ATTR_SUPPORTED:
+		if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT)
+			error = EINVAL;
+		break;  /* this attr is supported */
+
+	case NFS4ATTR_GETIT:
+		fsl = fetch_referral(sarg->cs->vp, sarg->cs->cr);
+		if (fsl == NULL)
+			error = EINVAL;
+		else {
+			na->fs_locations = *fsl;
+			kmem_free(fsl, sizeof (fs_locations4));
+		}
+		global_svstat_ptr[4][NFS_REFERRALS].value.ui64++;
+		break;
+
+	case NFS4ATTR_FREEIT:
+		if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT)
+			error = EINVAL;
+		rfs4_free_fs_locations4(&na->fs_locations);
+		break;
+
+	case NFS4ATTR_SETIT:
+	case NFS4ATTR_VERIT:
+		/*
+		 * read-only attr
+		 */
+		error = EINVAL;
+		break;
+	}
+	return (error);
 }
 
 /* ARGSUSED */
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c	Wed Dec 09 17:27:22 2009 -0600
@@ -97,7 +97,6 @@
 	0, NF4REG, NF4DIR, NF4BLK, NF4CHR, NF4LNK, NF4FIFO, 0, 0, NF4SOCK, 0
 };
 
-
 int
 nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp,
 		struct exportinfo **exi, struct svc_req *req,
@@ -117,8 +116,20 @@
 	    NULL, NULL, NULL))
 		return (error);
 
+	/*
+	 * If the directory is a referral point, don't return the
+	 * attrs, instead set rdattr_error to MOVED.
+	 */
+	if (vn_is_nfs_reparse(vp, cs->cr) && !client_is_downrev(req)) {
+		VN_RELE(vp);
+		DTRACE_PROBE2(nfs4serv__func__referral__moved,
+		    vnode_t *, vp, char *, "nfs4_readdir_getvp");
+		return (NFS4ERR_MOVED);
+	}
+
 	/* Is this object mounted upon? */
 	ismntpt = vn_ismntpt(vp);
+
 	/*
 	 * Nothing more to do if object is not a mount point or
 	 * a possible LOFS shadow of an LOFS mount (which won't
@@ -141,6 +152,13 @@
 			VN_RELE(pre_tvp);
 			return (error);
 		}
+		if (vn_is_nfs_reparse(vp, cs->cr)) {
+			VN_RELE(vp);
+			VN_RELE(pre_tvp);
+			DTRACE_PROBE2(nfs4serv__func__referral__moved,
+			    vnode_t *, vp, char *, "nfs4_readdir_getvp");
+			return (NFS4ERR_MOVED);
+		}
 	}
 
 	bzero(&fid, sizeof (fid));
@@ -818,9 +836,18 @@
 				va.va_mask = AT_ALL;
 				rddirattr_error =
 				    VOP_GETATTR(vp, &va, 0, cs->cr, NULL);
-				if (rddirattr_error)
+				if (rddirattr_error) {
 					ae = ar & (FATTR4_RDATTR_ERROR_MASK |
 					    FATTR4_MOUNTED_ON_FILEID_MASK);
+				} else {
+					/*
+					 * We may lie about the object
+					 * type for a referral
+					 */
+					if (vn_is_nfs_reparse(vp, cs->cr) &&
+					    client_is_downrev(req))
+						va.va_type = VLNK;
+				}
 			}
 		}
 
--- a/usr/src/uts/common/fs/nfs/nfs4_state.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_state.c	Wed Dec 09 17:27:22 2009 -0600
@@ -288,6 +288,8 @@
 
 static rfs4_index_t *rfs4_clientid_idx;
 static rfs4_index_t *rfs4_nfsclnt_idx;
+static rfs4_table_t *rfs4_clntip_tab;
+static rfs4_index_t *rfs4_clntip_idx;
 static rfs4_table_t *rfs4_openowner_tab;
 static rfs4_index_t *rfs4_openowner_idx;
 static rfs4_table_t *rfs4_state_tab;
@@ -330,6 +332,7 @@
 
 
 static time_t rfs4_client_cache_time = 0;
+static time_t rfs4_clntip_cache_time = 0;
 static time_t rfs4_openowner_cache_time = 0;
 static time_t rfs4_state_cache_time = 0;
 static time_t rfs4_lo_state_cache_time = 0;
@@ -348,6 +351,12 @@
 static uint32_t nfsclnt_hash(void *);
 static bool_t nfsclnt_compare(rfs4_entry_t, void *);
 static void *nfsclnt_mkkey(rfs4_entry_t);
+static bool_t rfs4_clntip_expiry(rfs4_entry_t);
+static void rfs4_clntip_destroy(rfs4_entry_t);
+static bool_t rfs4_clntip_create(rfs4_entry_t, void *);
+static uint32_t clntip_hash(void *);
+static bool_t clntip_compare(rfs4_entry_t, void *);
+static void *clntip_mkkey(rfs4_entry_t);
 static bool_t rfs4_openowner_create(rfs4_entry_t, void *);
 static void rfs4_openowner_destroy(rfs4_entry_t);
 static bool_t rfs4_openowner_expiry(rfs4_entry_t);
@@ -883,7 +892,7 @@
  * the server-generated short-hand clientid.
  */
 void
-rfs4_ss_clid(rfs4_client_t *cp, struct svc_req *req)
+rfs4_ss_clid(rfs4_client_t *cp)
 {
 	const char *kinet_ntop6(uchar_t *, char *, size_t);
 	char leaf[MAXNAMELEN], buf[INET6_ADDRSTRLEN];
@@ -896,19 +905,12 @@
 
 	buf[0] = 0;
 
-
-	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
-	if (ca == NULL) {
-		return;
-	}
+	ca = (struct sockaddr *)&cp->rc_addr;
 
 	/*
 	 * Convert the caller's IP address to a dotted string
 	 */
 	if (ca->sa_family == AF_INET) {
-
-		bcopy(svc_getrpccaller(req->rq_xprt)->buf, &cp->rc_addr,
-		    sizeof (struct sockaddr_in));
 		b = (uchar_t *)&((struct sockaddr_in *)ca)->sin_addr;
 		(void) sprintf(buf, "%03d.%03d.%03d.%03d", b[0] & 0xFF,
 		    b[1] & 0xFF, b[2] & 0xFF, b[3] & 0xFF);
@@ -916,8 +918,6 @@
 		struct sockaddr_in6 *sin6;
 
 		sin6 = (struct sockaddr_in6 *)ca;
-		bcopy(svc_getrpccaller(req->rq_xprt)->buf, &cp->rc_addr,
-		    sizeof (struct sockaddr_in6));
 		(void) kinet_ntop6((uchar_t *)&sin6->sin6_addr,
 		    buf, INET6_ADDRSTRLEN);
 	}
@@ -1257,6 +1257,22 @@
 	    clientid_compare, clientid_mkkey,
 	    FALSE);
 
+	rfs4_clntip_cache_time = 86400 * 365;	/* about a year */
+	rfs4_clntip_tab = rfs4_table_create(rfs4_server_state,
+	    "ClntIP",
+	    rfs4_clntip_cache_time,
+	    1,
+	    rfs4_clntip_create,
+	    rfs4_clntip_destroy,
+	    rfs4_clntip_expiry,
+	    sizeof (rfs4_clntip_t),
+	    TABSIZE,
+	    MAXTABSZ, 100);
+	rfs4_clntip_idx = rfs4_index_create(rfs4_clntip_tab,
+	    "client_ip", clntip_hash,
+	    clntip_compare, clntip_mkkey,
+	    TRUE);
+
 	rfs4_openowner_cache_time *= rfs4_lease_time;
 	rfs4_openowner_tab = rfs4_table_create(rfs4_server_state,
 	    "OpenOwner",
@@ -1649,6 +1665,7 @@
 {
 	rfs4_client_t *cp = (rfs4_client_t *)u_entry;
 	nfs_client_id4 *client = (nfs_client_id4 *)arg;
+	struct sockaddr *ca;
 	cid *cidp;
 	scid_confirm_verf *scvp;
 
@@ -1667,6 +1684,14 @@
 	bcopy(client->id_val, cp->rc_nfs_client.id_val, client->id_len);
 	cp->rc_nfs_client.verifier = client->verifier;
 
+	/* Copy client's IP address */
+	ca = client->cl_addr;
+	if (ca->sa_family == AF_INET)
+		bcopy(ca, &cp->rc_addr, sizeof (struct sockaddr_in));
+	else if (ca->sa_family == AF_INET6)
+		bcopy(ca, &cp->rc_addr, sizeof (struct sockaddr_in6));
+	cp->rc_nfs_client.cl_addr = (struct sockaddr *)&cp->rc_addr;
+
 	/* Init the value for the SETCLIENTID_CONFIRM verifier */
 	scvp = (scid_confirm_verf *)&cp->rc_confirm_verf;
 	scvp->cv_impl.c_id = cidp->impl_id.c_id;
@@ -1781,6 +1806,119 @@
 	}
 }
 
+static uint32_t
+clntip_hash(void *key)
+{
+	struct sockaddr *addr = key;
+	int i, len = 0;
+	uint32_t hash = 0;
+
+	if (addr->sa_family == AF_INET)
+		len = sizeof (struct sockaddr_in);
+	else if (addr->sa_family == AF_INET6)
+		len = sizeof (struct sockaddr_in6);
+
+	for (i = 0; i < len; i++) {
+		hash <<= 1;
+		hash += (uint_t)(((char *)addr)[i]);
+	}
+	return (hash);
+}
+
+static bool_t
+clntip_compare(rfs4_entry_t entry, void *key)
+{
+	rfs4_clntip_t *cp = (rfs4_clntip_t *)entry;
+	struct sockaddr *addr = key;
+	int len = 0;
+
+	if (addr->sa_family == AF_INET)
+		len = sizeof (struct sockaddr_in);
+	else if (addr->sa_family == AF_INET6)
+		len = sizeof (struct sockaddr_in6);
+	else
+		return (0);
+
+	return (bcmp(&cp->ri_addr, addr, len) == 0);
+}
+
+static void *
+clntip_mkkey(rfs4_entry_t entry)
+{
+	rfs4_clntip_t *cp = (rfs4_clntip_t *)entry;
+
+	return (&cp->ri_addr);
+}
+
+static bool_t
+rfs4_clntip_expiry(rfs4_entry_t u_entry)
+{
+	rfs4_clntip_t *cp = (rfs4_clntip_t *)u_entry;
+
+	if (rfs4_dbe_is_invalid(cp->ri_dbe))
+		return (TRUE);
+	return (FALSE);
+}
+
+/* ARGSUSED */
+static void
+rfs4_clntip_destroy(rfs4_entry_t u_entry)
+{
+}
+
+static bool_t
+rfs4_clntip_create(rfs4_entry_t u_entry, void *arg)
+{
+	rfs4_clntip_t *cp = (rfs4_clntip_t *)u_entry;
+	struct sockaddr *ca = (struct sockaddr *)arg;
+
+	/* Copy client's IP address */
+	if (ca->sa_family == AF_INET)
+		bcopy(ca, &cp->ri_addr, sizeof (struct sockaddr_in));
+	else if (ca->sa_family == AF_INET6)
+		bcopy(ca, &cp->ri_addr, sizeof (struct sockaddr_in6));
+	else
+		return (FALSE);
+	cp->ri_no_referrals = 1;
+
+	return (TRUE);
+}
+
+rfs4_clntip_t *
+rfs4_find_clntip(struct sockaddr *addr, bool_t *create)
+{
+	rfs4_clntip_t *cp;
+
+	rw_enter(&rfs4_findclient_lock, RW_READER);
+
+	cp = (rfs4_clntip_t *)rfs4_dbsearch(rfs4_clntip_idx, addr,
+	    create, addr, RFS4_DBS_VALID);
+
+	rw_exit(&rfs4_findclient_lock);
+
+	return (cp);
+}
+
+void
+rfs4_invalidate_clntip(struct sockaddr *addr)
+{
+	rfs4_clntip_t *cp;
+	bool_t create = FALSE;
+
+	rw_enter(&rfs4_findclient_lock, RW_READER);
+
+	cp = (rfs4_clntip_t *)rfs4_dbsearch(rfs4_clntip_idx, addr,
+	    &create, NULL, RFS4_DBS_VALID);
+	if (cp == NULL) {
+		rw_exit(&rfs4_findclient_lock);
+		return;
+	}
+	rfs4_dbe_invalidate(cp->ri_dbe);
+	rfs4_dbe_rele(cp->ri_dbe);
+
+	rw_exit(&rfs4_findclient_lock);
+}
+
 bool_t
 rfs4_lease_expired(rfs4_client_t *cp)
 {
--- a/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c	Wed Dec 09 17:27:22 2009 -0600
@@ -65,6 +65,7 @@
 #include <sys/list.h>
 #include <sys/stat.h>
 #include <sys/mntent.h>
+#include <sys/priv.h>
 
 #include <rpc/types.h>
 #include <rpc/auth.h>
@@ -78,6 +79,8 @@
 #include <nfs/nfs4_kprot.h>
 #include <nfs/rnode4.h>
 #include <nfs/nfs4_clnt.h>
+#include <nfs/nfsid_map.h>
+#include <nfs/nfs4_idmap_impl.h>
 
 #include <vm/hat.h>
 #include <vm/as.h>
@@ -97,6 +100,9 @@
 
 #include <sys/priv_names.h>
 
+extern zone_key_t	nfs4clnt_zone_key;
+extern zone_key_t	nfsidmap_zone_key;
+
 /*
  * The automatic unmounter thread stuff!
  */
@@ -202,13 +208,16 @@
 static int	nfs4_trigger_mount(vnode_t *, cred_t *, vnode_t **);
 static int	nfs4_trigger_domount(vnode_t *, domount_args_t *, vfs_t **,
     cred_t *, vnode_t **);
-static domount_args_t  *nfs4_trigger_domount_args_create(vnode_t *);
+static domount_args_t  *nfs4_trigger_domount_args_create(vnode_t *, cred_t *);
 static void	nfs4_trigger_domount_args_destroy(domount_args_t *dma,
     vnode_t *vp);
-static ephemeral_servinfo_t *nfs4_trigger_esi_create(vnode_t *, servinfo4_t *);
+static ephemeral_servinfo_t *nfs4_trigger_esi_create(vnode_t *, servinfo4_t *,
+    cred_t *);
 static void	nfs4_trigger_esi_destroy(ephemeral_servinfo_t *, vnode_t *);
 static ephemeral_servinfo_t *nfs4_trigger_esi_create_mirrormount(vnode_t *,
     servinfo4_t *);
+static ephemeral_servinfo_t *nfs4_trigger_esi_create_referral(vnode_t *,
+    cred_t *);
 static struct nfs_args 	*nfs4_trigger_nargs_create(mntinfo4_t *, servinfo4_t *,
     ephemeral_servinfo_t *);
 static void	nfs4_trigger_nargs_destroy(struct nfs_args *);
@@ -216,10 +225,11 @@
 static void	nfs4_trigger_destroy_mntopts(char *);
 static int 	nfs4_trigger_add_mntopt(char *, char *, vfs_t *);
 static enum clnt_stat nfs4_trigger_ping_server(servinfo4_t *, int);
+static enum clnt_stat nfs4_ping_server_common(struct knetconfig *,
+    struct netbuf *, int);
 
 extern int	umount2_engine(vfs_t *, int, cred_t *, int);
 
-
 vnodeops_t *nfs4_trigger_vnodeops;
 
 /*
@@ -372,12 +382,46 @@
 	return (VOP_OPEN(vpp, flag, cr, ct));
 }
 
+void
+nfs4_fake_attrs(vnode_t *vp, struct vattr *vap)
+{
+	uint_t mask;
+	timespec_t now;
+
+	/*
+	 * Set some attributes here for referrals.
+	 */
+	mask = vap->va_mask;
+	bzero(vap, sizeof (struct vattr));
+	vap->va_mask	= mask;
+	vap->va_uid	= 0;
+	vap->va_gid	= 0;
+	vap->va_nlink	= 1;
+	vap->va_size	= 1;
+	gethrestime(&now);
+	vap->va_atime	= now;
+	vap->va_mtime	= now;
+	vap->va_ctime	= now;
+	vap->va_type	= VDIR;
+	vap->va_mode	= 0555;
+	vap->va_fsid	= vp->v_vfsp->vfs_dev;
+	vap->va_rdev	= 0;
+	vap->va_blksize	= MAXBSIZE;
+	vap->va_nblocks	= 1;
+	vap->va_seq	= 0;
+}
+
 /*
  * For the majority of cases, nfs4_trigger_getattr() will not trigger
  * a mount. However, if ATTR_TRIGGER is set, we are being informed
  * that we need to force the mount before we attempt to determine
  * the attributes. The intent is an atomic operation for security
  * testing.
+ *
+ * If we're not triggering a mount, we can still inquire about the
+ * actual attributes from the server in the mirror mount case,
+ * and will return manufactured attributes for a referral (see
+ * the 'create' branch of find_referral_stubvp()).
  */
 static int
 nfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
@@ -394,8 +438,15 @@
 
 		error = VOP_GETATTR(newvp, vap, flags, cr, ct);
 		VN_RELE(newvp);
-	} else {
+
+	} else if (RP_ISSTUB_MIRRORMOUNT(VTOR4(vp))) {
+
 		error = nfs4_getattr(vp, vap, flags, cr, ct);
+
+	} else if (RP_ISSTUB_REFERRAL(VTOR4(vp))) {
+
+		nfs4_fake_attrs(vp, vap);
+		error = 0;
 	}
 
 	return (error);
@@ -446,17 +497,19 @@
 
 	ASSERT(RP_ISSTUB(drp));
 
-	/* for now, we only support mirror-mounts */
-	ASSERT(RP_ISSTUB_MIRRORMOUNT(drp));
-
 	/*
 	 * It's not legal to lookup ".." for an fs root, so we mustn't pass
 	 * that up. Instead, pass onto the regular op, regardless of whether
 	 * we've triggered a mount.
 	 */
 	if (strcmp(nm, "..") == 0)
-		return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr,
-		    ct, deflags, rpnp));
+		if (RP_ISSTUB_MIRRORMOUNT(drp)) {
+			return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr,
+			    ct, deflags, rpnp));
+		} else if (RP_ISSTUB_REFERRAL(drp)) {
+			/* Return the parent vnode */
+			return (vtodv(dvp, vpp, cr, TRUE));
+		}
 
 	error = nfs4_trigger_mount(dvp, cr, &newdvp);
 	if (error)
@@ -672,7 +725,7 @@
 }
 
 /*
- * Mount upon a trigger vnode; for mirror-mounts, etc.
+ * Mount upon a trigger vnode; for mirror-mounts, referrals, etc.
  *
  * The mount may have already occurred, via another thread. If not,
  * assemble the location information - which may require fetching - and
@@ -706,9 +759,6 @@
 
 	ASSERT(RP_ISSTUB(rp));
 
-	/* for now, we only support mirror-mounts */
-	ASSERT(RP_ISSTUB_MIRRORMOUNT(rp));
-
 	*newvpp = NULL;
 
 	/*
@@ -782,7 +832,7 @@
 
 	must_unlock = TRUE;
 
-	dma = nfs4_trigger_domount_args_create(vp);
+	dma = nfs4_trigger_domount_args_create(vp, cr);
 	if (dma == NULL) {
 		error = EINVAL;
 		goto done;
@@ -801,10 +851,15 @@
 	}
 
 	crset_zone_privall(mcred);
+	if (is_system_labeled())
+		(void) setpflags(NET_MAC_AWARE, 1, mcred);
 
 	error = nfs4_trigger_domount(vp, dma, &vfsp, mcred, newvpp);
 	nfs4_trigger_domount_args_destroy(dma, vp);
 
+	DTRACE_PROBE2(nfs4clnt__func__referral__mount,
+	    vnode_t *, vp, int, error);
+
 	crfree(mcred);
 
 done:
@@ -812,9 +867,20 @@
 	if (must_unlock) {
 		mutex_enter(&net->net_cnt_lock);
 		net->net_status &= ~NFS4_EPHEMERAL_TREE_MOUNTING;
+
+		/*
+		 * REFCNT: If we are the root of the tree, then we need
+		 * to keep a reference because we malloced the tree and
+		 * this is where we tied it to our mntinfo.
+		 *
+		 * If we are not the root of the tree, then our tie to
+		 * the mntinfo occured elsewhere and we need to
+		 * decrement the reference to the tree.
+		 */
 		if (is_building)
 			net->net_status &= ~NFS4_EPHEMERAL_TREE_BUILDING;
-		nfs4_ephemeral_tree_decr(net);
+		else
+			nfs4_ephemeral_tree_decr(net);
 		mutex_exit(&net->net_cnt_lock);
 
 		mutex_exit(&net->net_tree_lock);
@@ -830,7 +896,7 @@
  * Collect together both the generic & mount-type specific args.
  */
 static domount_args_t *
-nfs4_trigger_domount_args_create(vnode_t *vp)
+nfs4_trigger_domount_args_create(vnode_t *vp, cred_t *cr)
 {
 	int nointr;
 	char *hostlist;
@@ -848,7 +914,7 @@
 	/* check if the current server is responding */
 	status = nfs4_trigger_ping_server(svp, nointr);
 	if (status == RPC_SUCCESS) {
-		esi_first = nfs4_trigger_esi_create(vp, svp);
+		esi_first = nfs4_trigger_esi_create(vp, svp, cr);
 		if (esi_first == NULL) {
 			kmem_free(hostlist, MAXPATHLEN);
 			return (NULL);
@@ -924,7 +990,7 @@
 			if (status != RPC_SUCCESS)
 				continue;
 
-			esi = nfs4_trigger_esi_create(vp, svp);
+			esi = nfs4_trigger_esi_create(vp, svp, cr);
 			if (esi == NULL)
 				continue;
 
@@ -1006,7 +1072,7 @@
  * types of ephemeral mount, the way we gather its contents differs.
  */
 static ephemeral_servinfo_t *
-nfs4_trigger_esi_create(vnode_t *vp, servinfo4_t *svp)
+nfs4_trigger_esi_create(vnode_t *vp, servinfo4_t *svp, cred_t *cr)
 {
 	ephemeral_servinfo_t *esi;
 	rnode4_t *rp = VTOR4(vp);
@@ -1016,12 +1082,10 @@
 	/* Call the ephemeral type-specific routine */
 	if (RP_ISSTUB_MIRRORMOUNT(rp))
 		esi = nfs4_trigger_esi_create_mirrormount(vp, svp);
+	else if (RP_ISSTUB_REFERRAL(rp))
+		esi = nfs4_trigger_esi_create_referral(vp, cr);
 	else
 		esi = NULL;
-
-	/* for now, we only support mirror-mounts */
-	ASSERT(esi != NULL);
-
 	return (esi);
 }
 
@@ -1032,9 +1096,6 @@
 
 	ASSERT(RP_ISSTUB(rp));
 
-	/* for now, we only support mirror-mounts */
-	ASSERT(RP_ISSTUB_MIRRORMOUNT(rp));
-
 	/* Currently, no need for an ephemeral type-specific routine */
 
 	/*
@@ -1051,6 +1112,10 @@
  * in which case it should be moved to nfs4_trigger_esi_create(), or a
  * common function called.
  */
+
+/*
+ * Mirror mounts case - should have all data available
+ */
 static ephemeral_servinfo_t *
 nfs4_trigger_esi_create_mirrormount(vnode_t *vp, servinfo4_t *svp)
 {
@@ -1149,9 +1214,12 @@
 	stubpath += 1;
 
 	/* for nfs_args->fh */
-	esi->esi_path_len = strlen(svp->sv_path) + strlen(stubpath) + 1;
+	esi->esi_path_len = strlen(stubpath) + 1;
+	if (strcmp(svp->sv_path, "/") != 0)
+		esi->esi_path_len += strlen(svp->sv_path);
 	esi->esi_path = kmem_zalloc(esi->esi_path_len, KM_SLEEP);
-	(void) strcat(esi->esi_path, svp->sv_path);
+	if (strcmp(svp->sv_path, "/") != 0)
+		(void) strcat(esi->esi_path, svp->sv_path);
 	(void) strcat(esi->esi_path, stubpath);
 
 	stubpath -= 1;
@@ -1164,6 +1232,592 @@
 }
 
 /*
+ * Makes an upcall to NFSMAPID daemon to resolve hostname of NFS server to
+ * get network information required to do the mount call.
+ */
+int
+nfs4_callmapid(utf8string *server, struct nfs_fsl_info *resp)
+{
+	door_arg_t	door_args;
+	door_handle_t	dh;
+	XDR		xdr;
+	refd_door_args_t *xdr_argsp;
+	refd_door_res_t  *orig_resp;
+	k_sigset_t	smask;
+	int		xdr_len = 0;
+	int 		res_len = 16; /* length of an ip adress */
+	int		orig_reslen = res_len;
+	int		error = 0;
+	struct nfsidmap_globals *nig;
+
+	if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)
+		return (ECONNREFUSED);
+
+	nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
+	ASSERT(nig != NULL);
+
+	mutex_enter(&nig->nfsidmap_daemon_lock);
+	dh = nig->nfsidmap_daemon_dh;
+	if (dh == NULL) {
+		mutex_exit(&nig->nfsidmap_daemon_lock);
+		cmn_err(CE_NOTE,
+		    "nfs4_callmapid: nfsmapid daemon not " \
+		    "running unable to resolve host name\n");
+		return (EINVAL);
+	}
+	door_ki_hold(dh);
+	mutex_exit(&nig->nfsidmap_daemon_lock);
+
+	xdr_len = xdr_sizeof(&(xdr_utf8string), server);
+
+	xdr_argsp = kmem_zalloc(xdr_len + sizeof (*xdr_argsp), KM_SLEEP);
+	xdr_argsp->xdr_len = xdr_len;
+	xdr_argsp->cmd = NFSMAPID_SRV_NETINFO;
+
+	xdrmem_create(&xdr, (char *)&xdr_argsp->xdr_arg,
+	    xdr_len, XDR_ENCODE);
+
+	if (!xdr_utf8string(&xdr, server)) {
+		kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
+		door_ki_rele(dh);
+		return (1);
+	}
+
+	if (orig_reslen)
+		orig_resp = kmem_alloc(orig_reslen, KM_SLEEP);
+
+	door_args.data_ptr = (char *)xdr_argsp;
+	door_args.data_size = sizeof (*xdr_argsp) + xdr_argsp->xdr_len;
+	door_args.desc_ptr = NULL;
+	door_args.desc_num = 0;
+	door_args.rbuf = orig_resp ? (char *)orig_resp : NULL;
+	door_args.rsize = res_len;
+
+	sigintr(&smask, 1);
+	error = door_ki_upcall(dh, &door_args);
+	sigunintr(&smask);
+
+	door_ki_rele(dh);
+
+	kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
+	if (error) {
+		kmem_free(orig_resp, orig_reslen);
+		/*
+		 * There is no door to connect to. The referral daemon
+		 * must not be running yet.
+		 */
+		cmn_err(CE_WARN,
+		    "nfsmapid not running cannot resolve host name");
+		goto out;
+	}
+
+	/*
+	 * If the results buffer passed back are not the same as
+	 * what was sent free the old buffer and use the new one.
+	 */
+	if (orig_resp && orig_reslen) {
+		refd_door_res_t *door_resp;
+
+		door_resp = (refd_door_res_t *)door_args.rbuf;
+		if ((void *)door_args.rbuf != orig_resp)
+			kmem_free(orig_resp, orig_reslen);
+		if (door_resp->res_status == 0) {
+			xdrmem_create(&xdr, (char *)&door_resp->xdr_res,
+			    door_resp->xdr_len, XDR_DECODE);
+			bzero(resp, sizeof (struct nfs_fsl_info));
+			if (!xdr_nfs_fsl_info(&xdr, resp)) {
+				DTRACE_PROBE2(
+				    nfs4clnt__debug__referral__upcall__xdrfail,
+				    struct nfs_fsl_info *, resp,
+				    char *, "nfs4_callmapid");
+				error = EINVAL;
+			}
+		} else {
+			DTRACE_PROBE2(
+			    nfs4clnt__debug__referral__upcall__badstatus,
+			    int, door_resp->res_status,
+			    char *, "nfs4_callmapid");
+			error = door_resp->res_status;
+		}
+		kmem_free(door_args.rbuf, door_args.rsize);
+	}
+out:
+	DTRACE_PROBE2(nfs4clnt__func__referral__upcall,
+	    char *, server, int, error);
+	return (error);
+}
+
+/*
+ * Fetches the fs_locations attribute. Typically called
+ * from a Replication/Migration/Referrals/Mirror-mount context
+ *
+ * Fills in the attributes in garp. The caller is assumed
+ * to have allocated memory for garp.
+ *
+ * lock: if set do not lock s_recovlock and mi_recovlock mutex,
+ *	 it's already done by caller. Otherwise lock these mutexes
+ *	 before doing the rfs4call().
+ *
+ * Returns
+ * 	1	 for success
+ * 	0	 for failure
+ */
+int
+nfs4_fetch_locations(mntinfo4_t *mi, nfs4_sharedfh_t *sfh, char *nm,
+    cred_t *cr, nfs4_ga_res_t *garp, COMPOUND4res_clnt *callres, bool_t lock)
+{
+	COMPOUND4args_clnt args;
+	COMPOUND4res_clnt res;
+	nfs_argop4 *argop;
+	int argoplist_size = 3 * sizeof (nfs_argop4);
+	nfs4_server_t *sp = NULL;
+	int doqueue = 1;
+	nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
+	int retval = 1;
+	struct nfs4_clnt *nfscl;
+
+	if (lock == TRUE)
+		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
+	else
+		ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
+		    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
+
+	sp = find_nfs4_server(mi);
+	if (lock == TRUE)
+		nfs_rw_exit(&mi->mi_recovlock);
+
+	if (sp != NULL)
+		mutex_exit(&sp->s_lock);
+
+	if (lock == TRUE) {
+		if (sp != NULL)
+			(void) nfs_rw_enter_sig(&sp->s_recovlock,
+			    RW_WRITER, 0);
+		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_WRITER, 0);
+	} else {
+		if (sp != NULL) {
+			ASSERT(nfs_rw_lock_held(&sp->s_recovlock, RW_READER) ||
+			    nfs_rw_lock_held(&sp->s_recovlock, RW_WRITER));
+		}
+	}
+
+	/*
+	 * Do we want to do the setup for recovery here?
+	 *
+	 * We know that the server responded to a null ping a very
+	 * short time ago, and we know that we intend to do a
+	 * single stateless operation - we want to fetch attributes,
+	 * so we know we can't encounter errors about state.  If
+	 * something goes wrong with the GETATTR, like not being
+	 * able to get a response from the server or getting any
+	 * kind of FH error, we should fail the mount.
+	 *
+	 * We may want to re-visited this at a later time.
+	 */
+	argop = kmem_alloc(argoplist_size, KM_SLEEP);
+
+	args.ctag = TAG_GETATTR_FSLOCATION;
+	/* PUTFH LOOKUP GETATTR */
+	args.array_len = 3;
+	args.array = argop;
+
+	/* 0. putfh file */
+	argop[0].argop = OP_CPUTFH;
+	argop[0].nfs_argop4_u.opcputfh.sfh = sfh;
+
+	/* 1. lookup name, can't be dotdot */
+	argop[1].argop = OP_CLOOKUP;
+	argop[1].nfs_argop4_u.opclookup.cname = nm;
+
+	/* 2. file attrs */
+	argop[2].argop = OP_GETATTR;
+	argop[2].nfs_argop4_u.opgetattr.attr_request =
+	    FATTR4_FSID_MASK | FATTR4_FS_LOCATIONS_MASK |
+	    FATTR4_MOUNTED_ON_FILEID_MASK;
+	argop[2].nfs_argop4_u.opgetattr.mi = mi;
+
+	rfs4call(mi, &args, &res, cr, &doqueue, 0, &e);
+
+	if (lock == TRUE) {
+		nfs_rw_exit(&mi->mi_recovlock);
+		if (sp != NULL)
+			nfs_rw_exit(&sp->s_recovlock);
+	}
+
+	nfscl = zone_getspecific(nfs4clnt_zone_key, nfs_zone());
+	nfscl->nfscl_stat.referrals.value.ui64++;
+	DTRACE_PROBE3(nfs4clnt__func__referral__fsloc,
+	    nfs4_sharedfh_t *, sfh, char *, nm, nfs4_error_t *, &e);
+
+	if (e.error != 0) {
+		if (sp != NULL)
+			nfs4_server_rele(sp);
+		kmem_free(argop, argoplist_size);
+		return (0);
+	}
+
+	/*
+	 * Check for all possible error conditions.
+	 * For valid replies without an ops array or for illegal
+	 * replies, return a failure.
+	 */
+	if (res.status != NFS4_OK || res.array_len < 3 ||
+	    res.array[2].nfs_resop4_u.opgetattr.status != NFS4_OK) {
+		retval = 0;
+		goto exit;
+	}
+
+	/*
+	 * There isn't much value in putting the attributes
+	 * in the attr cache since fs_locations4 aren't
+	 * encountered very frequently, so just make them
+	 * available to the caller.
+	 */
+	*garp = res.array[2].nfs_resop4_u.opgetattr.ga_res;
+
+	DTRACE_PROBE2(nfs4clnt__debug__referral__fsloc,
+	    nfs4_ga_res_t *, garp, char *, "nfs4_fetch_locations");
+
+	/* No fs_locations? -- return a failure */
+	if (garp->n4g_ext_res == NULL ||
+	    garp->n4g_ext_res->n4g_fslocations.locations_val == NULL) {
+		retval = 0;
+		goto exit;
+	}
+
+	if (!garp->n4g_fsid_valid)
+		retval = 0;
+
+exit:
+	if (retval == 0) {
+		/* the call was ok but failed validating the call results */
+		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
+	} else {
+		ASSERT(callres != NULL);
+		*callres = res;
+	}
+
+	if (sp != NULL)
+		nfs4_server_rele(sp);
+	kmem_free(argop, argoplist_size);
+	return (retval);
+}
+
+/* tunable to disable referral mounts */
+int nfs4_no_referrals = 0;
+
+/*
+ * Returns NULL if the vnode cannot be created or found.
+ */
+vnode_t *
+find_referral_stubvp(vnode_t *dvp, char *nm, cred_t *cr)
+{
+	nfs_fh4 *stub_fh, *dfh;
+	nfs4_sharedfh_t *sfhp;
+	char *newfhval;
+	vnode_t *vp = NULL;
+	fattr4_mounted_on_fileid mnt_on_fileid;
+	nfs4_ga_res_t garp;
+	mntinfo4_t *mi;
+	COMPOUND4res_clnt callres;
+	hrtime_t t;
+
+	if (nfs4_no_referrals)
+		return (NULL);
+
+	/*
+	 * Get the mounted_on_fileid, unique on that server::fsid
+	 */
+	mi = VTOMI4(dvp);
+	if (nfs4_fetch_locations(mi, VTOR4(dvp)->r_fh, nm, cr,
+	    &garp, &callres, FALSE) == 0)
+		return (NULL);
+	mnt_on_fileid = garp.n4g_mon_fid;
+	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
+
+	/*
+	 * Build a fake filehandle from the dir FH and the mounted_on_fileid
+	 */
+	dfh = &VTOR4(dvp)->r_fh->sfh_fh;
+	stub_fh = kmem_alloc(sizeof (nfs_fh4), KM_SLEEP);
+	stub_fh->nfs_fh4_val = kmem_alloc(dfh->nfs_fh4_len +
+	    sizeof (fattr4_mounted_on_fileid), KM_SLEEP);
+	newfhval = stub_fh->nfs_fh4_val;
+
+	/* copy directory's file handle */
+	bcopy(dfh->nfs_fh4_val, newfhval, dfh->nfs_fh4_len);
+	stub_fh->nfs_fh4_len = dfh->nfs_fh4_len;
+	newfhval = newfhval + dfh->nfs_fh4_len;
+
+	/* Add mounted_on_fileid. Use bcopy to avoid alignment problem */
+	bcopy((char *)&mnt_on_fileid, newfhval,
+	    sizeof (fattr4_mounted_on_fileid));
+	stub_fh->nfs_fh4_len += sizeof (fattr4_mounted_on_fileid);
+
+	sfhp = sfh4_put(stub_fh, VTOMI4(dvp), NULL);
+	kmem_free(stub_fh->nfs_fh4_val, dfh->nfs_fh4_len +
+	    sizeof (fattr4_mounted_on_fileid));
+	kmem_free(stub_fh, sizeof (nfs_fh4));
+	if (sfhp == NULL)
+		return (NULL);
+
+	t = gethrtime();
+	garp.n4g_va.va_type = VDIR;
+	vp = makenfs4node(sfhp, NULL, dvp->v_vfsp, t,
+	    cr, dvp, fn_get(VTOSV(dvp)->sv_name, nm, sfhp));
+
+	if (vp != NULL)
+		vp->v_type = VDIR;
+
+	sfh4_rele(&sfhp);
+	return (vp);
+}
+
+int
+nfs4_setup_referral(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr)
+{
+	vnode_t *nvp;
+	rnode4_t *rp;
+
+	if ((nvp = find_referral_stubvp(dvp, nm, cr)) == NULL)
+		return (EINVAL);
+
+	rp = VTOR4(nvp);
+	mutex_enter(&rp->r_statelock);
+	r4_stub_referral(rp);
+	mutex_exit(&rp->r_statelock);
+	dnlc_enter(dvp, nm, nvp);
+
+	if (*vpp != NULL)
+		VN_RELE(*vpp);	/* no longer need this vnode */
+
+	*vpp = nvp;
+
+	return (0);
+}
+
+/*
+ * Fetch the location information and resolve the new server.
+ * Caller needs to free up the XDR data which is returned.
+ * Input: mount info, shared filehandle, nodename
+ * Return: Index to the result or Error(-1)
+ * Output: FsLocations Info, Resolved Server Info.
+ */
+int
+nfs4_process_referral(mntinfo4_t *mi, nfs4_sharedfh_t *sfh,
+    char *nm, cred_t *cr, nfs4_ga_res_t *grp, COMPOUND4res_clnt *res,
+    struct nfs_fsl_info *fsloc)
+{
+	fs_location4 *fsp;
+	struct nfs_fsl_info nfsfsloc;
+	int ret, i, error;
+	nfs4_ga_res_t garp;
+	COMPOUND4res_clnt callres;
+	struct knetconfig *knc;
+
+	ret = nfs4_fetch_locations(mi, sfh, nm, cr, &garp, &callres, TRUE);
+	if (ret == 0)
+		return (-1);
+
+	/*
+	 * As a lame attempt to figuring out if we're
+	 * handling a migration event or a referral,
+	 * look for rnodes with this fsid in the rnode
+	 * cache.
+	 *
+	 * If we can find one or more such rnodes, it
+	 * means we're handling a migration event and
+	 * we want to bail out in that case.
+	 */
+	if (r4find_by_fsid(mi, &garp.n4g_fsid)) {
+		DTRACE_PROBE3(nfs4clnt__debug__referral__migration,
+		    mntinfo4_t *, mi, nfs4_ga_res_t *, &garp,
+		    char *, "nfs4_process_referral");
+		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
+		return (-1);
+	}
+
+	/*
+	 * Find the first responsive server to mount.  When we find
+	 * one, fsp will point to it.
+	 */
+	for (i = 0; i < garp.n4g_ext_res->n4g_fslocations.locations_len; i++) {
+
+		fsp = &garp.n4g_ext_res->n4g_fslocations.locations_val[i];
+		if (fsp->server_len == 0 || fsp->server_val == NULL)
+			continue;
+
+		error = nfs4_callmapid(fsp->server_val, &nfsfsloc);
+		if (error != 0)
+			continue;
+
+		error = nfs4_ping_server_common(nfsfsloc.knconf,
+		    nfsfsloc.addr, !(mi->mi_flags & MI4_INT));
+		if (error == RPC_SUCCESS)
+			break;
+
+		DTRACE_PROBE2(nfs4clnt__debug__referral__srvaddr,
+		    sockaddr_in *, (struct sockaddr_in *)nfsfsloc.addr->buf,
+		    char *, "nfs4_process_referral");
+
+		(void) xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
+	}
+	knc = nfsfsloc.knconf;
+	if ((i >= garp.n4g_ext_res->n4g_fslocations.locations_len) ||
+	    (knc->knc_protofmly == NULL) || (knc->knc_proto == NULL)) {
+		DTRACE_PROBE2(nfs4clnt__debug__referral__nofsloc,
+		    nfs4_ga_res_t *, &garp, char *, "nfs4_process_referral");
+		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
+		return (-1);
+	}
+
+	/* Send the results back */
+	*fsloc = nfsfsloc;
+	*grp = garp;
+	*res = callres;
+	return (i);
+}
+
+/*
+ * Referrals case - need to fetch referral data and then upcall to
+ * user-level to get complete mount data.
+ */
+static ephemeral_servinfo_t *
+nfs4_trigger_esi_create_referral(vnode_t *vp, cred_t *cr)
+{
+	struct knetconfig	*sikncp, *svkncp;
+	struct netbuf		*bufp;
+	ephemeral_servinfo_t	*esi;
+	vnode_t			*dvp;
+	rnode4_t		*drp;
+	fs_location4		*fsp;
+	struct nfs_fsl_info	nfsfsloc;
+	nfs4_ga_res_t		garp;
+	char			*p;
+	char			fn[MAXNAMELEN];
+	int			i, index = -1;
+	mntinfo4_t		*mi;
+	COMPOUND4res_clnt	callres;
+
+	/*
+	 * If we're passed in a stub vnode that
+	 * isn't a "referral" stub, bail out
+	 * and return a failure
+	 */
+	if (!RP_ISSTUB_REFERRAL(VTOR4(vp)))
+		return (NULL);
+
+	if (vtodv(vp, &dvp, CRED(), TRUE) != 0)
+		return (NULL);
+
+	drp = VTOR4(dvp);
+	if (nfs_rw_enter_sig(&drp->r_rwlock, RW_READER, INTR4(dvp))) {
+		VN_RELE(dvp);
+		return (NULL);
+	}
+
+	if (vtoname(vp, fn, MAXNAMELEN) != 0) {
+		nfs_rw_exit(&drp->r_rwlock);
+		VN_RELE(dvp);
+		return (NULL);
+	}
+
+	mi = VTOMI4(dvp);
+	index = nfs4_process_referral(mi, drp->r_fh, fn, cr,
+	    &garp, &callres, &nfsfsloc);
+	nfs_rw_exit(&drp->r_rwlock);
+	VN_RELE(dvp);
+	if (index < 0)
+		return (NULL);
+
+	fsp = &garp.n4g_ext_res->n4g_fslocations.locations_val[index];
+	esi = kmem_zalloc(sizeof (ephemeral_servinfo_t), KM_SLEEP);
+
+	/* initially set to be our type of ephemeral mount; may be added to */
+	esi->esi_mount_flags = NFSMNT_REFERRAL;
+
+	esi->esi_hostname =
+	    kmem_zalloc(fsp->server_val->utf8string_len + 1, KM_SLEEP);
+	bcopy(fsp->server_val->utf8string_val, esi->esi_hostname,
+	    fsp->server_val->utf8string_len);
+	esi->esi_hostname[fsp->server_val->utf8string_len] = '\0';
+
+	bufp = kmem_alloc(sizeof (struct netbuf), KM_SLEEP);
+	bufp->len = nfsfsloc.addr->len;
+	bufp->maxlen = nfsfsloc.addr->maxlen;
+	bufp->buf = kmem_zalloc(bufp->len, KM_SLEEP);
+	bcopy(nfsfsloc.addr->buf, bufp->buf, bufp->len);
+	esi->esi_addr = bufp;
+
+	esi->esi_knconf = kmem_zalloc(sizeof (*esi->esi_knconf), KM_SLEEP);
+	sikncp = esi->esi_knconf;
+
+	DTRACE_PROBE2(nfs4clnt__debug__referral__nfsfsloc,
+	    struct nfs_fsl_info *, &nfsfsloc,
+	    char *, "nfs4_trigger_esi_create_referral");
+
+	svkncp = nfsfsloc.knconf;
+	sikncp->knc_semantics = svkncp->knc_semantics;
+	sikncp->knc_protofmly = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
+	(void) strlcat((char *)sikncp->knc_protofmly,
+	    (char *)svkncp->knc_protofmly, KNC_STRSIZE);
+	sikncp->knc_proto = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
+	(void) strlcat((char *)sikncp->knc_proto, (char *)svkncp->knc_proto,
+	    KNC_STRSIZE);
+	sikncp->knc_rdev = svkncp->knc_rdev;
+
+	DTRACE_PROBE2(nfs4clnt__debug__referral__knetconf,
+	    struct knetconfig *, sikncp,
+	    char *, "nfs4_trigger_esi_create_referral");
+
+	esi->esi_netname = kmem_zalloc(nfsfsloc.netnm_len, KM_SLEEP);
+	bcopy(nfsfsloc.netname, esi->esi_netname, nfsfsloc.netnm_len);
+	esi->esi_syncaddr = NULL;
+
+	esi->esi_path = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	esi->esi_path_len = MAXPATHLEN;
+	*p++ = '/';
+	for (i = 0; i < fsp->rootpath.pathname4_len; i++) {
+		component4 *comp;
+
+		comp = &fsp->rootpath.pathname4_val[i];
+		/* If no space, null the string and bail */
+		if ((p - esi->esi_path) + comp->utf8string_len + 1 > MAXPATHLEN)
+			goto err;
+		bcopy(comp->utf8string_val, p, comp->utf8string_len);
+		p += comp->utf8string_len;
+		*p++ = '/';
+	}
+	if (fsp->rootpath.pathname4_len != 0)
+		*(p - 1) = '\0';
+	else
+		*p = '\0';
+	p = esi->esi_path;
+	esi->esi_path = strdup(p);
+	esi->esi_path_len = strlen(p) + 1;
+	kmem_free(p, MAXPATHLEN);
+
+	/* Allocated in nfs4_process_referral() */
+	(void) xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
+	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
+
+	return (esi);
+err:
+	kmem_free(esi->esi_path, esi->esi_path_len);
+	kmem_free(esi->esi_hostname, fsp->server_val->utf8string_len + 1);
+	kmem_free(esi->esi_addr->buf, esi->esi_addr->len);
+	kmem_free(esi->esi_addr, sizeof (struct netbuf));
+	kmem_free(esi->esi_knconf->knc_protofmly, KNC_STRSIZE);
+	kmem_free(esi->esi_knconf->knc_proto, KNC_STRSIZE);
+	kmem_free(esi->esi_knconf, sizeof (*esi->esi_knconf));
+	kmem_free(esi->esi_netname, nfsfsloc.netnm_len);
+	kmem_free(esi, sizeof (ephemeral_servinfo_t));
+	(void) xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
+	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
+	return (NULL);
+}
+
+/*
  * Assemble the args, and call the generic VFS mount function to
  * finally perform the ephemeral mount.
  */
@@ -1357,6 +2011,9 @@
 	nargs->acdirmin = HR2SEC(mi->mi_acdirmin);
 	nargs->acdirmax = HR2SEC(mi->mi_acdirmax);
 
+	/* add any specific flags for this type of ephemeral mount */
+	nargs->flags |= esi->esi_mount_flags;
+
 	if (mi->mi_flags & MI4_NOCTO)
 		nargs->flags |= NFSMNT_NOCTO;
 	if (mi->mi_flags & MI4_GRPID)
@@ -1367,19 +2024,28 @@
 		nargs->flags |= NFSMNT_NOPRINT;
 	if (mi->mi_flags & MI4_DIRECTIO)
 		nargs->flags |= NFSMNT_DIRECTIO;
-	if (mi->mi_flags & MI4_PUBLIC)
+	if (mi->mi_flags & MI4_PUBLIC && nargs->flags & NFSMNT_MIRRORMOUNT)
 		nargs->flags |= NFSMNT_PUBLIC;
 
+	/* Do some referral-specific option tweaking */
+	if (nargs->flags & NFSMNT_REFERRAL) {
+		nargs->flags &= ~NFSMNT_DORDMA;
+		nargs->flags |= NFSMNT_TRYRDMA;
+	}
+
 	mutex_exit(&mi->mi_lock);
 
-	/* add any specific flags for this type of ephemeral mount */
-	nargs->flags |= esi->esi_mount_flags;
-
 	/*
 	 * Security data & negotiation policy.
 	 *
-	 * We need to preserve the parent mount's preference for security
-	 * negotiation, translating SV4_TRYSECDEFAULT -> NFSMNT_SECDEFAULT.
+	 * For mirror mounts, we need to preserve the parent mount's
+	 * preference for security negotiation, translating SV4_TRYSECDEFAULT
+	 * to NFSMNT_SECDEFAULT if present.
+	 *
+	 * For referrals, we always want security negotiation and will
+	 * set NFSMNT_SECDEFAULT and we will not copy current secdata.
+	 * The reason is that we can't negotiate down from a parent's
+	 * Kerberos flavor to AUTH_SYS.
 	 *
 	 * If SV4_TRYSECDEFAULT is not set, that indicates that a specific
 	 * security flavour was requested, with data in sv_secdata, and that
@@ -1395,8 +2061,16 @@
 	 * we will copy sv_currsec. Otherwise, copy sv_secdata. Regardless,
 	 * we will set NFSMNT_SECDEFAULT, to enable negotiation.
 	 */
-	if (svp->sv_flags & SV4_TRYSECDEFAULT) {
-		/* enable negotiation for ephemeral mount */
+	if (nargs->flags & NFSMNT_REFERRAL) {
+		/* enable negotiation for referral mount */
+		nargs->flags |= NFSMNT_SECDEFAULT;
+		secdata = kmem_alloc(sizeof (sec_data_t), KM_SLEEP);
+		secdata->secmod = secdata->rpcflavor = AUTH_SYS;
+		secdata->data = NULL;
+	}
+
+	else if (svp->sv_flags & SV4_TRYSECDEFAULT) {
+		/* enable negotiation for mirror mount */
 		nargs->flags |= NFSMNT_SECDEFAULT;
 
 		/*
@@ -1499,6 +2173,11 @@
 		return (EBUSY);
 	}
 
+	/*
+	 * We've just tied the mntinfo to the tree, so
+	 * now we bump the refcnt and hold it there until
+	 * this mntinfo is removed from the tree.
+	 */
 	nfs4_ephemeral_tree_hold(net);
 
 	/*
@@ -1515,7 +2194,6 @@
 	 */
 	eph->ne_mount_to = ntg->ntg_mount_to;
 
-	mi->mi_flags |= MI4_EPHEMERAL;
 	mi->mi_ephemeral = eph;
 
 	/*
@@ -1542,6 +2220,7 @@
 			mi->mi_flags &= ~MI4_EPHEMERAL;
 			mi->mi_ephemeral = NULL;
 			kmem_free(eph, sizeof (*eph));
+			nfs4_ephemeral_tree_rele(net);
 			rc = EBUSY;
 		} else {
 			if (prior->ne_child == NULL) {
@@ -1581,8 +2260,6 @@
 		eph->ne_prior = NULL;
 	}
 
-	nfs4_ephemeral_tree_rele(net);
-
 	mutex_exit(&mi->mi_lock);
 	mutex_exit(&mi_parent->mi_lock);
 
@@ -1758,15 +2435,13 @@
  */
 void
 nfs4_ephemeral_umount_unlock(bool_t *pmust_unlock,
-    bool_t *pmust_rele, nfs4_ephemeral_tree_t **pnet)
+    nfs4_ephemeral_tree_t **pnet)
 {
 	nfs4_ephemeral_tree_t	*net = *pnet;
 
 	if (*pmust_unlock) {
 		mutex_enter(&net->net_cnt_lock);
 		net->net_status &= ~NFS4_EPHEMERAL_TREE_UMOUNTING;
-		if (*pmust_rele)
-			nfs4_ephemeral_tree_decr(net);
 		mutex_exit(&net->net_cnt_lock);
 
 		mutex_exit(&net->net_tree_lock);
@@ -1783,7 +2458,7 @@
  */
 void
 nfs4_ephemeral_umount_activate(mntinfo4_t *mi, bool_t *pmust_unlock,
-    bool_t *pmust_rele, nfs4_ephemeral_tree_t **pnet)
+    nfs4_ephemeral_tree_t **pnet)
 {
 	/*
 	 * Now we need to get rid of the ephemeral data if it exists.
@@ -1798,6 +2473,7 @@
 		if (!(mi->mi_flags & MI4_EPHEMERAL_RECURSED))
 			nfs4_ephemeral_umount_cleanup(mi->mi_ephemeral);
 
+		nfs4_ephemeral_tree_rele(*pnet);
 		ASSERT(mi->mi_ephemeral != NULL);
 
 		kmem_free(mi->mi_ephemeral, sizeof (*mi->mi_ephemeral));
@@ -1805,15 +2481,19 @@
 	}
 	mutex_exit(&mi->mi_lock);
 
-	nfs4_ephemeral_umount_unlock(pmust_unlock, pmust_rele, pnet);
+	nfs4_ephemeral_umount_unlock(pmust_unlock, pnet);
 }
 
 /*
  * Unmount an ephemeral node.
+ *
+ * Note that if this code fails, then it must unlock.
+ *
+ * If it succeeds, then the caller must be prepared to do so.
  */
 int
 nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr,
-    bool_t *pmust_unlock, bool_t *pmust_rele, nfs4_ephemeral_tree_t **pnet)
+    bool_t *pmust_unlock, nfs4_ephemeral_tree_t **pnet)
 {
 	int			error = 0;
 	nfs4_ephemeral_t	*eph;
@@ -1826,7 +2506,7 @@
 	 * Make sure to set the default state for cleaning
 	 * up the tree in the caller (and on the way out).
 	 */
-	*pmust_unlock = *pmust_rele = FALSE;
+	*pmust_unlock = FALSE;
 
 	/*
 	 * The active vnodes on this file system may be ephemeral
@@ -1865,16 +2545,18 @@
 	is_recursed = mi->mi_flags & MI4_EPHEMERAL_RECURSED;
 	is_derooting = (eph == NULL);
 
+	mutex_enter(&net->net_cnt_lock);
+
 	/*
 	 * If this is not recursion, then we need to
-	 * grab a ref count.
+	 * check to see if a harvester thread has
+	 * already grabbed the lock.
 	 *
-	 * But wait, we also do not want to do that
-	 * if a harvester thread has already grabbed
-	 * the lock.
+	 * After we exit this branch, we may not
+	 * blindly return, we need to jump to
+	 * is_busy!
 	 */
 	if (!is_recursed) {
-		mutex_enter(&net->net_cnt_lock);
 		if (net->net_status &
 		    NFS4_EPHEMERAL_TREE_LOCKED) {
 			/*
@@ -1902,13 +2584,10 @@
 			}
 
 			was_locked = TRUE;
-		} else {
-			nfs4_ephemeral_tree_incr(net);
-			*pmust_rele = TRUE;
 		}
-
-		mutex_exit(&net->net_cnt_lock);
 	}
+
+	mutex_exit(&net->net_cnt_lock);
 	mutex_exit(&mi->mi_lock);
 
 	/*
@@ -1936,9 +2615,7 @@
 				if (net->net_status &
 				    (NFS4_EPHEMERAL_TREE_DEROOTING
 				    | NFS4_EPHEMERAL_TREE_INVALID)) {
-					nfs4_ephemeral_tree_decr(net);
 					mutex_exit(&net->net_cnt_lock);
-					*pmust_rele = FALSE;
 					goto is_busy;
 				}
 				mutex_exit(&net->net_cnt_lock);
@@ -1974,10 +2651,8 @@
 				if (net->net_status &
 				    NFS4_EPHEMERAL_TREE_INVALID ||
 				    (!is_derooting && eph == NULL)) {
-					nfs4_ephemeral_tree_decr(net);
 					mutex_exit(&net->net_cnt_lock);
 					mutex_exit(&net->net_tree_lock);
-					*pmust_rele = FALSE;
 					goto is_busy;
 				}
 				mutex_exit(&net->net_cnt_lock);
@@ -2048,8 +2723,14 @@
 		mutex_enter(&net->net_cnt_lock);
 		net->net_status &= ~NFS4_EPHEMERAL_TREE_DEROOTING;
 		net->net_status |= NFS4_EPHEMERAL_TREE_INVALID;
-		if (was_locked == FALSE)
-			nfs4_ephemeral_tree_decr(net);
+		DTRACE_NFSV4_1(nfs4clnt__dbg__ephemeral__tree__derooting,
+		    uint_t, net->net_refcnt);
+
+		/*
+		 * We will not finalize this node, so safe to
+		 * release it.
+		 */
+		nfs4_ephemeral_tree_decr(net);
 		mutex_exit(&net->net_cnt_lock);
 
 		if (was_locked == FALSE)
@@ -2057,8 +2738,8 @@
 
 		/*
 		 * We have just blown away any notation of this
-		 * tree being locked. We can't let the caller
-		 * try to clean things up.
+		 * tree being locked or having a refcnt.
+		 * We can't let the caller try to clean things up.
 		 */
 		*pmust_unlock = FALSE;
 
@@ -2077,8 +2758,7 @@
 
 is_busy:
 
-	nfs4_ephemeral_umount_unlock(pmust_unlock, pmust_rele,
-	    pnet);
+	nfs4_ephemeral_umount_unlock(pmust_unlock, pnet);
 
 	return (error);
 }
@@ -2314,19 +2994,18 @@
 		/*
 		 * At this point we are done processing this tree.
 		 *
-		 * If the tree is invalid and we are the only reference
+		 * If the tree is invalid and we were the only reference
 		 * to it, then we push it on the local linked list
 		 * to remove it at the end. We avoid that action now
 		 * to keep the tree processing going along at a fair clip.
 		 *
-		 * Else, even if we are the only reference, we drop
-		 * our hold on the current tree and allow it to be
-		 * reused as needed.
+		 * Else, even if we were the only reference, we
+		 * allow it to be reused as needed.
 		 */
 		mutex_enter(&net->net_cnt_lock);
-		if (net->net_refcnt == 1 &&
+		nfs4_ephemeral_tree_decr(net);
+		if (net->net_refcnt == 0 &&
 		    net->net_status & NFS4_EPHEMERAL_TREE_INVALID) {
-			nfs4_ephemeral_tree_decr(net);
 			net->net_status &= ~NFS4_EPHEMERAL_TREE_LOCKED;
 			mutex_exit(&net->net_cnt_lock);
 			mutex_exit(&net->net_tree_lock);
@@ -2341,7 +3020,6 @@
 			continue;
 		}
 
-		nfs4_ephemeral_tree_decr(net);
 		net->net_status &= ~NFS4_EPHEMERAL_TREE_LOCKED;
 		mutex_exit(&net->net_cnt_lock);
 		mutex_exit(&net->net_tree_lock);
@@ -2620,9 +3298,9 @@
 }
 
 static enum clnt_stat
-nfs4_trigger_ping_server(servinfo4_t *svp, int nointr)
+nfs4_ping_server_common(struct knetconfig *knc, struct netbuf *addr, int nointr)
 {
-	int retries, error;
+	int retries;
 	uint_t max_msgsize;
 	enum clnt_stat status;
 	CLIENT *cl;
@@ -2634,9 +3312,8 @@
 	timeout.tv_sec = 2;
 	timeout.tv_usec = 0;
 
-	error = clnt_tli_kcreate(svp->sv_knconf, &svp->sv_addr, NFS_PROGRAM,
-	    NFS_V4, max_msgsize, retries, CRED(), &cl);
-	if (error)
+	if (clnt_tli_kcreate(knc, addr, NFS_PROGRAM, NFS_V4,
+	    max_msgsize, retries, CRED(), &cl) != 0)
 		return (RPC_FAILED);
 
 	if (nointr)
@@ -2651,3 +3328,9 @@
 
 	return (status);
 }
+
+static enum clnt_stat
+nfs4_trigger_ping_server(servinfo4_t *svp, int nointr)
+{
+	return (nfs4_ping_server_common(svp->sv_knconf, &svp->sv_addr, nointr));
+}
--- a/usr/src/uts/common/fs/nfs/nfs4_subr.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_subr.c	Wed Dec 09 17:27:22 2009 -0600
@@ -59,6 +59,8 @@
 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
@@ -90,7 +92,7 @@
  */
 static list_t nfs4_clnt_list;
 static kmutex_t nfs4_clnt_list_lock;
-static zone_key_t nfs4clnt_zone_key;
+zone_key_t nfs4clnt_zone_key;
 
 static struct kmem_cache *chtab4_cache;
 
@@ -1943,6 +1945,9 @@
 	 * crossed an underlying server fs boundary.
 	 *
 	 * This stub will be for a mirror-mount.
+	 * A referral would look like a boundary crossing
+	 * as well, but would not be the same type of object,
+	 * so we would expect to mark the object dead.
 	 *
 	 * See comment in r4_do_attrcache() for more details.
 	 */
@@ -2109,7 +2114,8 @@
 			bool_t abort;
 
 			abort = nfs4_start_recovery(&e, mi,
-			    rootvp, NULL, NULL, NULL, OP_LOOKUP, NULL);
+			    rootvp, NULL, NULL, NULL, OP_LOOKUP, NULL, NULL,
+			    NULL);
 			if (abort) {
 				nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
 				    &recov_state, FALSE);
@@ -2169,7 +2175,7 @@
 
 					abort = nfs4_start_recovery(&e, mi,
 					    rootvp, NULL, NULL, NULL,
-					    OP_LOOKUP, NULL);
+					    OP_LOOKUP, NULL, NULL, NULL);
 					if (abort) {
 						nfs4_end_fop(mi, rootvp, NULL,
 						    OH_LOOKUP, &recov_state,
@@ -2757,6 +2763,7 @@
 	mutex_enter(&nfs4_clnt_list_lock);
 	list_insert_head(&nfs4_clnt_list, nfscl);
 	mutex_exit(&nfs4_clnt_list_lock);
+
 	return (nfscl);
 }
 
--- a/usr/src/uts/common/fs/nfs/nfs4_vfsops.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_vfsops.c	Wed Dec 09 17:27:22 2009 -0600
@@ -76,6 +76,8 @@
 #include <nfs/nfs4_clnt.h>
 #include <sys/fs/autofs.h>
 
+#include <sys/sdt.h>
+
 
 /*
  * Arguments passed to thread to free data structures from forced unmount.
@@ -160,6 +162,14 @@
 extern void nfs4_ephemeral_init(void);
 extern void nfs4_ephemeral_fini(void);
 
+/* referral related routines */
+static servinfo4_t *copy_svp(servinfo4_t *);
+static void free_knconf_contents(struct knetconfig *k);
+static char *extract_referral_point(const char *, int);
+static void setup_newsvpath(servinfo4_t *, int);
+static void update_servinfo4(servinfo4_t *, fs_location4 *,
+		struct nfs_fsl_info *, char *, int);
+
 /*
  * Initialize the vfs structure
  */
@@ -1270,7 +1280,7 @@
 		    "getlinktext_otw: initiating recovery\n"));
 
 		if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL,
-		    OP_READLINK, NULL) == FALSE) {
+		    OP_READLINK, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
 			if (!e.error)
 				(void) xdr_free(xdr_COMPOUND4res_clnt,
@@ -1457,6 +1467,198 @@
 }
 
 /*
+ * This routine updates servinfo4 structure with the new referred server
+ * info.
+ * nfsfsloc has the location related information
+ * fsp has the hostname and pathname info.
+ * new path = pathname from referral + part of orig pathname(based on nth).
+ */
+static void
+update_servinfo4(servinfo4_t *svp, fs_location4 *fsp,
+    struct nfs_fsl_info *nfsfsloc, char *orig_path, int nth)
+{
+	struct knetconfig *knconf, *svknconf;
+	struct netbuf *saddr;
+	sec_data_t	*secdata;
+	utf8string *host;
+	int i = 0, num_slashes = 0;
+	char *p, *spath, *op, *new_path;
+
+	/* Update knconf */
+	knconf = svp->sv_knconf;
+	free_knconf_contents(knconf);
+	bzero(knconf, sizeof (struct knetconfig));
+	svknconf = nfsfsloc->knconf;
+	knconf->knc_semantics = svknconf->knc_semantics;
+	knconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
+	knconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
+	knconf->knc_rdev = svknconf->knc_rdev;
+	bcopy(svknconf->knc_protofmly, knconf->knc_protofmly, KNC_STRSIZE);
+	bcopy(svknconf->knc_proto, knconf->knc_proto, KNC_STRSIZE);
+
+	/* Update server address */
+	saddr = &svp->sv_addr;
+	if (saddr->buf != NULL)
+		kmem_free(saddr->buf, saddr->maxlen);
+	saddr->buf  = kmem_alloc(nfsfsloc->addr->maxlen, KM_SLEEP);
+	saddr->len = nfsfsloc->addr->len;
+	saddr->maxlen = nfsfsloc->addr->maxlen;
+	bcopy(nfsfsloc->addr->buf, saddr->buf, nfsfsloc->addr->len);
+
+	/* Update server name */
+	host = fsp->server_val;
+	kmem_free(svp->sv_hostname, svp->sv_hostnamelen);
+	svp->sv_hostname = kmem_zalloc(host->utf8string_len + 1, KM_SLEEP);
+	bcopy(host->utf8string_val, svp->sv_hostname, host->utf8string_len);
+	svp->sv_hostname[host->utf8string_len] = '\0';
+	svp->sv_hostnamelen = host->utf8string_len + 1;
+
+	/*
+	 * Update server path.
+	 * We need to setup proper path here.
+	 * For ex., If we got a path name serv1:/rp/aaa/bbb
+	 * where aaa is a referral and points to serv2:/rpool/aa
+	 * we need to set the path to serv2:/rpool/aa/bbb
+	 * The first part of this below code generates /rpool/aa
+	 * and the second part appends /bbb to the server path.
+	 */
+	spath = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	*p++ = '/';
+	for (i = 0; i < fsp->rootpath.pathname4_len; i++) {
+		component4 *comp;
+
+		comp = &fsp->rootpath.pathname4_val[i];
+		/* If no space, null the string and bail */
+		if ((p - spath) + comp->utf8string_len + 1 > MAXPATHLEN) {
+			p = spath + MAXPATHLEN - 1;
+			spath[0] = '\0';
+			break;
+		}
+		bcopy(comp->utf8string_val, p, comp->utf8string_len);
+		p += comp->utf8string_len;
+		*p++ = '/';
+	}
+	if (fsp->rootpath.pathname4_len != 0)
+		*(p - 1) = '\0';
+	else
+		*p = '\0';
+	p = spath;
+
+	new_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	(void) strlcpy(new_path, p, MAXPATHLEN);
+	kmem_free(p, MAXPATHLEN);
+	i = strlen(new_path);
+
+	for (op = orig_path; *op; op++) {
+		if (*op == '/')
+			num_slashes++;
+		if (num_slashes == nth + 2) {
+			while (*op != '\0') {
+				new_path[i] = *op;
+				i++;
+				op++;
+			}
+			break;
+		}
+	}
+	new_path[i] = '\0';
+
+	kmem_free(svp->sv_path, svp->sv_pathlen);
+	svp->sv_pathlen = strlen(new_path) + 1;
+	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
+	bcopy(new_path, svp->sv_path, svp->sv_pathlen);
+	kmem_free(new_path, MAXPATHLEN);
+
+	/*
+	 * All the security data is specific to old server.
+	 * Clean it up except secdata which deals with mount options.
+	 * We need to inherit that data. Copy secdata into our new servinfo4.
+	 */
+	if (svp->sv_dhsec) {
+		sec_clnt_freeinfo(svp->sv_dhsec);
+		svp->sv_dhsec = NULL;
+	}
+	if (svp->sv_save_secinfo &&
+	    svp->sv_save_secinfo != svp->sv_secinfo) {
+		secinfo_free(svp->sv_save_secinfo);
+		svp->sv_save_secinfo = NULL;
+	}
+	if (svp->sv_secinfo) {
+		secinfo_free(svp->sv_secinfo);
+		svp->sv_secinfo = NULL;
+	}
+	svp->sv_currsec = NULL;
+
+	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
+	*secdata = *svp->sv_secdata;
+	secdata->data = NULL;
+	if (svp->sv_secdata) {
+		sec_clnt_freeinfo(svp->sv_secdata);
+		svp->sv_secdata = NULL;
+	}
+	svp->sv_secdata = secdata;
+}
+
+/*
+ * Resolve a referral. The referral is in the n+1th component of
+ * svp->sv_path and has a parent nfs4 file handle "fh".
+ * Upon return, the sv_path will point to the new path that has referral
+ * component resolved to its referred path and part of original path.
+ * Hostname and other address information is also updated.
+ */
+int
+resolve_referral(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, int nth,
+    nfs_fh4 *fh)
+{
+	nfs4_sharedfh_t	*sfh;
+	struct nfs_fsl_info nfsfsloc;
+	nfs4_ga_res_t garp;
+	COMPOUND4res_clnt callres;
+	fs_location4	*fsp;
+	char *nm, *orig_path;
+	int orig_pathlen = 0, ret = -1, index;
+
+	if (svp->sv_pathlen <= 0)
+		return (ret);
+
+	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
+	orig_pathlen = svp->sv_pathlen;
+	orig_path = kmem_alloc(orig_pathlen, KM_SLEEP);
+	bcopy(svp->sv_path, orig_path, orig_pathlen);
+	nm = extract_referral_point(svp->sv_path, nth);
+	setup_newsvpath(svp, nth);
+	nfs_rw_exit(&svp->sv_lock);
+
+	sfh = sfh4_get(fh, mi);
+	index = nfs4_process_referral(mi, sfh, nm, cr,
+	    &garp, &callres, &nfsfsloc);
+	sfh4_rele(&sfh);
+	kmem_free(nm, MAXPATHLEN);
+	if (index < 0) {
+		kmem_free(orig_path, orig_pathlen);
+		return (index);
+	}
+
+	fsp =  &garp.n4g_ext_res->n4g_fslocations.locations_val[index];
+	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
+	update_servinfo4(svp, fsp, &nfsfsloc, orig_path, nth);
+	nfs_rw_exit(&svp->sv_lock);
+
+	mutex_enter(&mi->mi_lock);
+	mi->mi_vfs_referral_loop_cnt++;
+	mutex_exit(&mi->mi_lock);
+
+	ret = 0;
+bad:
+	/* Free up XDR memory allocated in nfs4_process_referral() */
+	xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
+	xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
+	kmem_free(orig_path, orig_pathlen);
+
+	return (ret);
+}
+
+/*
  * Get the root filehandle for the given filesystem and server, and update
  * svp.
  *
@@ -1466,7 +1668,6 @@
  *
  * Errors are returned by the nfs4_error_t parameter.
  */
-
 static void
 nfs4getfh_otw(struct mntinfo4 *mi, servinfo4_t *svp, vtype_t *vtp,
     int flags, cred_t *cr, nfs4_error_t *ep)
@@ -1498,7 +1699,14 @@
 
 	recov_state.rs_flags = 0;
 	recov_state.rs_num_retry_despite_err = 0;
+
 recov_retry:
+	if (mi->mi_vfs_referral_loop_cnt >= NFS4_REFERRAL_LOOP_MAX) {
+		DTRACE_PROBE3(nfs4clnt__debug__referral__loop, mntinfo4 *,
+		    mi, servinfo4_t *, svp, char *, "nfs4getfh_otw");
+		nfs4_error_init(ep, EINVAL);
+		return;
+	}
 	nfs4_error_zinit(ep);
 
 	if (!recovery) {
@@ -1599,7 +1807,7 @@
 		    (CE_NOTE, "nfs4getfh_otw: initiating recovery\n"));
 
 		abort = nfs4_start_recovery(ep, mi, NULL,
-		    NULL, NULL, NULL, OP_GETFH, NULL);
+		    NULL, NULL, NULL, OP_GETFH, NULL, NULL, NULL);
 		if (!ep->error) {
 			ep->error = geterrno4(res.status);
 			(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
@@ -1628,7 +1836,8 @@
 is_link_err:
 
 	/* for non-recovery errors */
-	if (res.status && res.status != NFS4ERR_SYMLINK) {
+	if (res.status && res.status != NFS4ERR_SYMLINK &&
+	    res.status != NFS4ERR_MOVED) {
 		if (!recovery) {
 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
 			    needrecov);
@@ -1643,10 +1852,18 @@
 	 * If any intermediate component in the path is a symbolic link,
 	 * resolve the symlink, then try mount again using the new path.
 	 */
-	if (res.status == NFS4ERR_SYMLINK) {
+	if (res.status == NFS4ERR_SYMLINK || res.status == NFS4ERR_MOVED) {
 		int where;
 
 		/*
+		 * Need to call nfs4_end_op before resolve_sympath to avoid
+		 * potential nfs4_start_op deadlock.
+		 */
+		if (!recovery)
+			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
+			    needrecov);
+
+		/*
 		 * This must be from OP_LOOKUP failure. The (cfh) for this
 		 * OP_LOOKUP is a symlink node. Found out where the
 		 * OP_GETFH is for the (cfh) that is a symlink node.
@@ -1661,21 +1878,24 @@
 		where = res.array_len - 2;
 		ASSERT(where > 0);
 
-		resop = &res.array[where - 1];
-		ASSERT(resop->resop == OP_GETFH);
-		tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
-		nthcomp = res.array_len/3 - 1;
-
-		/*
-		 * Need to call nfs4_end_op before resolve_sympath to avoid
-		 * potential nfs4_start_op deadlock.
-		 */
-		if (!recovery)
-			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
-			    needrecov);
-
-		ep->error = resolve_sympath(mi, svp, nthcomp, tmpfhp, cr,
-		    flags);
+		if (res.status == NFS4ERR_SYMLINK) {
+
+			resop = &res.array[where - 1];
+			ASSERT(resop->resop == OP_GETFH);
+			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
+			nthcomp = res.array_len/3 - 1;
+			ep->error = resolve_sympath(mi, svp, nthcomp,
+			    tmpfhp, cr, flags);
+
+		} else if (res.status == NFS4ERR_MOVED) {
+
+			resop = &res.array[where - 2];
+			ASSERT(resop->resop == OP_GETFH);
+			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
+			nthcomp = res.array_len/3 - 1;
+			ep->error = resolve_referral(mi, svp, cr, nthcomp,
+			    tmpfhp);
+		}
 
 		nfs4args_lookup_free(argop, num_argops);
 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
@@ -1809,7 +2029,6 @@
 	    garp->n4g_ext_res->n4g_suppattrs | FATTR4_MANDATTR_MASK;
 
 	nfs_rw_exit(&svp->sv_lock);
-
 	nfs4args_lookup_free(argop, num_argops);
 	kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
@@ -1817,6 +2036,128 @@
 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
 }
 
+/*
+ * Save a copy of Servinfo4_t structure.
+ * We might need when there is a failure in getting file handle
+ * in case of a referral to replace servinfo4 struct and try again.
+ */
+static struct servinfo4 *
+copy_svp(servinfo4_t *nsvp)
+{
+	servinfo4_t *svp = NULL;
+	struct knetconfig *sknconf, *tknconf;
+	struct netbuf *saddr, *taddr;
+
+	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
+	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
+	svp->sv_flags = nsvp->sv_flags;
+	svp->sv_fsid = nsvp->sv_fsid;
+	svp->sv_hostnamelen = nsvp->sv_hostnamelen;
+	svp->sv_pathlen = nsvp->sv_pathlen;
+	svp->sv_supp_attrs = nsvp->sv_supp_attrs;
+
+	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
+	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
+	bcopy(nsvp->sv_hostname, svp->sv_hostname, svp->sv_hostnamelen);
+	bcopy(nsvp->sv_path, svp->sv_path, svp->sv_pathlen);
+
+	saddr = &nsvp->sv_addr;
+	taddr = &svp->sv_addr;
+	taddr->maxlen = saddr->maxlen;
+	taddr->len = saddr->len;
+	if (saddr->len > 0) {
+		taddr->buf = kmem_zalloc(saddr->maxlen, KM_SLEEP);
+		bcopy(saddr->buf, taddr->buf, saddr->len);
+	}
+
+	svp->sv_knconf = kmem_zalloc(sizeof (struct knetconfig), KM_SLEEP);
+	sknconf = nsvp->sv_knconf;
+	tknconf = svp->sv_knconf;
+	tknconf->knc_semantics = sknconf->knc_semantics;
+	tknconf->knc_rdev = sknconf->knc_rdev;
+	if (sknconf->knc_proto != NULL) {
+		tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
+		bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
+		    KNC_STRSIZE);
+	}
+	if (sknconf->knc_protofmly != NULL) {
+		tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
+		bcopy(sknconf->knc_protofmly, (char *)tknconf->knc_protofmly,
+		    KNC_STRSIZE);
+	}
+
+	if (nsvp->sv_origknconf != NULL) {
+		svp->sv_origknconf = kmem_zalloc(sizeof (struct knetconfig),
+		    KM_SLEEP);
+		sknconf = nsvp->sv_origknconf;
+		tknconf = svp->sv_origknconf;
+		tknconf->knc_semantics = sknconf->knc_semantics;
+		tknconf->knc_rdev = sknconf->knc_rdev;
+		if (sknconf->knc_proto != NULL) {
+			tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
+			bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
+			    KNC_STRSIZE);
+		}
+		if (sknconf->knc_protofmly != NULL) {
+			tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE,
+			    KM_SLEEP);
+			bcopy(sknconf->knc_protofmly,
+			    (char *)tknconf->knc_protofmly, KNC_STRSIZE);
+		}
+	}
+
+	svp->sv_secdata = copy_sec_data(nsvp->sv_secdata);
+	svp->sv_dhsec = copy_sec_data(svp->sv_dhsec);
+	/*
+	 * Rest of the security information is not copied as they are built
+	 * with the information available from secdata and dhsec.
+	 */
+	svp->sv_next = NULL;
+
+	return (svp);
+}
+
+servinfo4_t *
+restore_svp(mntinfo4_t *mi, servinfo4_t *svp, servinfo4_t *origsvp)
+{
+	servinfo4_t *srvnext, *tmpsrv;
+
+	if (strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) {
+		/*
+		 * Since the hostname changed, we must be dealing
+		 * with a referral, and the lookup failed.  We will
+		 * restore the whole servinfo4_t to what it was before.
+		 */
+		srvnext = svp->sv_next;
+		svp->sv_next = NULL;
+		tmpsrv = copy_svp(origsvp);
+		sv4_free(svp);
+		svp = tmpsrv;
+		svp->sv_next = srvnext;
+		mutex_enter(&mi->mi_lock);
+		mi->mi_servers = svp;
+		mi->mi_curr_serv = svp;
+		mutex_exit(&mi->mi_lock);
+
+	} else if (origsvp->sv_pathlen != svp->sv_pathlen) {
+
+		/*
+		 * For symlink case: restore original path because
+		 * it might have contained symlinks that were
+		 * expanded by nfsgetfh_otw before the failure occurred.
+		 */
+		nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
+		kmem_free(svp->sv_path, svp->sv_pathlen);
+		svp->sv_path =
+		    kmem_alloc(origsvp->sv_pathlen, KM_SLEEP);
+		svp->sv_pathlen = origsvp->sv_pathlen;
+		bcopy(origsvp->sv_path, svp->sv_path,
+		    origsvp->sv_pathlen);
+		nfs_rw_exit(&svp->sv_lock);
+	}
+	return (svp);
+}
+
 static ushort_t nfs4_max_threads = 8;	/* max number of active async threads */
 static uint_t nfs4_bsize = 32 * 1024;	/* client `block' size */
 static uint_t nfs4_async_clusters = 1;	/* # of reqs from each async queue */
@@ -1830,12 +2171,11 @@
 void
 nfs4_remap_root(mntinfo4_t *mi, nfs4_error_t *ep, int flags)
 {
-	struct servinfo4 *svp;
+	struct servinfo4 *svp, *origsvp;
 	vtype_t vtype;
 	nfs_fh4 rootfh;
 	int getfh_flags;
-	char *orig_sv_path;
-	int orig_sv_pathlen, num_retry;
+	int num_retry;
 
 	mutex_enter(&mi->mi_lock);
 
@@ -1861,9 +2201,7 @@
 	 * to re-lookup everything and recover.
 	 */
 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
-	orig_sv_pathlen = svp->sv_pathlen;
-	orig_sv_path = kmem_alloc(orig_sv_pathlen, KM_SLEEP);
-	bcopy(svp->sv_path, orig_sv_path, orig_sv_pathlen);
+	origsvp = copy_svp(svp);
 	nfs_rw_exit(&svp->sv_lock);
 
 	num_retry = nfs4_max_mount_retry;
@@ -1882,24 +2220,13 @@
 
 		/*
 		 * For some reason, the mount compound failed.  Before
-		 * retrying, we need to restore the original sv_path
-		 * because it might have contained symlinks that were
-		 * expanded by nfsgetfh_otw before the failure occurred.
-		 * replace current sv_path with orig sv_path -- just in case
-		 * it changed due to embedded symlinks.
+		 * retrying, we need to restore original conditions.
 		 */
-		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
-		if (orig_sv_pathlen != svp->sv_pathlen) {
-			kmem_free(svp->sv_path, svp->sv_pathlen);
-			svp->sv_path = kmem_alloc(orig_sv_pathlen, KM_SLEEP);
-			svp->sv_pathlen = orig_sv_pathlen;
-		}
-		bcopy(orig_sv_path, svp->sv_path, orig_sv_pathlen);
-		nfs_rw_exit(&svp->sv_lock);
+		svp = restore_svp(mi, svp, origsvp);
 
 	} while (num_retry-- > 0);
 
-	kmem_free(orig_sv_path, orig_sv_pathlen);
+	sv4_free(origsvp);
 
 	if (ep->error != 0 || ep->stat != 0) {
 		return;
@@ -1940,7 +2267,7 @@
 	dev_t nfs_dev;
 	int error = 0;
 	rnode4_t *rp;
-	int i;
+	int i, len;
 	struct vattr va;
 	vtype_t vtype = VNON;
 	vtype_t tmp_vtype = VNON;
@@ -1951,9 +2278,10 @@
 	struct nfs_stats *nfsstatsp;
 	nfs4_fname_t *mfname;
 	nfs4_error_t e;
-	char *orig_sv_path;
-	int orig_sv_pathlen, num_retry, removed;
+	int num_retry, removed;
 	cred_t *lcr = NULL, *tcr = cr;
+	struct servinfo4 *origsvp;
+	char *resource;
 
 	nfsstatsp = zone_getspecific(nfsstat_zone_key, nfs_zone());
 	ASSERT(nfsstatsp != NULL);
@@ -1980,6 +2308,8 @@
 		mi->mi_flags |= MI4_PUBLIC;
 	if (flags & NFSMNT_MIRRORMOUNT)
 		mi->mi_flags |= MI4_MIRRORMOUNT;
+	if (flags & NFSMNT_REFERRAL)
+		mi->mi_flags |= MI4_REFERRAL;
 	mi->mi_retrans = NFS_RETRIES;
 	if (svp->sv_knconf->knc_semantics == NC_TPI_COTS_ORD ||
 	    svp->sv_knconf->knc_semantics == NC_TPI_COTS)
@@ -2100,9 +2430,7 @@
 	 * Save server path we're attempting to mount.
 	 */
 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
-	orig_sv_pathlen = svp_head->sv_pathlen;
-	orig_sv_path = kmem_alloc(svp_head->sv_pathlen, KM_SLEEP);
-	bcopy(svp_head->sv_path, orig_sv_path, svp_head->sv_pathlen);
+	origsvp = copy_svp(svp);
 	nfs_rw_exit(&svp->sv_lock);
 
 	/*
@@ -2162,21 +2490,13 @@
 				break;
 
 			/*
-			 * replace current sv_path with orig sv_path -- just in
-			 * case it changed due to embedded symlinks.
+			 * For some reason, the mount compound failed.  Before
+			 * retrying, we need to restore original conditions.
 			 */
-			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
-			if (orig_sv_pathlen != svp->sv_pathlen) {
-				kmem_free(svp->sv_path, svp->sv_pathlen);
-				svp->sv_path = kmem_alloc(orig_sv_pathlen,
-				    KM_SLEEP);
-				svp->sv_pathlen = orig_sv_pathlen;
-			}
-			bcopy(orig_sv_path, svp->sv_path, orig_sv_pathlen);
-			nfs_rw_exit(&svp->sv_lock);
+			svp = restore_svp(mi, svp, origsvp);
+			svp_head = svp;
 
 		} while (num_retry-- > 0);
-
 		error = e.error ? e.error : geterrno4(e.stat);
 		if (error) {
 			nfs_cmn_err(error, CE_WARN,
@@ -2215,8 +2535,6 @@
 			firstsvp = svp;
 	}
 
-	kmem_free(orig_sv_path, orig_sv_pathlen);
-
 	if (firstsvp == NULL) {
 		if (error == 0)
 			error = ENOENT;
@@ -2286,6 +2604,19 @@
 	mi->mi_flags &= ~MI4_MOUNTING;
 	mutex_exit(&mi->mi_lock);
 
+	/* Update VFS with new server and path info */
+	if ((strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) ||
+	    (strcmp(svp->sv_path, origsvp->sv_path) != 0)) {
+		len = svp->sv_hostnamelen + svp->sv_pathlen;
+		resource = kmem_zalloc(len, KM_SLEEP);
+		(void) strcat(resource, svp->sv_hostname);
+		(void) strcat(resource, ":");
+		(void) strcat(resource, svp->sv_path);
+		vfs_setresource(vfsp, resource);
+		kmem_free(resource, len);
+	}
+
+	sv4_free(origsvp);
 	*rtvpp = rtvp;
 	if (lcr != NULL)
 		crfree(lcr);
@@ -2321,6 +2652,9 @@
 	 */
 	MI4_RELE(mi);
 
+	if (origsvp != NULL)
+		sv4_free(origsvp);
+
 	*rtvpp = NULL;
 	return (error);
 }
@@ -2336,7 +2670,6 @@
 	int			removed;
 
 	bool_t			must_unlock;
-	bool_t			must_rele;
 
 	nfs4_ephemeral_tree_t	*eph_tree;
 
@@ -2388,7 +2721,7 @@
 	 * again when needed.
 	 */
 	if (nfs4_ephemeral_umount(mi, flag, cr,
-	    &must_unlock, &must_rele, &eph_tree)) {
+	    &must_unlock, &eph_tree)) {
 		ASSERT(must_unlock == FALSE);
 		mutex_enter(&mi->mi_async_lock);
 		mi->mi_max_threads = omax;
@@ -2402,8 +2735,7 @@
 	 * then the file system is busy and can't be unmounted.
 	 */
 	if (check_rtable4(vfsp)) {
-		nfs4_ephemeral_umount_unlock(&must_unlock, &must_rele,
-		    &eph_tree);
+		nfs4_ephemeral_umount_unlock(&must_unlock, &eph_tree);
 
 		mutex_enter(&mi->mi_async_lock);
 		mi->mi_max_threads = omax;
@@ -2416,8 +2748,7 @@
 	 * The unmount can't fail from now on, so record any
 	 * ephemeral changes.
 	 */
-	nfs4_ephemeral_umount_activate(mi, &must_unlock,
-	    &must_rele, &eph_tree);
+	nfs4_ephemeral_umount_activate(mi, &must_unlock, &eph_tree);
 
 	/*
 	 * There are no active files that could require over-the-wire
@@ -2982,7 +3313,7 @@
 		 */
 		if (FAILOVER_MOUNT4(mi) && nfs4_try_failover(n4ep)) {
 			(void) nfs4_start_recovery(n4ep, mi, NULL,
-			    NULL, NULL, NULL, OP_SETCLIENTID, NULL);
+			    NULL, NULL, NULL, OP_SETCLIENTID, NULL, NULL, NULL);
 			/*
 			 * Don't retry here, just return and let
 			 * recovery take over.
@@ -3534,7 +3865,12 @@
 		} un_curtime;
 		verifier4	un_verifier;
 	} nfs4clientid_verifier;
-	char id_val[] = "Solaris: %s, NFSv4 kernel client";
+	/*
+	 * We change this ID string carefully and with the Solaris
+	 * NFS server behaviour in mind.  "+referrals" indicates
+	 * a client that can handle an NFSv4 referral.
+	 */
+	char id_val[] = "Solaris: %s, NFSv4 kernel client +referrals";
 	int len;
 
 	np = kmem_zalloc(sizeof (struct nfs4_server), KM_SLEEP);
@@ -3946,7 +4282,6 @@
 	int			removed;
 
 	bool_t			must_unlock;
-	bool_t			must_rele;
 	nfs4_ephemeral_tree_t	*eph_tree;
 
 	/*
@@ -4020,9 +4355,9 @@
 	 * directory tree, we are okay.
 	 */
 	if (!nfs4_ephemeral_umount(mi, flag, cr,
-	    &must_unlock, &must_rele, &eph_tree))
+	    &must_unlock, &eph_tree))
 		nfs4_ephemeral_umount_activate(mi, &must_unlock,
-		    &must_rele, &eph_tree);
+		    &eph_tree);
 
 	/*
 	 * The original purge of the dnlc via 'dounmount'
@@ -4058,3 +4393,81 @@
 	if (removed)
 		zone_rele(mi->mi_zone);
 }
+
+/* Referral related sub-routines */
+
+/* Freeup knetconfig */
+static void
+free_knconf_contents(struct knetconfig *k)
+{
+	if (k == NULL)
+		return;
+	if (k->knc_protofmly)
+		kmem_free(k->knc_protofmly, KNC_STRSIZE);
+	if (k->knc_proto)
+		kmem_free(k->knc_proto, KNC_STRSIZE);
+}
+
+/*
+ * This updates newpath variable with exact name component from the
+ * path which gave us a NFS4ERR_MOVED error.
+ * If the path is /rp/aaa/bbb and nth value is 1, aaa is returned.
+ */
+static char *
+extract_referral_point(const char *svp, int nth)
+{
+	int num_slashes = 0;
+	const char *p;
+	char *newpath = NULL;
+	int i = 0;
+
+	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	for (p = svp; *p; p++) {
+		if (*p == '/')
+			num_slashes++;
+		if (num_slashes == nth + 1) {
+			p++;
+			while (*p != '/') {
+				if (*p == '\0')
+					break;
+				newpath[i] = *p;
+				i++;
+				p++;
+			}
+			newpath[i++] = '\0';
+			break;
+		}
+	}
+	return (newpath);
+}
+
+/*
+ * This sets up a new path in sv_path to do a lookup of the referral point.
+ * If the path is /rp/aaa/bbb and the referral point is aaa,
+ * this updates /rp/aaa. This path will be used to get referral
+ * location.
+ */
+static void
+setup_newsvpath(servinfo4_t *svp, int nth)
+{
+	int num_slashes = 0, pathlen, i = 0;
+	char *newpath, *p;
+
+	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	for (p = svp->sv_path; *p; p++) {
+		newpath[i] =  *p;
+		if (*p == '/')
+			num_slashes++;
+		if (num_slashes == nth + 1) {
+			newpath[i] = '\0';
+			pathlen = strlen(newpath) + 1;
+			kmem_free(svp->sv_path, svp->sv_pathlen);
+			svp->sv_path = kmem_alloc(pathlen, KM_SLEEP);
+			svp->sv_pathlen = pathlen;
+			bcopy(newpath, svp->sv_path, pathlen);
+			break;
+		}
+		i++;
+	}
+	kmem_free(newpath, MAXPATHLEN);
+}
--- a/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Wed Dec 09 17:27:22 2009 -0600
@@ -90,6 +90,7 @@
 
 #include <sys/ddi.h>
 #include <sys/int_fmtio.h>
+#include <sys/fs/autofs.h>
 
 typedef struct {
 	nfs4_ga_res_t	*di_garp;
@@ -164,7 +165,6 @@
 static int 	nfs4_lockrelease(vnode_t *, int, offset_t, cred_t *);
 static int	nfs4_block_and_wait(clock_t *, rnode4_t *);
 static cred_t  *state_to_cred(nfs4_open_stream_t *);
-static int	vtoname(vnode_t *, char *, ssize_t);
 static void	denied_to_flk(LOCK4denied *, flock64_t *, LOCKT4args *);
 static pid_t	lo_to_pid(lock_owner4 *);
 static void	nfs4_reinstitute_local_lock_state(vnode_t *, flock64_t *,
@@ -1183,7 +1183,7 @@
 
 			abort = nfs4_start_recovery(&e, VTOMI4(dvp), dvp, vpi,
 			    NULL, lost_rqst.lr_op == OP_OPEN ?
-			    &lost_rqst : NULL, OP_OPEN, bsep);
+			    &lost_rqst : NULL, OP_OPEN, bsep, NULL, NULL);
 
 			if (bsep)
 				kmem_free(bsep, sizeof (*bsep));
@@ -1897,7 +1897,7 @@
 			abort = nfs4_start_recovery(ep,
 			    VTOMI4(vp), vp, NULL, NULL,
 			    lost_rqst.lr_op == OP_OPEN ?
-			    &lost_rqst : NULL, OP_OPEN, NULL);
+			    &lost_rqst : NULL, OP_OPEN, NULL, NULL, NULL);
 			nfs4args_copen_free(open_args);
 			goto bailout;
 		}
@@ -1936,7 +1936,7 @@
 
 		abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL,
 		    NULL, lost_rqst.lr_op == OP_OPEN ? &lost_rqst :
-		    NULL, OP_OPEN, bsep);
+		    NULL, OP_OPEN, bsep, NULL, NULL);
 
 		nfs4args_copen_free(open_args);
 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
@@ -1995,7 +1995,7 @@
 	case NFS4ERR_FHEXPIRED:
 		/* recover filehandle and retry */
 		abort = nfs4_start_recovery(ep,
-		    mi, vp, NULL, NULL, NULL, OP_OPEN, NULL);
+		    mi, vp, NULL, NULL, NULL, OP_OPEN, NULL, NULL, NULL);
 		nfs4args_copen_free(open_args);
 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
 		nfs4_end_open_seqid_sync(oop);
@@ -2511,7 +2511,7 @@
 		abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL, NULL,
 		    (close_type != CLOSE_RESEND &&
 		    lost_rqst.lr_op == OP_CLOSE) ? &lost_rqst : NULL,
-		    OP_CLOSE, bsep);
+		    OP_CLOSE, bsep, NULL, NULL);
 
 		/* drop open seq sync, and let the calling function regrab it */
 		nfs4_end_open_seqid_sync(oop);
@@ -3280,7 +3280,7 @@
 
 			abort = nfs4_start_recovery(&e,
 			    VTOMI4(vp), vp, NULL, &wargs->stateid,
-			    NULL, OP_WRITE, NULL);
+			    NULL, OP_WRITE, NULL, NULL, NULL);
 			if (!e.error) {
 				e.error = geterrno4(res.status);
 				(void) xdr_free(xdr_COMPOUND4res_clnt,
@@ -3540,7 +3540,7 @@
 			    "nfs4read: initiating recovery\n"));
 			abort = nfs4_start_recovery(&e,
 			    mi, vp, NULL, &rargs->stateid,
-			    NULL, OP_READ, NULL);
+			    NULL, OP_READ, NULL, NULL, NULL);
 			nfs4_end_fop(mi, vp, NULL, OH_READ,
 			    &recov_state, needrecov);
 			/*
@@ -3987,7 +3987,7 @@
 
 			abort = nfs4_start_recovery(&e,
 			    VTOMI4(vp), vp, NULL, NULL, NULL,
-			    OP_SETATTR, NULL);
+			    OP_SETATTR, NULL, NULL, NULL);
 			nfs4_end_op(VTOMI4(vp), vp, NULL, &recov_state,
 			    needrecov);
 			/*
@@ -4379,7 +4379,7 @@
 		    "nfs4_access: initiating recovery\n"));
 
 		if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL,
-		    NULL, OP_ACCESS, NULL) == FALSE) {
+		    NULL, OP_ACCESS, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_ACCESS,
 			    &recov_state, needrecov);
 			if (!e.error)
@@ -4536,7 +4536,7 @@
 		    "nfs4_readlink: initiating recovery\n"));
 
 		if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL,
-		    NULL, OP_READLINK, NULL) == FALSE) {
+		    NULL, OP_READLINK, NULL, NULL, NULL) == FALSE) {
 			if (!e.error)
 				(void) xdr_free(xdr_COMPOUND4res_clnt,
 				    (caddr_t)&res);
@@ -4897,7 +4897,7 @@
 
 	if (nfs4_needs_recovery(&e, FALSE, unldvp->v_vfsp)) {
 		if (nfs4_start_recovery(&e, VTOMI4(unldvp), unldvp, NULL,
-		    NULL, NULL, OP_REMOVE, NULL) == FALSE) {
+		    NULL, NULL, OP_REMOVE, NULL, NULL, NULL) == FALSE) {
 			if (!e.error)
 				(void) xdr_free(xdr_COMPOUND4res_clnt,
 				    (caddr_t)&res);
@@ -5316,6 +5316,17 @@
 
 	rfs4call(VTOMI4(dvp), &args, &res, cr, &doqueue, 0, &e);
 
+	if (!isdotdot && res.status == NFS4ERR_MOVED) {
+		e.error = nfs4_setup_referral(dvp, nm, vpp, cr);
+		if (e.error != 0 && *vpp != NULL)
+			VN_RELE(*vpp);
+		nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP,
+		    &recov_state, FALSE);
+		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
+		kmem_free(argop, argoplist_size);
+		return (e.error);
+	}
+
 	if (nfs4_needs_recovery(&e, FALSE, dvp->v_vfsp)) {
 		/*
 		 * For WRONGSEC of a non-dotdot case, send secinfo directly
@@ -5343,7 +5354,7 @@
 		}
 
 		if (nfs4_start_recovery(&e, mi, dvp, NULL, NULL, NULL,
-		    OP_LOOKUP, NULL) == FALSE) {
+		    OP_LOOKUP, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP,
 			    &recov_state, TRUE);
 
@@ -5743,6 +5754,17 @@
 
 	rfs4call(VTOMI4(dvp), &args, &res, cr, &doqueue, 0, &e);
 
+	if (!isdotdot && res.status == NFS4ERR_MOVED) {
+		e.error = nfs4_setup_referral(dvp, nm, vpp, cr);
+		if (e.error != 0 && *vpp != NULL)
+			VN_RELE(*vpp);
+		nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP,
+		    &recov_state, FALSE);
+		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
+		kmem_free(argop, argoplist_size);
+		return (e.error);
+	}
+
 	if (nfs4_needs_recovery(&e, FALSE, dvp->v_vfsp)) {
 		/*
 		 * For WRONGSEC of a non-dotdot case, send secinfo directly
@@ -5768,7 +5790,7 @@
 		}
 
 		if (nfs4_start_recovery(&e, mi, dvp, NULL, NULL, NULL,
-		    OP_LOOKUP, NULL) == FALSE) {
+		    OP_LOOKUP, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP,
 			    &recov_state, TRUE);
 
@@ -6374,7 +6396,7 @@
 
 		abort = nfs4_start_recovery(&e,
 		    VTOMI4(dvp), dvp, NULL, NULL, NULL,
-		    OP_OPENATTR, NULL);
+		    OP_OPENATTR, NULL, NULL, NULL);
 		nfs4_end_op(VTOMI4(dvp), dvp, NULL, &recov_state, needrecov);
 		if (!e.error) {
 			e.error = geterrno4(res.status);
@@ -6986,7 +7008,7 @@
 
 	if (needrecov) {
 		if (nfs4_start_recovery(&e, mi, dvp, NULL, NULL, NULL,
-		    OP_CREATE, NULL) == FALSE) {
+		    OP_CREATE, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_op(mi, dvp, NULL, &recov_state,
 			    needrecov);
 			need_end_op = FALSE;
@@ -7345,7 +7367,7 @@
 
 	if (needrecov) {
 		if (nfs4_start_recovery(&e, VTOMI4(dvp), dvp,
-		    NULL, NULL, NULL, OP_REMOVE, NULL) == FALSE) {
+		    NULL, NULL, NULL, OP_REMOVE, NULL, NULL, NULL) == FALSE) {
 			if (!e.error)
 				(void) xdr_free(xdr_COMPOUND4res_clnt,
 				    (caddr_t)&res);
@@ -7519,7 +7541,7 @@
 		bool_t abort;
 
 		abort = nfs4_start_recovery(&e, VTOMI4(svp), svp, tdvp,
-		    NULL, NULL, OP_LINK, NULL);
+		    NULL, NULL, OP_LINK, NULL, NULL, NULL);
 		if (abort == FALSE) {
 			nfs4_end_op(VTOMI4(svp), svp, tdvp, &recov_state,
 			    needrecov);
@@ -8187,7 +8209,7 @@
 
 	if (needrecov) {
 		if (nfs4_start_recovery(&e, mi, odvp, ndvp, NULL, NULL,
-		    OP_RENAME, NULL) == FALSE) {
+		    OP_RENAME, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_op(mi, odvp, ndvp, &recov_state, needrecov);
 			if (!e.error)
 				(void) xdr_free(xdr_COMPOUND4res_clnt,
@@ -8444,7 +8466,7 @@
 		bool_t abort;
 
 		abort = nfs4_start_recovery(&e, mi, odvp, ndvp, NULL, NULL,
-		    OP_RENAME, NULL);
+		    OP_RENAME, NULL, NULL, NULL);
 		if (abort == FALSE) {
 			nfs4_end_fop(mi, odvp, ndvp, OH_VFH_RENAME,
 			    &recov_state, needrecov);
@@ -8722,7 +8744,7 @@
 
 	if (needrecov) {
 		if (nfs4_start_recovery(&e, VTOMI4(dvp), dvp, NULL, NULL,
-		    NULL, OP_REMOVE, NULL) == FALSE) {
+		    NULL, OP_REMOVE, NULL, NULL, NULL) == FALSE) {
 			if (!e.error)
 				(void) xdr_free(xdr_COMPOUND4res_clnt,
 				    (caddr_t)&res);
@@ -9319,7 +9341,7 @@
 		    "nfs4readdir: initiating recovery.\n"));
 
 		abort = nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL,
-		    NULL, OP_READDIR, NULL);
+		    NULL, OP_READDIR, NULL, NULL, NULL);
 		if (abort == FALSE) {
 			nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_READDIR,
 			    &recov_state, needrecov);
@@ -11756,7 +11778,7 @@
 
 	if (needrecov) {
 		if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL,
-		    NULL, OP_COMMIT, NULL) == FALSE) {
+		    NULL, OP_COMMIT, NULL, NULL, NULL) == FALSE) {
 			nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_COMMIT,
 			    &recov_state, needrecov);
 			if (!e.error)
@@ -12318,6 +12340,12 @@
 	if (error) /* EINVAL */
 		return (error);
 
+	/*
+	 * If this is a referral stub, don't try to go OTW for an ACL
+	 */
+	if (RP_ISSTUB_REFERRAL(VTOR4(vp)))
+		return (fs_fab_acl(vp, vsecattr, flag, cr, ct));
+
 	if (mi->mi_flags & MI4_ACL) {
 		/*
 		 * Check if the data is cached and the cache is valid.  If it
@@ -12752,8 +12780,8 @@
 				    vp, 0, args.ctag,
 				    open_confirm_args->seqid);
 
-			abort = nfs4_start_recovery(ep, VTOMI4(vp), vp,
-			    NULL, NULL, NULL, OP_OPEN_CONFIRM, bsep);
+			abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL,
+			    NULL, NULL, OP_OPEN_CONFIRM, bsep, NULL, NULL);
 			if (bsep) {
 				kmem_free(bsep, sizeof (*bsep));
 				if (num_bseqid_retryp &&
@@ -13686,7 +13714,7 @@
 		abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL, NULL,
 		    (lost_rqstp && (lost_rqstp->lr_op == OP_LOCK ||
 		    lost_rqstp->lr_op == OP_LOCKU)) ? lost_rqstp :
-		    NULL, op, bsep);
+		    NULL, op, bsep, NULL, NULL);
 
 		if (bsep)
 			kmem_free(bsep, sizeof (*bsep));
@@ -14215,7 +14243,7 @@
 			    VTOMI4(vp), vp, NULL, NULL,
 			    (lost_rqst.lr_op == OP_LOCK ||
 			    lost_rqst.lr_op == OP_LOCKU) ?
-			    &lost_rqst : NULL, OP_LOCKU, NULL);
+			    &lost_rqst : NULL, OP_LOCKU, NULL, NULL, NULL);
 			lock_owner_rele(lop);
 			lop = NULL;
 		}
@@ -15303,7 +15331,7 @@
 		have_sync_lock = 0;
 		(void) nfs4_start_recovery(ep, mi, vp, NULL, NULL,
 		    lost_rqst.lr_op == OP_CLOSE ?
-		    &lost_rqst : NULL, OP_CLOSE, NULL);
+		    &lost_rqst : NULL, OP_CLOSE, NULL, NULL, NULL);
 		close_failed = 1;
 		force_close = 0;
 		goto close_cleanup;
@@ -15387,7 +15415,7 @@
 			abort = nfs4_start_recovery(ep, mi, vp, NULL, NULL,
 			    new_lost_rqst.lr_op == OP_OPEN_DOWNGRADE ?
 			    &new_lost_rqst : NULL, OP_OPEN_DOWNGRADE,
-			    bsep);
+			    bsep, NULL, NULL);
 			if (odg_cred_otw)
 				crfree(odg_cred_otw);
 			if (bsep)
@@ -15976,5 +16004,5 @@
 	(void) nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL,
 	    (req.lr_op == OP_LOCK || req.lr_op == OP_LOCKU) ?
 	    &req : NULL, flk->l_type == F_UNLCK ? OP_LOCKU : OP_LOCK,
-	    NULL);
-}
+	    NULL, NULL, NULL);
+}
--- a/usr/src/uts/common/fs/nfs/nfs4_xdr.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs4_xdr.c	Wed Dec 09 17:27:22 2009 -0600
@@ -42,7 +42,16 @@
 #include <nfs/nfs4.h>
 #include <nfs/nfs4_clnt.h>
 #include <sys/sdt.h>
+#include <sys/mkdev.h>
 #include <rpc/rpc_rdma.h>
+#include <rpc/xdr.h>
+
+#define	xdr_dev_t xdr_u_int
+
+extern bool_t xdr_netbuf(XDR *, struct netbuf *);
+extern bool_t xdr_vector(XDR *, char *, const uint_t, const uint_t,
+	const xdrproc_t);
+bool_t xdr_knetconfig(XDR *, struct knetconfig *);
 
 bool_t
 xdr_bitmap4(XDR *xdrs, bitmap4 *objp)
@@ -146,6 +155,143 @@
 }
 
 /*
+ * used by NFSv4 referrals to get info needed for NFSv4 referral mount.
+ */
+bool_t
+xdr_nfs_fsl_info(XDR *xdrs, struct nfs_fsl_info *objp)
+{
+
+	if (!xdr_u_int(xdrs, &objp->netbuf_len))
+		return (FALSE);
+	if (!xdr_u_int(xdrs, &objp->netnm_len))
+		return (FALSE);
+	if (!xdr_u_int(xdrs, &objp->knconf_len))
+		return (FALSE);
+
+#if defined(_LP64)
+	/*
+	 * The object can come from a 32-bit binary; nfsmapid.
+	 * To be safe we double the size of the knetconfig to
+	 * allow some buffering for decoding.
+	 */
+	if (xdrs->x_op == XDR_DECODE)
+		objp->knconf_len += sizeof (struct knetconfig);
+#endif
+
+	if (!xdr_string(xdrs, &objp->netname, ~0))
+		return (FALSE);
+	if (!xdr_pointer(xdrs, (char **)&objp->addr, objp->netbuf_len,
+	    (xdrproc_t)xdr_netbuf))
+		return (FALSE);
+	if (!xdr_pointer(xdrs, (char **)&objp->knconf,
+	    objp->knconf_len, (xdrproc_t)xdr_knetconfig))
+		return (FALSE);
+	return (TRUE);
+}
+
+bool_t
+xdr_knetconfig(XDR *xdrs, struct knetconfig *objp)
+{
+	rpc_inline_t *buf;
+	u_longlong_t dev64;
+#if !defined(_LP64)
+	uint32_t major, minor;
+#endif
+	int i;
+
+	if (!xdr_u_int(xdrs, &objp->knc_semantics))
+		return (FALSE);
+	if (xdrs->x_op == XDR_DECODE) {
+		objp->knc_protofmly = (((char *)objp) +
+		    sizeof (struct knetconfig));
+		objp->knc_proto = objp->knc_protofmly + KNC_STRSIZE;
+	}
+	if (!xdr_opaque(xdrs, objp->knc_protofmly, KNC_STRSIZE))
+		return (FALSE);
+	if (!xdr_opaque(xdrs, objp->knc_proto, KNC_STRSIZE))
+		return (FALSE);
+
+	/*
+	 * For interoperability between 32-bit daemon and 64-bit kernel,
+	 * we always treat dev_t as 64-bit number and do the expanding
+	 * or compression of dev_t as needed.
+	 * We have to hand craft the conversion since there is no available
+	 * function in ddi.c. Besides ddi.c is available only in the kernel
+	 * and we want to keep both user and kernel of xdr_knetconfig() the
+	 * same for consistency.
+	 */
+	if (xdrs->x_op == XDR_ENCODE) {
+#if defined(_LP64)
+		dev64 = objp->knc_rdev;
+#else
+		major = (objp->knc_rdev >> NBITSMINOR32) & MAXMAJ32;
+		minor = objp->knc_rdev & MAXMIN32;
+		dev64 = (((unsigned long long)major) << NBITSMINOR64) | minor;
+#endif
+		if (!xdr_u_longlong_t(xdrs, &dev64))
+			return (FALSE);
+	}
+	if (xdrs->x_op == XDR_DECODE) {
+#if defined(_LP64)
+		if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&objp->knc_rdev))
+			return (FALSE);
+#else
+		if (!xdr_u_longlong_t(xdrs, &dev64))
+			return (FALSE);
+
+		major = (dev64 >> NBITSMINOR64) & L_MAXMAJ32;
+		minor = dev64 & L_MAXMIN32;
+		objp->knc_rdev = (major << L_BITSMINOR32) | minor;
+#endif
+	}
+
+	if (xdrs->x_op == XDR_ENCODE) {
+		buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
+			    sizeof (uint_t), (xdrproc_t)xdr_u_int))
+				return (FALSE);
+		} else {
+			uint_t *genp;
+
+			for (i = 0, genp = objp->knc_unused;
+			    i < 8; i++) {
+#if defined(_LP64) || defined(_KERNEL)
+				IXDR_PUT_U_INT32(buf, *genp++);
+#else
+				IXDR_PUT_U_LONG(buf, *genp++);
+#endif
+			}
+		}
+		return (TRUE);
+	} else if (xdrs->x_op == XDR_DECODE) {
+		buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
+			    sizeof (uint_t), (xdrproc_t)xdr_u_int))
+				return (FALSE);
+		} else {
+			uint_t *genp;
+
+			for (i = 0, genp = objp->knc_unused;
+			    i < 8; i++) {
+#if defined(_LP64) || defined(_KERNEL)
+					*genp++ = IXDR_GET_U_INT32(buf);
+#else
+					*genp++ = IXDR_GET_U_LONG(buf);
+#endif
+			}
+		}
+		return (TRUE);
+	}
+
+	if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
+	    sizeof (uint_t), (xdrproc_t)xdr_u_int))
+		return (FALSE);
+	return (TRUE);
+}
+
+/*
  * XDR_INLINE decode a filehandle.
  */
 bool_t
@@ -492,8 +638,12 @@
 static bool_t
 xdr_fs_location4(XDR *xdrs, fs_location4 *objp)
 {
+	if (xdrs->x_op == XDR_DECODE) {
+		objp->server_val = NULL;
+		objp->rootpath.pathname4_val = NULL;
+	}
 	if (!xdr_array(xdrs, (char **)&objp->server_val,
-	    (uint_t *)&objp->server_len, NFS4_FS_LOCATIONS_LIMIT,
+	    (uint_t *)&objp->server_len, NFS4_MAX_UTF8STRING,
 	    sizeof (utf8string), (xdrproc_t)xdr_utf8string))
 		return (FALSE);
 	return (xdr_array(xdrs, (char **)&objp->rootpath.pathname4_val,
@@ -560,6 +710,11 @@
 bool_t
 xdr_fattr4_fs_locations(XDR *xdrs, fattr4_fs_locations *objp)
 {
+	if (xdrs->x_op == XDR_DECODE) {
+		objp->fs_root.pathname4_len = 0;
+		objp->fs_root.pathname4_val = NULL;
+		objp->locations_val = NULL;
+	}
 	if (!xdr_array(xdrs, (char **)&objp->fs_root.pathname4_val,
 	    (uint_t *)&objp->fs_root.pathname4_len,
 	    NFS4_MAX_PATHNAME4,
@@ -930,7 +1085,9 @@
 	    FATTR4_HOMOGENEOUS_MASK)) {
 
 		if (resbmap & FATTR4_FS_LOCATIONS_MASK) {
-			ASSERT(0);
+			if (!xdr_fattr4_fs_locations(xdrs,
+			    &gesp->n4g_fslocations))
+				return (FALSE);
 		}
 		if (resbmap & FATTR4_HIDDEN_MASK) {
 			ASSERT(0);
@@ -2253,7 +2410,9 @@
 static bool_t
 xdr_ga_res(XDR *xdrs, GETATTR4res *objp, GETATTR4args *aobjp)
 {
+#ifdef INLINE
 	uint32_t *ptr;
+#endif
 	bitmap4 resbmap;
 	uint32_t attrlen;
 
@@ -2307,11 +2466,13 @@
 	}
 
 	/* Check to see if the attrs can be inlined and go for it if so */
+#ifdef INLINE
 	if (!(resbmap & FATTR4_ACL_MASK) &&
 	    (ptr = (uint32_t *)XDR_INLINE(xdrs, attrlen)) != NULL)
 		return (xdr_ga_fattr_res_inline(ptr, &objp->ga_res,
 		    resbmap, aobjp->attr_request, aobjp->mi, NULL));
 	else
+#endif
 		return (xdr_ga_fattr_res(xdrs, &objp->ga_res,
 		    resbmap, aobjp->attr_request, aobjp->mi, NULL));
 }
@@ -4300,6 +4461,7 @@
 {
 	int i;
 	nfs_resop4 *array = *arrayp;
+	nfs4_ga_res_t *gr;
 
 	/*
 	 * Optimized XDR_FREE only results array
@@ -4319,10 +4481,15 @@
 		case OP_GETATTR:
 			if (array[i].nfs_resop4_u.opgetattr.status != NFS4_OK)
 				continue;
-			if (array[i].nfs_resop4_u.opgetattr.ga_res.n4g_ext_res)
-				kmem_free(array[i].nfs_resop4_u.opgetattr.
-				    ga_res.n4g_ext_res,
+
+			gr = &array[i].nfs_resop4_u.opgetattr.ga_res;
+			if (gr->n4g_ext_res) {
+				if (gr->n4g_resbmap & FATTR4_FS_LOCATIONS_MASK)
+					(void) xdr_fattr4_fs_locations(xdrs,
+					    &gr->n4g_ext_res->n4g_fslocations);
+				kmem_free(gr->n4g_ext_res,
 				    sizeof (struct nfs4_ga_ext_res));
+			}
 			continue;
 		case OP_GETFH:
 			if (array[i].nfs_resop4_u.opgetfh.status != NFS4_OK)
--- a/usr/src/uts/common/fs/nfs/nfs_srv.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs_srv.c	Wed Dec 09 17:27:22 2009 -0600
@@ -117,6 +117,10 @@
 
 	/* check for overflows */
 	if (!error) {
+		/* Lie about the object type for a referral */
+		if (vn_is_nfs_reparse(vp, cr))
+			va.va_type = VLNK;
+
 		acl_perm(vp, exi, &va, cr);
 		error = vattr_to_nattr(&va, &ns->ns_attr);
 	}
@@ -489,6 +493,7 @@
 	struct vattr va;
 	struct sockaddr *ca;
 	char *name = NULL;
+	int is_referral = 0;
 
 	vp = nfs_fhtovp(fhp, exi);
 	if (vp == NULL) {
@@ -515,11 +520,15 @@
 		return;
 	}
 
+	/* We lied about the object type for a referral */
+	if (vn_is_nfs_reparse(vp, cr))
+		is_referral = 1;
+
 	/*
 	 * XNFS and RFC1094 require us to return ENXIO if argument
 	 * is not a link. BUGID 1138002.
 	 */
-	if (vp->v_type != VLNK) {
+	if (vp->v_type != VLNK && !is_referral) {
 		VN_RELE(vp);
 		rl->rl_data = NULL;
 		rl->rl_status = NFSERR_NXIO;
@@ -531,28 +540,53 @@
 	 */
 	rl->rl_data = kmem_alloc(NFS_MAXPATHLEN, KM_SLEEP);
 
-	/*
-	 * Set up io vector to read sym link data
-	 */
-	iov.iov_base = rl->rl_data;
-	iov.iov_len = NFS_MAXPATHLEN;
-	uio.uio_iov = &iov;
-	uio.uio_iovcnt = 1;
-	uio.uio_segflg = UIO_SYSSPACE;
-	uio.uio_extflg = UIO_COPY_CACHED;
-	uio.uio_loffset = (offset_t)0;
-	uio.uio_resid = NFS_MAXPATHLEN;
-
-	/*
-	 * Do the readlink.
-	 */
-	error = VOP_READLINK(vp, &uio, cr, NULL);
+	if (is_referral) {
+		char *s;
+		size_t strsz;
+
+		/* Get an artificial symlink based on a referral */
+		s = build_symlink(vp, cr, &strsz);
+		global_svstat_ptr[2][NFS_REFERLINKS].value.ui64++;
+		DTRACE_PROBE2(nfs2serv__func__referral__reflink,
+		    vnode_t *, vp, char *, s);
+		if (s == NULL)
+			error = EINVAL;
+		else {
+			error = 0;
+			(void) strlcpy(rl->rl_data, s, NFS_MAXPATHLEN);
+			rl->rl_count = (uint32_t)MIN(strsz, NFS_MAXPATHLEN);
+			kmem_free(s, strsz);
+		}
+
+	} else {
+
+		/*
+		 * Set up io vector to read sym link data
+		 */
+		iov.iov_base = rl->rl_data;
+		iov.iov_len = NFS_MAXPATHLEN;
+		uio.uio_iov = &iov;
+		uio.uio_iovcnt = 1;
+		uio.uio_segflg = UIO_SYSSPACE;
+		uio.uio_extflg = UIO_COPY_CACHED;
+		uio.uio_loffset = (offset_t)0;
+		uio.uio_resid = NFS_MAXPATHLEN;
+
+		/*
+		 * Do the readlink.
+		 */
+		error = VOP_READLINK(vp, &uio, cr, NULL);
+
+		rl->rl_count = (uint32_t)(NFS_MAXPATHLEN - uio.uio_resid);
+
+		if (!error)
+			rl->rl_data[rl->rl_count] = '\0';
+
+	}
+
 
 	VN_RELE(vp);
 
-	rl->rl_count = (uint32_t)(NFS_MAXPATHLEN - uio.uio_resid);
-	rl->rl_data[rl->rl_count] = '\0';
-
 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
 	name = nfscmd_convname(ca, exi, rl->rl_data,
 	    NFSCMD_CONV_OUTBOUND, MAXPATHLEN);
--- a/usr/src/uts/common/fs/nfs/nfs_stats.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/nfs/nfs_stats.c	Wed Dec 09 17:27:22 2009 -0600
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/kstat.h>
 #include <sys/zone.h>
@@ -87,6 +85,8 @@
 static const kstat_named_t svstat_tmpl[] = {
 	{ "calls",	KSTAT_DATA_UINT64 },
 	{ "badcalls",	KSTAT_DATA_UINT64 },
+	{ "referrals",	KSTAT_DATA_UINT64 },
+	{ "referlinks",	KSTAT_DATA_UINT64 },
 };
 
 /* Points to the global zone server kstat data for all nfs versions */
@@ -108,7 +108,7 @@
 
 	for (vers = NFS_VERSION; vers <= NFS_V4; vers++) {
 		svstatp[vers] = nfsstat_zone_init_common(zoneid, "nfs", vers,
-			    "nfs_server", svstat_tmpl, sizeof (svstat_tmpl));
+		    "nfs_server", svstat_tmpl, sizeof (svstat_tmpl));
 		if (zoneid == GLOBAL_ZONEID)
 			global_svstat_ptr[vers] = svstatp[vers];
 	}
--- a/usr/src/uts/common/fs/vnode.c	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/fs/vnode.c	Wed Dec 09 17:27:22 2009 -0600
@@ -4441,3 +4441,32 @@
 
 	return (0);
 }
+
+/*
+ * Function to check whether a symlink is a reparse point.
+ * Return B_TRUE if it is a reparse point, else return B_FALSE
+ */
+boolean_t
+vn_is_reparse(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+	xvattr_t xvattr;
+	xoptattr_t *xoap;
+
+	if ((vp->v_type != VLNK) ||
+	    !(vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR)))
+		return (B_FALSE);
+
+	xva_init(&xvattr);
+	xoap = xva_getxoptattr(&xvattr);
+	ASSERT(xoap);
+	XVA_SET_REQ(&xvattr, XAT_REPARSE);
+
+	if (VOP_GETATTR(vp, &xvattr.xva_vattr, 0, cr, ct))
+		return (B_FALSE);
+
+	if ((!(xvattr.xva_vattr.va_mask & AT_XVATTR)) ||
+	    (!(XVA_ISSET_RTN(&xvattr, XAT_REPARSE))))
+		return (B_FALSE);
+
+	return (xoap->xoa_reparse ? B_TRUE : B_FALSE);
+}
--- a/usr/src/uts/common/nfs/export.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/export.h	Wed Dec 09 17:27:22 2009 -0600
@@ -295,6 +295,7 @@
 /* Forward declarations */
 struct exportinfo;
 struct exp_visible;
+struct svc_req;
 
 /*
  * Treenodes are used to build tree representing every node which is part
@@ -409,6 +410,7 @@
 	struct exp_visible	*exi_visible;
 	struct charset_cache	*exi_charset;
 	unsigned		exi_volatile_dev:1;
+	unsigned		exi_moved:1;
 #ifdef VOLATILE_FH_TEST
 	uint32_t		exi_volatile_id;
 	struct ex_vol_rename	*exi_vol_rename;
@@ -512,6 +514,9 @@
 extern int	export_unlink(fsid_t *, fid_t *, vnode_t *,
 			struct exportinfo **);
 extern vnode_t *untraverse(vnode_t *);
+extern int	vn_is_nfs_reparse(vnode_t *, cred_t *);
+extern int	client_is_downrev(struct svc_req *);
+extern char    *build_symlink(vnode_t *, cred_t *, size_t *);
 
 /*
  * Functions that handle the NFSv4 server namespace
--- a/usr/src/uts/common/nfs/mount.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/mount.h	Wed Dec 09 17:27:22 2009 -0600
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -31,8 +31,6 @@
 #ifndef	_NFS_MOUNT_H
 #define	_NFS_MOUNT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -157,11 +155,9 @@
 #define	NFSMNT_TRYRDMA		0x8000000 /* Try RDMA mount,no proto advised */
 #define	NFSMNT_DORDMA		0x10000000 /* Do an RDMA mount, regardless */
 #define	NFSMNT_MIRRORMOUNT	0x20000000 /* Is a mirrormount */
+#define	NFSMNT_REFERRAL		0x40000000 /* Is a referral */
 
-/*
- * This will have to change when we do referrals.
- */
-#define	NFSMNT_EPHEMERAL	NFSMNT_MIRRORMOUNT
+#define	NFSMNT_EPHEMERAL	(NFSMNT_MIRRORMOUNT | NFSMNT_REFERRAL)
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/nfs/nfs.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/nfs.h	Wed Dec 09 17:27:22 2009 -0600
@@ -884,7 +884,8 @@
 #define	NATIVEPATH	0x02	/* Native path, i.e., via mount protocol */
 #define	SECURITY_QUERY	0x04	/* Security query */
 
-enum nfs_svccounts {NFS_CALLS, NFS_BADCALLS}; /* index for svstat_ptr */
+/* index for svstat_ptr */
+enum nfs_svccounts {NFS_CALLS, NFS_BADCALLS, NFS_REFERRALS, NFS_REFERLINKS};
 
 /*	function defs for NFS kernel */
 extern int	nfs_waitfor_purge_complete(vnode_t *);
--- a/usr/src/uts/common/nfs/nfs4.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/nfs4.h	Wed Dec 09 17:27:22 2009 -0600
@@ -460,6 +460,7 @@
  * ss_remove - indicates that the rfs4_client_destroy function should
  * 		clean up stable storage file.
  * forced_expire - set if the sysadmin has used clear_locks for this client.
+ * no_referrals - set if the client is Solaris and pre-dates referrals
  * deleg_revoked - how many delegations have been revoked for this client?
  *
  * cp_confirmed - this refers to a confirmed client struct that has
@@ -499,6 +500,17 @@
 } rfs4_client_t;
 
 /*
+ * ClntIP struct - holds the diagnosis about whether the client
+ * cannot support referrals.  Set to true for old Solaris clients.
+ */
+
+typedef struct rfs4_clntip {
+	rfs4_dbe_t		*ri_dbe;
+	struct sockaddr_storage ri_addr;
+	unsigned		ri_no_referrals:1;
+} rfs4_clntip_t;
+
+/*
  * The openowner contains the client supplied open_owner4 as well as
  * the matching sequence id and is used to track the client's usage of
  * the open_owner4.  Note that a reply is saved here as well for
@@ -775,6 +787,7 @@
 extern	rfs4_client_t	*rfs4_findclient(nfs_client_id4 *,
 					bool_t *, rfs4_client_t *);
 extern	rfs4_client_t	*rfs4_findclient_by_id(clientid4, bool_t);
+extern	rfs4_client_t	*rfs4_findclient_by_addr(struct sockaddr *);
 extern	void		rfs4_client_rele(rfs4_client_t *);
 extern	void		rfs4_client_close(rfs4_client_t *);
 extern	void		rfs4_client_state_remove(rfs4_client_t *);
@@ -783,6 +796,10 @@
 extern	bool_t		rfs4_lease_expired(rfs4_client_t *);
 extern	nfsstat4	rfs4_check_clientid(clientid4 *, int);
 
+/* rfs4_clntip_t handling */
+extern	rfs4_clntip_t	*rfs4_find_clntip(struct sockaddr *, bool_t *);
+extern	void		rfs4_invalidate_clntip(struct sockaddr *);
+
 /* rfs4_openowner_t handling */
 extern	rfs4_openowner_t *rfs4_findopenowner(open_owner4 *, bool_t *, seqid4);
 extern	void		rfs4_update_open_sequence(rfs4_openowner_t *);
@@ -1095,6 +1112,7 @@
 						/* rdattr_error */
 	nfsstat4	rdattr_error;	/* used for per-entry status */
 					/* (if rdattr_err) */
+	bool_t		is_referral;	/* because sometimes we tell lies */
 	bool_t		mntdfid_set;
 	fattr4_mounted_on_fileid
 			mounted_on_fileid;
@@ -1302,6 +1320,11 @@
 extern void	vs_ace4_destroy(vsecattr_t *);
 extern void	vs_aent_destroy(vsecattr_t *);
 
+extern int	vn_find_nfs_record(vnode_t *, nvlist_t **, char **, char **);
+extern int	vn_is_nfs_reparse(vnode_t *, cred_t *);
+extern fs_locations4 *fetch_referral(vnode_t *, cred_t *);
+extern char	*build_symlink(vnode_t *, cred_t *, size_t *);
+
 extern int	stateid4_cmp(stateid4 *, stateid4 *);
 
 extern vtype_t	nf4_to_vt[];
--- a/usr/src/uts/common/nfs/nfs4_attr.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/nfs4_attr.h	Wed Dec 09 17:27:22 2009 -0600
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _NFS4_ATTR_H
 #define	_NFS4_ATTR_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -436,6 +433,7 @@
 	 * ACL4_SUPPORT_ALARM_ACL
 	 */
 	fattr4_aclsupport		n4g_aclsupport;
+	fattr4_fs_locations		n4g_fslocations;
 } nfs4_ga_ext_res_t;
 
 extern bitmap4 rfs4_supported_attrs;
--- a/usr/src/uts/common/nfs/nfs4_clnt.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/nfs4_clnt.h	Wed Dec 09 17:27:22 2009 -0600
@@ -46,6 +46,7 @@
 #include <sys/avl.h>
 #include <sys/list.h>
 #include <rpc/auth.h>
+#include <sys/door.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -161,6 +162,8 @@
 struct clstat4 {
 	kstat_named_t	calls;			/* client requests */
 	kstat_named_t	badcalls;		/* rpc failures */
+	kstat_named_t	referrals;		/* referrals */
+	kstat_named_t	referlinks;		/* referrals as symlinks */
 	kstat_named_t	clgets;			/* client handle gets */
 	kstat_named_t	cltoomany;		/* client handle cache misses */
 #ifdef DEBUG
@@ -302,6 +305,7 @@
 	TAG_FSINFO,
 	TAG_GET_SYMLINK,
 	TAG_GETATTR,
+	TAG_GETATTR_FSLOCATION,
 	TAG_INACTIVE,
 	TAG_LINK,
 	TAG_LOCK,
@@ -374,6 +378,8 @@
 			{0x67657420, 0x736c6e6b, 0x20747874}},	\
 		{TAG_GETATTR,		"getattr",		\
 			{0x67657461, 0x74747220, 0x20202020}},	\
+		{TAG_GETATTR_FSLOCATION, "getattr fslocation",	\
+			{0x67657461, 0x74747220, 0x66736c6f}},	\
 		{TAG_INACTIVE,		"inactive",		\
 			{0x696e6163, 0x74697665, 0x20202020}},	\
 		{TAG_LINK,		"link",			\
@@ -553,7 +559,8 @@
 
 /*
  * Static server information.
- * These fields are read-only once they are initialized:
+ * These fields are read-only once they are initialized; sv_lock
+ * should be held as writer if they are changed during mount:
  *	sv_addr
  *	sv_dhsec
  *	sv_hostname
@@ -699,7 +706,8 @@
 	NR_DELAY,
 	NR_LOST_LOCK,
 	NR_LOST_STATE_RQST,
-	NR_STALE
+	NR_STALE,
+	NR_MOVED
 } nfs4_recov_t;
 
 /*
@@ -709,6 +717,8 @@
 #define	NFS4_MSG_MAX	100
 extern int nfs4_msg_max;
 
+#define	NFS4_REFERRAL_LOOP_MAX	20
+
 typedef enum {
 	RE_BAD_SEQID,
 	RE_BADHANDLE,
@@ -729,7 +739,8 @@
 	RE_UNEXPECTED_ERRNO,
 	RE_UNEXPECTED_STATUS,
 	RE_WRONGSEC,
-	RE_LOST_STATE_BAD_OP
+	RE_LOST_STATE_BAD_OP,
+	RE_REFERRAL
 } nfs4_event_type_t;
 
 typedef enum {
@@ -1033,6 +1044,10 @@
 
 	uint_t mi_srvset_cnt; /* increment when changing the nfs4_server_t */
 	struct nfs4_server *mi_srv; /* backpointer to nfs4_server_t */
+	/*
+	 * Referral related info.
+	 */
+	int		mi_vfs_referral_loop_cnt;
 } mntinfo4_t;
 
 /*
@@ -1085,7 +1100,7 @@
 #define	MI4_ACL			 0x2000
 /* MI4_MIRRORMOUNT is also defined in nfsstat.c */
 #define	MI4_MIRRORMOUNT		 0x4000
-/* 0x8000 is available */
+#define	MI4_REFERRAL		 0x8000
 /* 0x10000 is available */
 #define	MI4_NOPRINT		 0x20000
 #define	MI4_DIRECTIO		 0x40000
@@ -1103,11 +1118,7 @@
 #define	MI4_ASYNC_MGR_STOP	 0x40000000
 #define	MI4_TIMEDOUT		 0x80000000
 
-/*
- * Note that when we add referrals, then MI4_EPHEMERAL
- * will be MI4_MIRRORMOUNT | MI4_REFERRAL.
- */
-#define	MI4_EPHEMERAL		MI4_MIRRORMOUNT
+#define	MI4_EPHEMERAL		(MI4_MIRRORMOUNT | MI4_REFERRAL)
 
 #define	INTR4(vp)	(VTOMI4(vp)->mi_flags & MI4_INT)
 
@@ -1128,6 +1139,7 @@
 #define	MI4R_SRV_REBOOT		0x20	/* server has rebooted */
 #define	MI4R_LOST_STATE		0x40
 #define	MI4R_BAD_SEQID		0x80
+#define	MI4R_MOVED		0x100
 
 #define	MI4_HOLD(mi) {		\
 	mi_hold(mi);		\
@@ -1492,6 +1504,7 @@
 extern void	nfs4_lockcompletion(vnode_t *, int);
 extern bool_t	nfs4_map_lost_lock_conflict(vnode_t *);
 extern int	vtodv(vnode_t *, vnode_t **, cred_t *, bool_t);
+extern int	vtoname(vnode_t *, char *, ssize_t);
 extern void	nfs4open_confirm(vnode_t *, seqid4*, stateid4 *, cred_t *,
 		    bool_t, bool_t *, nfs4_open_owner_t *, bool_t,
 		    nfs4_error_t *, int *);
@@ -1502,6 +1515,9 @@
 extern void 	mi_hold(mntinfo4_t *);
 extern void	mi_rele(mntinfo4_t *);
 
+extern vnode_t	*find_referral_stubvp(vnode_t *, char *, cred_t *);
+extern int	 nfs4_setup_referral(vnode_t *, char *, vnode_t **, cred_t *);
+
 extern sec_data_t	*copy_sec_data(sec_data_t *);
 extern gss_clntdata_t	*copy_sec_data_gss(gss_clntdata_t *);
 
@@ -1969,7 +1985,8 @@
 extern int	nfs4_recov_marks_dead(nfsstat4);
 extern bool_t	nfs4_start_recovery(nfs4_error_t *, struct mntinfo4 *,
 			vnode_t *, vnode_t *, stateid4 *,
-			nfs4_lost_rqst_t *, nfs_opnum4, nfs4_bseqid_entry_t *);
+			nfs4_lost_rqst_t *, nfs_opnum4, nfs4_bseqid_entry_t *,
+			vnode_t *, char *);
 extern int	nfs4_start_op(struct mntinfo4 *, vnode_t *, vnode_t *,
 			nfs4_recov_state_t *);
 extern void	nfs4_end_op(struct mntinfo4 *, vnode_t *, vnode_t *,
@@ -1991,14 +2008,18 @@
  * of whether or not the code in _unlock is to be ran.
  */
 extern void	nfs4_ephemeral_umount_activate(mntinfo4_t *,
-    bool_t *, bool_t *, nfs4_ephemeral_tree_t **);
+    bool_t *, nfs4_ephemeral_tree_t **);
 extern int	nfs4_ephemeral_umount(mntinfo4_t *, int, cred_t *,
-    bool_t *, bool_t *, nfs4_ephemeral_tree_t **);
-extern void	nfs4_ephemeral_umount_unlock(bool_t *, bool_t *,
+    bool_t *, nfs4_ephemeral_tree_t **);
+extern void	nfs4_ephemeral_umount_unlock(bool_t *,
     nfs4_ephemeral_tree_t **);
 
 extern int	nfs4_record_ephemeral_mount(mntinfo4_t *mi, vnode_t *mvp);
 
+extern int	nfs4_callmapid(utf8string *, struct nfs_fsl_info *);
+extern int	nfs4_fetch_locations(mntinfo4_t *, struct nfs4_sharedfh *,
+    char *, cred_t *, nfs4_ga_res_t *, COMPOUND4res_clnt *, bool_t);
+
 extern int	wait_for_recall(vnode_t *, vnode_t *, nfs4_op_hint_t,
 			nfs4_recov_state_t *);
 extern void	nfs4_end_op_recall(vnode_t *, vnode_t *, nfs4_recov_state_t *);
@@ -2138,6 +2159,10 @@
 extern void fn_move(nfs4_fname_t *, nfs4_fname_t *, char *);
 extern nfs4_fname_t *fn_parent(nfs4_fname_t *);
 
+/* Referral Support */
+extern int nfs4_process_referral(mntinfo4_t *, nfs4_sharedfh_t *, char *,
+    cred_t *, nfs4_ga_res_t *, COMPOUND4res_clnt *, struct nfs_fsl_info *);
+
 #endif
 
 /*
--- a/usr/src/uts/common/nfs/nfs4_kprot.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/nfs4_kprot.h	Wed Dec 09 17:27:22 2009 -0600
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -227,6 +227,15 @@
 };
 typedef struct fs_locations4 fs_locations4;
 
+struct nfs_fsl_info {
+	uint_t netbuf_len;
+	uint_t netnm_len;
+	uint_t knconf_len;
+	char *netname;
+	struct netbuf *addr;
+	struct knetconfig *knconf;
+};
+
 /*
  * ACL support
  */
@@ -590,6 +599,7 @@
 	verifier4 verifier;
 	uint_t id_len;
 	char *id_val;
+	struct sockaddr *cl_addr;
 };
 typedef struct nfs_client_id4 nfs_client_id4;
 
@@ -1636,6 +1646,12 @@
 extern  bool_t xdr_CB_COMPOUND4args_srv(XDR *, CB_COMPOUND4args *);
 extern  bool_t xdr_CB_COMPOUND4res(XDR *, CB_COMPOUND4res *);
 
+/*
+ * xdr for referrrals upcall
+ */
+extern	bool_t xdr_knetconfig(XDR *, struct knetconfig *);
+extern	bool_t xdr_nfs_fsl_info(XDR *, struct nfs_fsl_info *);
+
 
 #ifdef __cplusplus
 }
--- a/usr/src/uts/common/nfs/nfsid_map.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/nfsid_map.h	Wed Dec 09 17:27:22 2009 -0600
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _NFSID_MAP_H
 #define	_NFSID_MAP_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifndef _KERNEL
 #include <stddef.h>
 #endif
@@ -59,6 +56,7 @@
 #define	NFSMAPID_UID_STR	2
 #define	NFSMAPID_STR_GID	3
 #define	NFSMAPID_GID_STR	4
+#define	NFSMAPID_SRV_NETINFO	5
 
 /*
  * We are passing in arguments in a variable length struct
@@ -145,6 +143,35 @@
 #define	MAPID_RES_LEN(str_length)	\
 	((offsetof(mapid_res_t, str[0]) + 1 + (str_length) + 7) & ~ 7)
 
+/*
+ * Support for referral name resolution by the NFS client
+ */
+typedef struct refd_door_args {
+	int		cmd;		/* NFS4_FS_LOCATIONS/NFS4_SRV_NETINFO */
+	int		xdr_len;	/* Length of xdr Buffer */
+	char		xdr_arg[1];	/* Buffer holding xdr encoded data */
+} refd_door_args_t;
+
+typedef struct refd_door_res {
+	int		res_status;
+	int		xdr_len;
+	char		xdr_res[1];
+} refd_door_res_t;
+
+#ifdef _SYSCALL32
+typedef struct refd_door_args32 {
+	int32_t		cmd;
+	int32_t		xdr_len;
+	char		xdr_arg[1];
+} refd_door_args32_t;
+
+typedef struct 	refd_door_res32 {
+	int32_t		res_status;
+	int32_t		xdr_len;
+	char		xdr_res[1];
+} refd_door_res32_t;
+#endif
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/nfs/rnode4.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/nfs/rnode4.h	Wed Dec 09 17:27:22 2009 -0600
@@ -41,7 +41,8 @@
 
 typedef enum nfs4_stub_type {
 	NFS4_STUB_NONE,
-	NFS4_STUB_MIRRORMOUNT
+	NFS4_STUB_MIRRORMOUNT,
+	NFS4_STUB_REFERRAL
 } nfs4_stub_type_t;
 
 typedef enum nfs4_access_type {
@@ -333,7 +334,7 @@
 					/* sv_fsid (servinfo4_t) to see why */
 					/* stub type was set		    */
 	nfs4_stub_type_t	r_stub_type;
-					/* e.g. mirror-mount */
+					/* e.g. mirror-mount or referral */
 	uint_t		r_inmap;	/* to serialize read/write and mmap */
 } rnode4_t;
 
@@ -371,6 +372,7 @@
 
 #define	RP_ISSTUB(rp)	(((rp)->r_stub_type != NFS4_STUB_NONE))
 #define	RP_ISSTUB_MIRRORMOUNT(rp) ((rp)->r_stub_type == NFS4_STUB_MIRRORMOUNT)
+#define	RP_ISSTUB_REFERRAL(rp)	((rp)->r_stub_type == NFS4_STUB_REFERRAL)
 
 /*
  * Open file instances.
@@ -415,6 +417,7 @@
 
 extern nfs4_opinst_t *r4mkopenlist(struct mntinfo4 *);
 extern void	r4releopenlist(nfs4_opinst_t *);
+extern int	r4find_by_fsid(mntinfo4_t *, fattr4_fsid *);
 
 /* Access cache calls */
 extern nfs4_access_type_t nfs4_access_check(rnode4_t *, uint32_t, cred_t *);
@@ -499,6 +502,7 @@
 extern void	rddir4_cache_rele(rnode4_t *, rddir4_cache *);
 
 extern void	r4_stub_mirrormount(rnode4_t *);
+extern void	r4_stub_referral(rnode4_t *);
 extern void	r4_stub_none(rnode4_t *);
 
 #ifdef DEBUG
--- a/usr/src/uts/common/sys/mkdev.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/sys/mkdev.h	Wed Dec 09 17:27:22 2009 -0600
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -30,8 +29,6 @@
 #ifndef _SYS_MKDEV_H
 #define	_SYS_MKDEV_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 
 #ifdef	__cplusplus
@@ -54,10 +51,11 @@
 #define	MAXMAJ32	0x3ffful	/* SVR4 max major value */
 #define	MAXMIN32	0x3fffful	/* SVR4 max minor value */
 
+#define	NBITSMAJOR64	32	/* # of major device bits in 64-bit Solaris */
+#define	NBITSMINOR64	32	/* # of minor device bits in 64-bit Solaris */
+
 #ifdef _LP64
 
-#define	NBITSMAJOR64	32	/* # of major device bits in 64-bit Solaris */
-#define	NBITSMINOR64	32	/* # of minor device bits in 64-bit Solaris */
 #define	MAXMAJ64	0xfffffffful	/* max major value */
 #define	MAXMIN64	0xfffffffful	/* max minor value */
 
--- a/usr/src/uts/common/sys/vnode.h	Wed Dec 09 14:43:40 2009 -0800
+++ b/usr/src/uts/common/sys/vnode.h	Wed Dec 09 17:27:22 2009 -0600
@@ -1239,6 +1239,7 @@
 vnode_t *makespecvp(dev_t dev, vtype_t type);
 vn_vfslocks_entry_t *vn_vfslocks_getlock(void *);
 void	vn_vfslocks_rele(vn_vfslocks_entry_t *);
+boolean_t vn_is_reparse(vnode_t *, cred_t *, caller_context_t *);
 
 void vn_copypath(struct vnode *src, struct vnode *dst);
 void vn_setpath_str(struct vnode *vp, const char *str, size_t len);