changeset 7040:eeae3015c35e

6668593 Support NT "named streams" 6720550 rename over existing file should attempt to remove
author gwr
date Fri, 04 Jul 2008 06:02:33 -0700
parents 72bd9824e54d
children b4c5fe87fad8
files usr/src/uts/common/Makefile.files usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c
diffstat 11 files changed, 1082 insertions(+), 339 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/Makefile.files	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/Makefile.files	Fri Jul 04 06:02:33 2008 -0700
@@ -1047,10 +1047,10 @@
 		smb_rq.o	smb_smb.o	smb_tran.o	smb_trantcp.o \
 		smb_usr.o	smb_subrs.o	subr_mchain.o	smb_pass.o
 
-SMBFS_OBJS +=	smbfs_vfsops.o	smbfs_vnops.o	smbfs_node.o \
-		smbfs_acl.o     smbfs_client.o  smbfs_io.o      \
-		smbfs_smb.o     smbfs_subr.o    smbfs_subr2.o   \
-		smbfs_rwlock.o
+SMBFS_OBJS +=	smbfs_vfsops.o	smbfs_vnops.o	smbfs_node.o	\
+		smbfs_acl.o	smbfs_client.o	smbfs_io.o	\
+		smbfs_smb.o	smbfs_subr.o	smbfs_subr2.o	\
+		smbfs_rwlock.o	smbfs_xattr.o
 
 
 #
--- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c	Fri Jul 04 06:02:33 2008 -0700
@@ -373,6 +373,7 @@
 	{NT_STATUS_NO_SUCH_FILE,		ENOENT},
 	{NT_STATUS_OBJECT_NAME_COLLISION,	EEXIST},
 	{NT_STATUS_OBJECT_NAME_NOT_FOUND,	ENOENT},
+	{NT_STATUS_OBJECT_NAME_INVALID,		EINVAL},
 	{NT_STATUS_OBJECT_PATH_INVALID,		ENOTDIR},
 	{NT_STATUS_PAGEFILE_QUOTA,		EDQUOT},
 	{NT_STATUS_PASSWORD_EXPIRED,		EACCES},
@@ -978,6 +979,7 @@
 		case ERRbadformat:
 		case ERRremcd:
 		case ERRrmuns:
+		case ERRunknownlevel:
 			return (EINVAL);
 		case ERRbadfile:
 		case ERRbadpath:
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c	Fri Jul 04 06:02:33 2008 -0700
@@ -224,7 +224,9 @@
     struct smbfattr *fap, vnode_t **vpp)
 {
 	struct smbnode *dnp = VTOSMB(dvp);
+	struct smbnode *np;
 	vnode_t *vp;
+	char sep;
 
 	*vpp = NULL;
 
@@ -235,10 +237,16 @@
 		return (EINVAL);
 	}
 
-	/* The real work is in this call... */
+	/*
+	 * See the comment near the top of smbfs_xattr.c about
+	 * the logic for what separators to use where.
+	 */
+	sep = (dnp->n_flag & N_XATTR) ? 0 : '\\';
+
+	/* Find or create the node. */
 	vp = smbfs_make_node(dvp->v_vfsp,
 	    dnp->n_rpath, dnp->n_rplen,
-	    name, nmlen, fap);
+	    name, nmlen, sep, fap);
 
 	/*
 	 * We always have a vp now, because
@@ -246,6 +254,16 @@
 	 * calls kmem_alloc with KM_SLEEP.
 	 */
 	ASSERT(vp);
+	np = VTOSMB(vp);
+
+	/*
+	 * Files in an XATTR dir are also XATTR.
+	 */
+	if (dnp->n_flag & N_XATTR) {
+		mutex_enter(&np->r_statelock);
+		np->n_flag |= N_XATTR;
+		mutex_exit(&np->r_statelock);
+	}
 
 #ifdef NOT_YET
 	/* update the attr_cache info if the file is clean */
@@ -287,7 +305,12 @@
 
 	mutex_enter(&np->r_statelock);
 
-	vtype = vp->v_type;
+	vtype = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG;
+	if (vp->v_type != vtype)
+		SMBVDEBUG("vtype change %d to %d\n",
+		    vp->v_type, vtype);
+	vp->v_type = vtype;
+
 	if (vtype == VREG) {
 		if (np->n_size != fap->fa_size) {
 			/*
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h	Fri Jul 04 06:02:33 2008 -0700
@@ -175,6 +175,7 @@
 #define	NATTRCHANGED	0x02000 /* use smbfs_attr_cacheremove at close */
 #define	NALLOC		0x04000 /* being created */
 #define	NWALLOC		0x08000 /* awaiting creation */
+#define	N_XATTR 	0x10000 /* extended attribute (dir or file) */
 
 typedef struct smbnode {
 	/* from Sun NFS struct rnode (XXX: cleanup needed) */
@@ -218,7 +219,8 @@
 	uint32_t	n_flag;
 	smbmntinfo_t	*n_mount;
 	ino64_t		n_ino;
-	/* Lock for the next 7 is r_lkserlock */
+	/* Lock for the next 8 is r_lkserlock */
+	enum vtype	n_ovtype;	/* vnode type opened */
 	int		n_dirrefs;
 	struct smbfs_fctx	*n_dirseq;	/* ff context */
 	long		n_dirofs;	/* last ff offset */
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c	Fri Jul 04 06:02:33 2008 -0700
@@ -200,6 +200,14 @@
 	 */
 	ASSERT(np->r_lkserlock.count != 0);
 
+	/*
+	 * Extended attribute directory or file.
+	 */
+	if (np->n_flag & N_XATTR) {
+		error = smbfs_xa_getfattr(np, fap, scrp);
+		return (error);
+	}
+
 	if (np->n_fidrefs)
 		error = smbfs_smb_qfileinfo(np, fap, scrp, 0);
 	else
@@ -210,10 +218,13 @@
 		error = smbfs_smb_query_info(np, NULL, 0, fap, scrp);
 	}
 
-#if 0	/* Moved this part to caller. */
-	if (!error && fap->fa_mtime.tv_sec == 0)
-		smbfs_attr_touchdir(dnp);
-#endif
+	/*
+	 * Note directory size is not provided by
+	 * windows servers (they leave it as zero)
+	 */
+	if ((fap->fa_attr & SMB_FA_DIR) &&
+	    (fap->fa_size < DEV_BSIZE))
+		fap->fa_size = DEV_BSIZE;
 
 	return (error);
 }
@@ -488,198 +499,8 @@
 
 /*
  * Support functions for _qstreaminfo
- * Todo: show NT file streams as
- * Solaris named attributes.
+ * See smbfs_xattr.c
  */
-#ifdef APPLE
-
-static char *
-sfm2xattr(char *sfm)
-{
-	if (!strncasecmp(sfm, SFM_RESOURCEFORK_NAME,
-	    sizeof (SFM_RESOURCEFORK_NAME)))
-		return (XATTR_RESOURCEFORK_NAME);
-	if (!strncasecmp(sfm, SFM_FINDERINFO_NAME,
-	    sizeof (SFM_FINDERINFO_NAME)))
-		return (XATTR_FINDERINFO_NAME);
-	return (NULL);
-}
-
-static int
-smbfs_smb_undollardata(struct smbnode *np, struct smbfs_fctx *ctx)
-{
-	char *cp;
-	int len = strlen(SMB_DATASTREAM);
-
-	if (!ctx->f_name)	/* sanity check */
-		goto bad;
-	if (ctx->f_nmlen < len + 1)	/* "::$DATA" at a minimum */
-		goto bad;
-	if (*ctx->f_name != ':')	/* leading colon - "always" */
-		goto bad;
-	cp =  &ctx->f_name[ctx->f_nmlen - len]; /* point to 2nd colon */
-	if (bcmp(cp, SMB_DATASTREAM, len))
-		goto bad;
-	if (ctx->f_nmlen == len + 1)	/* merely the data fork? */
-		return (0);		/* skip it */
-	/*
-	 * XXX here we should be calling KPI to validate the stream name
-	 */
-	if (ctx->f_nmlen >= 18 &&
-	    !(bcmp(ctx->f_name, ":com.apple.system.", 18) == 0))
-		return (0);	/* skip protected system attrs */
-	if (ctx->f_nmlen - len > XATTR_MAXNAMELEN + 1)
-		goto bad;	/* mustnt return more than 128 bytes */
-	/*
-	 * Un-count a colon and the $DATA, then the
-	 * 2nd colon is replaced by a terminating null.
-	 */
-	ctx->f_nmlen -= len;
-	*cp = '\0';
-	return (1);
-bad:
-	SMBSDEBUG("file \"%.*s\" has bad stream \"%.*s\"\n",
-	    np->n_nmlen, np->n_name, ctx->f_nmlen, ctx->f_name);
-	return (0); /* skip it */
-}
-
-PRIVSYM int
-smbfs_smb_qstreaminfo(struct smbnode *np, struct smb_cred *scrp,
-			uio_t uio, size_t *sizep)
-{
-	struct smb_share *ssp = np->n_mount->smi_share;
-	struct smb_vc *vcp = SSTOVC(ssp);
-	struct smb_t2rq *t2p;
-	int error;
-	struct mbchain *mbp;
-	struct mdchain *mdp;
-	uint32_t next, nlen, used;
-	struct smbfs_fctx ctx;
-
-	*sizep = 0;
-	ctx.f_ssp = ssp;
-	ctx.f_name = NULL;
-
-	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION,
-	    scrp, &t2p);
-	if (error)
-		return (error);
-	mbp = &t2p->t2_tparam;
-	mb_init(mbp);
-	/*
-	 * SMB_QFILEINFO_STREAM_INFORMATION is an option to consider
-	 * here.  Samba declined to support the older info level with
-	 * a comment claiming doing so caused a BSOD.
-	 */
-	mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
-	mb_put_uint32le(mbp, 0);
-	/* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */
-	error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
-	if (error)
-		goto out;
-	t2p->t2_maxpcount = 2;
-	t2p->t2_maxdcount = vcp->vc_txmax;
-	error = smb_t2_request(t2p);
-	if (error) {
-		if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
-			error = ENOTSUP;
-		goto out;
-	}
-	mdp = &t2p->t2_rdata;
-	/*
-	 * On a directory Windows is likely to return a zero data count.
-	 * Check for that now to avoid EBADRPC from md_get_uint32le
-	 */
-	if (mdp->md_cur == NULL)
-		goto out;
-	do {
-		if ((error = md_get_uint32le(mdp, &next)))
-			goto out;
-		if ((error = md_get_uint32le(mdp, &nlen))) /* name length */
-			goto out;
-		if ((error = md_get_uint64le(mdp, NULL))) /* stream size */
-			goto out;
-		if ((error = md_get_uint64le(mdp, NULL))) /* allocated size */
-			goto out;
-		/*
-		 * Sanity check to limit DoS or buffer overrun attempts.
-		 * The arbitrary 16384 is sufficient for all legit packets.
-		 */
-		if (nlen > 16384) {
-			SMBVDEBUG("huge name length in packet!\n");
-			error = EBADRPC;
-			goto out;
-		}
-		ctx.f_name = kmem_zalloc(nlen, KM_SLEEP);
-		ctx.f_namesz = nlen;
-		if ((error = md_get_mem(mdp, ctx.f_name, nlen, MB_MSYSTEM)))
-			goto out;
-		/*
-		 * skip pad bytes and/or tail of overlong name
-		 */
-		used = 4 + 4 + 8 + 8 + nlen;
-		if (next && next > used) {
-			if (next - used > 16384) {
-				SMBVDEBUG("huge offset in packet!\n");
-				error = EBADRPC;
-				goto out;
-			}
-			md_get_mem(mdp, NULL, next - used, MB_MSYSTEM);
-		}
-		/* ignore a trailing null, not that we expect them */
-		if (SMB_UNICODE_STRINGS(vcp)) {
-			if (nlen > 1 && !ctx.f_name[nlen - 1] &&
-			    !ctx.f_name[nlen - 2])
-				nlen -= 2;
-		} else {
-			if (nlen && !ctx.f_name[nlen - 1])
-				nlen -= 1;
-		}
-		ctx.f_nmlen = nlen;
-		smbfs_fname_tolocal(&ctx); /* converts from UCS2LE */
-		/*
-		 * We should now have a name in the form
-		 * : <foo> :$DATA
-		 * Where <foo> is UTF-8 w/o null termination
-		 * If it isn't in that form we want to LOG it and skip it.
-		 * Note we want to skip w/o logging the "data fork" entry,
-		 * which is simply ::$DATA
-		 * Otherwise we want to uiomove out <foo> with a null added.
-		 */
-		if (smbfs_smb_undollardata(np, &ctx)) {
-			char *s;
-
-			/* the "+ 1" skips over the leading colon */
-			s = sfm2xattr(ctx.f_name + 1);
-#ifndef DUAL_EAS	/* XXX */
-	/*
-	 * In Tiger Carbon still accesses dot-underscore files directly, so...
-	 * For Tiger we preserve the SFM/Thursby AFP_* stream names rather
-	 * than mapping them to com.apple.*.  This means our copy engines
-	 * will preserve SFM/Thursby resource-fork and finder-info.
-	 */
-			s = NULL;
-#endif
-			if (s)
-				ctx.f_nmlen = strlen(s) + 1;
-			else
-				s = ctx.f_name + 1;
-			if (uio)
-				uiomove(s, ctx.f_nmlen, uio);
-			else
-				*sizep += ctx.f_nmlen;
-		}
-		kmem_free(ctx.f_name, ctx.f_namesz);
-		ctx.f_name = NULL;
-	} while (next && !error);
-out:
-	if (ctx.f_name)
-		kmem_free(ctx.f_name, ctx.f_namesz);
-	smb_t2_done(t2p);
-	return (error);
-}
-
-#endif /* APPLE */
 
 int
 smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp,
@@ -2150,6 +1971,7 @@
 	struct mbchain *mbp;
 	int error;
 	uint16_t fa;
+	char sep;
 
 	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scrp);
 	if (error)
@@ -2162,20 +1984,28 @@
 	mb_put_uint16le(mbp, fa);
 	smb_rq_wend(rqp);
 	smb_rq_bstart(rqp);
+
+	/*
+	 * When we're not adding any component name, the
+	 * passed sep is ignored, so just pass sep=0.
+	 */
 	mb_put_uint8(mbp, SMB_DT_ASCII);
-	do {
-		error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\');
-		if (error)
-			break;
-		mb_put_uint8(mbp, SMB_DT_ASCII);
-		error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen,
-		    '\\');
-		if (error)
-			break;
-		smb_rq_bend(rqp);
-		error = smb_rq_simple(rqp);
-		/*LINTED*/
-	} while (0);
+	error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, 0);
+	if (error)
+		goto out;
+
+	/*
+	 * After XATTR directories, separator is ":"
+	 */
+	sep = (src->n_flag & N_XATTR) ? ':' : '\\';
+	mb_put_uint8(mbp, SMB_DT_ASCII);
+	error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen, sep);
+	if (error)
+		goto out;
+
+	smb_rq_bend(rqp);
+	error = smb_rq_simple(rqp);
+out:
 	smb_rq_done(rqp);
 	return (error);
 }
@@ -2376,9 +2206,10 @@
 /*ARGSUSED*/
 static int
 smbfs_smb_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp,
-    const char *wildcard, int wclen, uint16_t attr, struct smb_cred *scrp)
+    const char *wildcard, int wclen, uint16_t attr)
 {
-	/* #pragma unused(dnp, scrp) */
+
+	ctx->f_type = ft_LM1;
 	ctx->f_attrmask = attr;
 	if (wildcard) {
 		if (wclen == 1 && wildcard[0] == '*') {
@@ -2607,8 +2438,10 @@
 /*ARGSUSED*/
 static int
 smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
-    const char *wildcard, int wclen, uint16_t attr, struct smb_cred *scrp)
+    const char *wildcard, int wclen, uint16_t attr)
 {
+
+	ctx->f_type = ft_LM2;
 	ctx->f_namesz = SMB_MAXFNAMELEN;
 	if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
 		ctx->f_namesz *= 2;
@@ -2808,6 +2641,8 @@
 	ctx->f_eofs = next;
 	ctx->f_ecnt--;
 	ctx->f_left--;
+
+	smbfs_fname_tolocal(ctx);
 	return (0);
 }
 
@@ -2825,7 +2660,7 @@
 }
 
 int
-smbfs_smb_findopen(struct smbnode *dnp, const char *wildcard, int wclen,
+smbfs_smb_findopen(struct smbnode *dnp, const char *wild, int wlen,
 			int attr, struct smb_cred *scrp,
 			struct smbfs_fctx **ctxpp)
 {
@@ -2836,20 +2671,25 @@
 	if (ctx == NULL)
 		return (ENOMEM);
 	bzero(ctx, sizeof (*ctx));
-	if (dnp->n_mount->smi_share) {
-		ctx->f_ssp = dnp->n_mount->smi_share;
+
+	ctx->f_flags = SMBFS_RDD_FINDFIRST;
+	ctx->f_dnp = dnp;
+	ctx->f_scred = scrp;
+	ctx->f_ssp = dnp->n_mount->smi_share;
+
+	if (dnp->n_flag & N_XATTR) {
+		error = smbfs_xa_findopen(ctx, dnp, wild, wlen);
+		goto out;
 	}
-	ctx->f_dnp = dnp;
-	ctx->f_flags = SMBFS_RDD_FINDFIRST;
-	ctx->f_scred = scrp;
+
 	if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
 	    (dnp->n_mount->smi_args.flags & SMBFS_MOUNT_NO_LONG)) {
-		ctx->f_flags |= SMBFS_RDD_USESEARCH;
-		error = smbfs_smb_findopenLM1(ctx, dnp, wildcard, wclen,
-		    attr, scrp);
-	} else
-		error = smbfs_smb_findopenLM2(ctx, dnp, wildcard, wclen,
-		    attr, scrp);
+		error = smbfs_smb_findopenLM1(ctx, dnp, wild, wlen, attr);
+	} else {
+		error = smbfs_smb_findopenLM2(ctx, dnp, wild, wlen, attr);
+	}
+
+out:
 	if (error)
 		(void) smbfs_smb_findclose(ctx, scrp);
 	else
@@ -2883,30 +2723,40 @@
 
 	ctx->f_scred = scrp;
 	for (;;) {
-		if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
+		bzero(&ctx->f_attr, sizeof (ctx->f_attr));
+		switch (ctx->f_type) {
+		case ft_LM1:
 			error = smbfs_smb_findnextLM1(ctx, (uint16_t)limit);
-		} else
+			break;
+		case ft_LM2:
 			error = smbfs_smb_findnextLM2(ctx, (uint16_t)limit);
+			break;
+		case ft_XA:
+			error = smbfs_xa_findnext(ctx, (uint16_t)limit);
+			break;
+		default:
+			ASSERT(0);
+			error = EINVAL;
+			break;
+		}
 		if (error)
 			return (error);
-		if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
-			/*LINTED*/
-			uint16_t *up = (uint16_t *)ctx->f_name;
-
-			/* Do comparisons on UCS-2LE characters */
-			if ((ctx->f_nmlen == 2 && up[0] == htoles('.')) ||
-			    (ctx->f_nmlen == 4 && up[0] == htoles('.') &&
-			    up[1] == htoles('.')))
-				continue;
-		} else {
-			if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
-			    (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
-			    ctx->f_name[1] == '.'))
-				continue;
-		}
+		/*
+		 * Skip "." or ".." - easy now that ctx->f_name
+		 * has already been converted to utf-8 format.
+		 */
+		if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
+		    (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
+		    ctx->f_name[1] == '.'))
+			continue;
 		break;
 	}
-	smbfs_fname_tolocal(ctx);
+
+	/*
+	 * Moved the smbfs_fname_tolocal(ctx) call into
+	 * the ..._findnext functions above.
+	 */
+
 	ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name,
 	    ctx->f_nmlen);
 	return (0);
@@ -2917,11 +2767,19 @@
 smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp)
 {
 	int error;
+
 	ctx->f_scred = scrp;
-	if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
+	switch (ctx->f_type) {
+	case ft_LM1:
 		error = smbfs_smb_findcloseLM1(ctx);
-	} else
+		break;
+	case ft_LM2:
 		error = smbfs_smb_findcloseLM2(ctx);
+		break;
+	case ft_XA:
+		error = smbfs_xa_findclose(ctx);
+		break;
+	}
 	if (ctx->f_rname)
 		kmem_free(ctx->f_rname, ctx->f_rnamelen);
 	if (ctx->f_firstnm)
@@ -2967,8 +2825,6 @@
 	if (smbfs_rw_enter_sig(&dnp->r_lkserlock, RW_READER, intr))
 		return (EINTR);
 
-	bzero(fap, sizeof (*fap));
-
 	/*
 	 * This hides a server bug observable in Win98:
 	 * size changes may not show until a CLOSE or a FLUSH op
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c	Fri Jul 04 06:02:33 2008 -0700
@@ -289,12 +289,34 @@
 		if (error)
 			return (error);
 	}
+
 	error = smb_put_dmem(mbp, vcp,
 	    dnp->n_rpath, dnp->n_rplen,
 	    caseopt, lenp);
 	if (name) {
-		/* If not at root, put separator */
-		if (dnp->n_rplen > 1) {
+		/*
+		 * Special case at share root:
+		 * Don't put another slash.
+		 */
+		if (dnp->n_rplen <= 1 && sep == '\\')
+			sep = 0;
+		/*
+		 * More special cases, now for XATTR:
+		 * Our "faked up" XATTR directories use a
+		 * full path name ending with ":" so as to
+		 * avoid conflicts with any real paths.
+		 * (It is not a valid CIFS path name.)
+		 * Therefore, when we're composing a full
+		 * path name from an XATTR directory, we
+		 * need to _ommit_ the ":" separator and
+		 * instead copy the one from the "fake"
+		 * parent node's path name.
+		 */
+		if (dnp->n_flag & N_XATTR)
+			sep = 0;
+
+		if (sep) {
+			/* Put the separator */
 			if (unicode)
 				error = mb_put_uint16le(mbp, sep);
 			else
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h	Fri Jul 04 06:02:33 2008 -0700
@@ -78,6 +78,14 @@
 struct statvfs;
 struct timespec;
 
+/*
+ * Types of find_first, find_next context objects
+ */
+typedef enum {
+	ft_LM1 = 1,
+	ft_LM2,
+	ft_XA
+} smbfs_fctx_type_t;
 
 /*
  * Context to perform findfirst/findnext/findclose operations
@@ -85,7 +93,7 @@
 #define	SMBFS_RDD_FINDFIRST	0x01
 #define	SMBFS_RDD_EOF		0x02
 #define	SMBFS_RDD_FINDSINGLE	0x04
-#define	SMBFS_RDD_USESEARCH	0x08
+/* note	SMBFS_RDD_USESEARCH	0x08 replaced by smbfs_fctx_type */
 #define	SMBFS_RDD_NOCLOSE	0x10
 #define	SMBFS_RDD_GOTRNAME	0x1000
 
@@ -99,6 +107,7 @@
 	/*
 	 * Setable values
 	 */
+	smbfs_fctx_type_t	f_type;
 	int		f_flags;	/* SMBFS_RDD_ */
 	/*
 	 * Return values
@@ -248,7 +257,7 @@
 vnode_t *smbfs_make_node(vfs_t *vfsp,
     const char *dir, int dirlen,
     const char *name, int nmlen,
-    struct smbfattr *fap);
+    char sep, struct smbfattr *fap);
 void smb_addfree(smbnode_t *sp);
 void smb_addhash(smbnode_t *sp);
 void smb_rmhash(smbnode_t *);
@@ -265,6 +274,17 @@
 			int ioflag, int timo);
 int smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr);
 
+/* smbfs_xattr.c */
+int smbfs_get_xattrdir(vnode_t *dvp, vnode_t **vpp, cred_t *cr, int);
+int smbfs_xa_parent(vnode_t *vp, vnode_t **vpp);
+int smbfs_xa_exists(vnode_t *vp, cred_t *cr);
+int smbfs_xa_getfattr(struct smbnode *np, struct smbfattr *fap,
+	struct smb_cred *scrp);
+int smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
+	const char *name, int nmlen);
+int smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit);
+int smbfs_xa_findclose(struct smbfs_fctx *ctx);
+
 /* For Solaris, interruptible rwlock */
 int smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr);
 int smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw);
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c	Fri Jul 04 06:02:33 2008 -0700
@@ -174,6 +174,7 @@
 	int dirlen,
 	const char *name,
 	int nmlen,
+	char sep,
 	struct smbfattr *fap)
 {
 	char *rpath;
@@ -189,26 +190,33 @@
 
 	/*
 	 * Build the full path name in allocated memory
-	 * so we have it for lookup, etc.
+	 * so we have it for lookup, etc.  Note the
+	 * special case at the root (dir=="\\", dirlen==1)
+	 * where this does not add a slash separator.
+	 * To do that would make a double slash, which
+	 * has special meaning in CIFS.
 	 *
 	 * ToDo:  Would prefer to allocate a remote path
 	 * only when we will create a new node.
 	 */
+	if (dirlen <= 1 && sep == '\\')
+		sep = '\0';	/* no slash */
+
+	/* Compute the length of rpath and allocate. */
 	rplen = dirlen;
-	if (name) {
-		/* If not at root, we'll add a slash. */
-		if (dirlen > 1)
-			rplen++;
+	if (sep)
+		rplen++;
+	if (name)
 		rplen += nmlen;
-	}
+
 	rpath = kmem_alloc(rplen + 1, KM_SLEEP);
 
+	/* Fill in rpath */
 	bcopy(dir, rpath, dirlen);
-	if (name) {
-		if (dirlen > 1)
-			rpath[dirlen++] = '\\';
+	if (sep)
+		rpath[dirlen++] = sep;
+	if (name)
 		bcopy(name, &rpath[dirlen], nmlen);
-	}
 	rpath[rplen] = 0;
 
 	hash = smbfs_hash(rpath, rplen);
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c	Fri Jul 04 06:02:33 2008 -0700
@@ -80,12 +80,51 @@
 void		smbfsfini();
 static int	smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *);
 
+/*
+ * SMBFS Mount options table for MS_OPTIONSTR
+ * Note: These are not all the options.
+ * Some options come in via MS_DATA.
+ * Others are generic (see vfs.c)
+ */
+static char *intr_cancel[] = { MNTOPT_NOINTR, NULL };
+static char *nointr_cancel[] = { MNTOPT_INTR, NULL };
+#ifdef NOT_YET
+static char *force_dio_cancel[] = { MNTOPT_NOFORCEDIRECTIO, NULL };
+static char *noforce_dio_cancel[] = { MNTOPT_FORCEDIRECTIO, NULL };
+static char *largefiles_cancel[] = { MNTOPT_NOLARGEFILES, NULL };
+static char *nolargefiles_cancel[] = { MNTOPT_LARGEFILES, NULL };
+#endif
+static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL };
+static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL };
+
+static mntopt_t mntopts[] = {
+/*
+ *	option name		cancel option	default arg	flags
+ *		ufs arg flag
+ */
+	{ MNTOPT_INTR,		intr_cancel,	NULL,	MO_DEFAULT, 0 },
+	{ MNTOPT_NOINTR,	nointr_cancel,	NULL,	0,	0 },
+#ifdef NOT_YET
+	{ MNTOPT_FORCEDIRECTIO,	force_dio_cancel, NULL, 0,	0 },
+	{ MNTOPT_NOFORCEDIRECTIO, noforce_dio_cancel, NULL, 0, 0 },
+	{ MNTOPT_LARGEFILES,	largefiles_cancel, NULL, MO_DEFAULT, 0 },
+	{ MNTOPT_NOLARGEFILES,	nolargefiles_cancel, NULL, 0,	0 },
+#endif
+	{ MNTOPT_XATTR,		xattr_cancel,	NULL,	MO_DEFAULT, 0 },
+	{ MNTOPT_NOXATTR,	noxattr_cancel, NULL,	0,	0 }
+};
+
+static mntopts_t smbfs_mntopts = {
+	sizeof (mntopts) / sizeof (mntopt_t),
+	mntopts
+};
+
 static vfsdef_t vfw = {
 	VFSDEF_VERSION,
 	"smbfs",		/* type name string */
 	smbfsinit,		/* init routine */
-	VSW_NOTZONESAFE,	/* flags */
-	NULL			/* mount options table prototype */
+	VSW_HASPROTO|VSW_NOTZONESAFE,	/* flags */
+	&smbfs_mntopts			/* mount options table prototype */
 };
 
 static struct modlfs modlfs = {
@@ -297,6 +336,8 @@
 	 *
 	 * uap->datalen might be different from sizeof (args)
 	 * in a compatible situation.
+	 *
+	 * XXX - todo: handle mount options string
 	 */
 	STRUCT_INIT(args, get_udatamodel());
 	bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE));
@@ -419,6 +460,14 @@
 	smi->smi_share	= ssp;
 	ssp->ss_mount	= smi;
 	smi->smi_zone	= mntzone;
+	smi->smi_flags	= SMI_LLOCK;
+
+	/*
+	 * Handle mount options.  See also XATTR below.
+	 * XXX: forcedirectio, largefiles (later)
+	 */
+	if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL))
+		smi->smi_flags |= SMI_INT;
 
 	/*
 	 * XXX If not root, get uid/gid from the covered vnode.
@@ -428,19 +477,23 @@
 	smi->smi_args.uid 	= STRUCT_FGET(args, uid);
 	smi->smi_args.gid 	= STRUCT_FGET(args, gid);
 
+	/*
+	 * Get attributes of the remote file system,
+	 * i.e. ACL support, named streams, etc.
+	 */
 	error = smbfs_smb_qfsattr(ssp, &smi->smi_fsattr, &scred);
 	if (error) {
 		SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error);
 	}
 
-#ifdef NOT_YET
-	/* Once acls are implemented, remove the ifdefs */
-	else if (smbfs_aclsflunksniff(smi, &scred)) {
-		mutex_enter(&smi->smi_lock);
-		smi->smi_fsattr &= ~FILE_PERSISTENT_ACLS;
-		mutex_exit(&smi->smi_lock);
-	}
-#endif /* NOT_YET */
+	/*
+	 * We enable XATTR by default (via smbfs_mntopts)
+	 * but if the share does not support named streams,
+	 * force the NOXATTR option (also clears XATTR).
+	 * Caller will set or clear VFS_XATTR after this.
+	 */
+	if ((smi->smi_fsattr & FILE_NAMED_STREAMS) == 0)
+		vfs_setmntopt(vfsp, MNTOPT_NOXATTR, NULL, 0);
 
 	/*
 	 * Assign a unique device id to the mount
@@ -459,7 +512,6 @@
 	vfsp->vfs_bsize = MAXBSIZE;
 	vfsp->vfs_bcount = 0;
 
-	smi->smi_flags	= SMI_INT | SMI_LLOCK;
 	smi->smi_vfsp	= vfsp;
 	smbfs_zonelist_add(smi);
 
@@ -467,7 +519,7 @@
 	 * Create the root vnode, which we need in unmount
 	 * for the call to smb_check_table(), etc.
 	 */
-	rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, NULL);
+	rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, 0, NULL);
 	if (!rtvp) {
 		cmn_err(CE_WARN, "smbfs_mount: make_node failed\n");
 		return (ENOENT);
@@ -723,7 +775,7 @@
 	stvfs.f_frsize = stvfs.f_bsize;
 	stvfs.f_favail = stvfs.f_ffree;
 	stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0];
-	strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ);
+	(void) strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ);
 	stvfs.f_flag	= vf_to_stf(vfsp->vfs_flag);
 	stvfs.f_namemax	= (uint32_t)MAXNAMELEN - 1;
 
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c	Fri Jul 04 05:26:40 2008 -0700
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c	Fri Jul 04 06:02:33 2008 -0700
@@ -69,11 +69,14 @@
 /*
  * These characters are illegal in NTFS file names.
  * ref: http://support.microsoft.com/kb/147438
+ *
+ * Careful!  The check in the XATTR case skips the
+ * first character to allow colon in XATTR names.
  */
 static const char illegal_chars[] = {
+	':',	/* colon - keep this first! */
 	'\\',	/* back slash */
 	'/',	/* slash */
-	':',	/* colon */
 	'*',	/* asterisk */
 	'?',	/* question mark */
 	'"',	/* double quote */
@@ -91,6 +94,10 @@
 
 /* local static function defines */
 
+#ifdef USE_DNLC
+static int	smbfslookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp,
+			cred_t *cr);
+#endif
 static int	smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr,
 			int dnlc, caller_context_t *);
 static int	smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm,
@@ -266,6 +273,23 @@
 	smb_credinit(&scred, curproc, cr);
 
 	/*
+	 * Keep track of the vnode type at first open.
+	 * It may change later, and we need close to do
+	 * cleanup for the type we opened.  Also deny
+	 * open of new types until old type is closed.
+	 * XXX: Per-open instance nodes whould help.
+	 */
+	if (np->n_ovtype == VNON) {
+		ASSERT(np->n_dirrefs == 0);
+		ASSERT(np->n_fidrefs == 0);
+	} else if (np->n_ovtype != vp->v_type) {
+		SMBVDEBUG("open n_ovtype=%d v_type=%d\n",
+		    np->n_ovtype, vp->v_type);
+		error = EACCES;
+		goto out;
+	}
+
+	/*
 	 * Directory open is easy.
 	 */
 	if (vp->v_type == VDIR) {
@@ -311,10 +335,11 @@
 
 	/*
 	 * we always ask for READ_CONTROL so we can always get the
-	 * owner/group IDs to satisfy a stat.
+	 * owner/group IDs to satisfy a stat.  Ditto attributes.
 	 * XXX: verify that works with "drop boxes"
 	 */
-	rights |= STD_RIGHT_READ_CONTROL_ACCESS;
+	rights |= (STD_RIGHT_READ_CONTROL_ACCESS |
+	    SA_RIGHT_FILE_READ_ATTRIBUTES);
 	if ((flag & FREAD))
 		rights |= SA_RIGHT_FILE_READ_DATA;
 	if ((flag & FWRITE))
@@ -359,9 +384,15 @@
 	mutex_exit(&np->r_statelock);
 
 have_fid:
+	/*
+	 * Keep track of the vnode type at first open.
+	 * (see comments above)
+	 */
+	if (np->n_ovtype == VNON)
+		np->n_ovtype = vp->v_type;
+
 	/* Get attributes (maybe). */
 
-
 	/* Darwin (derived) code. */
 
 	va.va_mask = AT_MTIME;
@@ -459,7 +490,15 @@
 	smb_credinit(&scred, curproc, cr);
 
 	error = 0;
-	if (vp->v_type == VDIR) {
+
+	/*
+	 * Note that vp->v_type may change if a remote node
+	 * is deleted and recreated as a different type, and
+	 * our getattr may change v_type accordingly.
+	 * Now use n_ovtype to keep track of the v_type
+	 * we had during open (see comments above).
+	 */
+	if (np->n_ovtype == VDIR) {
 		struct smbfs_fctx *fctx;
 		ASSERT(np->n_dirrefs > 0);
 		if (--np->n_dirrefs)
@@ -484,6 +523,9 @@
 		    error, np->n_rpath);
 	}
 
+	/* Allow next open to use any v_type. */
+	np->n_ovtype = VNON;
+
 	if (np->n_flag & NATTRCHANGED)
 		smbfs_attr_cacheremove(np);
 
@@ -744,6 +786,7 @@
 		return (EINTR);
 	smb_credinit(&scred, curproc, cr);
 
+	bzero(&fattr, sizeof (fattr));
 	error = smbfs_smb_getfattr(np, &fattr, &scred);
 
 	smb_credrele(&scred);
@@ -818,6 +861,19 @@
 	ASSERT(curproc->p_zone == smi->smi_zone);
 
 	/*
+	 * There are no settable attributes on the XATTR dir,
+	 * so just silently ignore these.  On XATTR files,
+	 * you can set the size but nothing else.
+	 */
+	if (vp->v_flag & V_XATTRDIR)
+		return (0);
+	if (np->n_flag & N_XATTR) {
+		if (mask & AT_TIMES)
+			SMBVDEBUG("ignore set time on xattr\n");
+		mask &= AT_SIZE;
+	}
+
+	/*
 	 * If our caller is trying to set multiple attributes, they
 	 * can make no assumption about what order they are done in.
 	 * Here we try to do them in order of decreasing likelihood
@@ -1181,19 +1237,45 @@
 	int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
 	int *direntflags, pathname_t *realpnp)
 {
-	int		error;
-	smbnode_t	*dnp;
+	vfs_t		*vfs;
 	smbmntinfo_t	*smi;
-
-	smi = VTOSMI(dvp);
+	smbnode_t	*dnp;
+	int		error;
+
+	vfs = dvp->v_vfsp;
+	smi = VFTOSMI(vfs);
 
 	if (curproc->p_zone != smi->smi_zone)
 		return (EPERM);
 
-	if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+	if (smi->smi_flags & SMI_DEAD || vfs->vfs_flag & VFS_UNMOUNTED)
 		return (EIO);
 
 	dnp = VTOSMB(dvp);
+
+	/*
+	 * Are we looking up extended attributes?  If so, "dvp" is
+	 * the file or directory for which we want attributes, and
+	 * we need a lookup of the (faked up) attribute directory
+	 * before we lookup the rest of the path.
+	 */
+	if (flags & LOOKUP_XATTR) {
+		/*
+		 * Require the xattr mount option.
+		 */
+		if ((vfs->vfs_flag & VFS_XATTR) == 0)
+			return (EINVAL);
+
+		/*
+		 * We don't allow recursive attributes.
+		 */
+		if (dnp->n_flag & N_XATTR)
+			return (EINVAL);
+
+		error = smbfs_get_xattrdir(dvp, vpp, cr, flags);
+		return (error);
+	}
+
 	if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_READER, SMBINTR(dvp))) {
 		error = EINTR;
 		goto out;
@@ -1218,6 +1300,7 @@
 	smbnode_t	*dnp;
 	smbmntinfo_t	*smi;
 	/* struct smb_vc	*vcp; */
+	const char	*ill;
 	const char	*name = (const char *)nm;
 	int 		nmlen = strlen(nm);
 	int 		rplen;
@@ -1256,23 +1339,15 @@
 		return (0);
 	}
 
-	/* if the name is longer that what is supported, return an error */
-	if (nmlen > supplen)
-		return (ENAMETOOLONG);
+	/*
+	 * Can't do lookups in non-directories.
+	 */
+	if (dvp->v_type != VDIR)
+		return (ENOTDIR);
 
 	/*
-	 * Avoid surprises with characters that are
-	 * illegal in Windows file names.
-	 * Todo: CATIA mappings  XXX
+	 * Need search permission in the directory.
 	 */
-	if (strpbrk(nm, illegal_chars))
-		return (EINVAL);
-
-	/* if the dvp is not a directory, return an error */
-	if (dvp->v_type != VDIR)
-		return (ENOTDIR);
-
-	/* Need search permission in the directory. */
 	error = smbfs_access(dvp, VEXEC, 0, cr, ct);
 	if (error)
 		return (error);
@@ -1288,13 +1363,35 @@
 		return (0);
 	}
 
-#ifdef NOT_YET
-	if (dnlc) {
+	/*
+	 * Now some sanity checks on the name.
+	 * First check the length.
+	 */
+	if (nmlen > supplen)
+		return (ENAMETOOLONG);
+
 	/*
-	 * NOTE: search the dnlc here
+	 * Avoid surprises with characters that are
+	 * illegal in Windows file names.
+	 * Todo: CATIA mappings  XXX
 	 */
+	ill = illegal_chars;
+	if (dnp->n_flag & N_XATTR)
+		ill++; /* allow colon */
+	if (strpbrk(nm, ill))
+		return (EINVAL);
+
+#ifdef USE_DNLC
+	if (dnlc) {
+		/*
+		 * Lookup this name in the DNLC.  If there was a valid entry,
+		 * then return the results of the lookup.
+		 */
+		error = smbfslookup_dnlc(dvp, nm, vpp, cr);
+		if (error || *vpp != NULL)
+			return (error);
 	}
-#endif
+#endif	/* USE_DNLC */
 
 	/*
 	 * Handle lookup of ".." which is quite tricky,
@@ -1327,6 +1424,15 @@
 		}
 
 		/*
+		 * Special case for XATTR directory
+		 */
+		if (dvp->v_flag & V_XATTRDIR) {
+			error = smbfs_xa_parent(dvp, vpp);
+			/* Intentionally no dnlc_update */
+			return (error);
+		}
+
+		/*
 		 * Find the parent path length.
 		 */
 		rplen = dnp->n_rplen;
@@ -1344,11 +1450,12 @@
 		}
 		vp = smbfs_make_node(dvp->v_vfsp,
 		    dnp->n_rpath, rplen,
-		    NULL, 0, NULL);
-		if (vp == NULL) {
-			return (ENOENT);
-		}
+		    NULL, 0, 0, NULL);
+		ASSERT(vp);
 		vp->v_type = VDIR;
+#ifdef USE_DNLC
+		dnlc_update(dvp, nm, vp);
+#endif
 
 		/* Success! */
 		*vpp = vp;
@@ -1366,6 +1473,10 @@
 	/* Note: this can allocate a new "name" */
 	error = smbfs_smb_lookup(dnp, &name, &nmlen, &fa, &scred);
 	smb_credrele(&scred);
+#ifdef USE_DNLC
+	if (error == ENOENT)
+		dnlc_enter(dvp, nm, DNLC_NO_VNODE);
+#endif
 	if (error)
 		goto out;
 
@@ -1376,6 +1487,10 @@
 	if (error)
 		goto out;
 
+#ifdef USE_DNLC
+	dnlc_update(dvp, nm, vp);
+#endif
+
 	/* Success! */
 	*vpp = vp;
 
@@ -1387,6 +1502,84 @@
 	return (error);
 }
 
+#ifdef USE_DNLC
+#ifdef DEBUG
+static int smbfs_lookup_dnlc_hits = 0;
+static int smbfs_lookup_dnlc_misses = 0;
+static int smbfs_lookup_dnlc_neg_hits = 0;
+static int smbfs_lookup_dnlc_disappears = 0;
+static int smbfs_lookup_dnlc_lookups = 0;
+#endif
+
+/* ARGSUSED */
+static int
+smbfslookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr)
+{
+	int error;
+	vnode_t *vp;
+	struct vattr va;
+	smbnode_t *dnp;
+
+	dnp = VTOSMB(dvp);
+
+	ASSERT(*nm != '\0');
+	ASSERT(curproc->p_zone == VTOSMI(dvp)->smi_zone);
+
+	/*
+	 * Lookup this name in the DNLC.  If successful, then validate
+	 * the caches and then recheck the DNLC.  The DNLC is rechecked
+	 * just in case this entry got invalidated during the call
+	 * to smbfsgetattr().
+	 * An assumption is being made that it is safe to say that a
+	 * file exists which may not on the server.  Any operations to
+	 * the server will fail with ESTALE.
+	 */
+
+#ifdef DEBUG
+	smbfs_lookup_dnlc_lookups++;
+#endif
+	vp = dnlc_lookup(dvp, nm);
+	if (vp != NULL) {
+		if (vp == DNLC_NO_VNODE && !vn_is_readonly(dvp))
+			smbfs_attr_cacheremove(dnp);
+		VN_RELE(vp);
+		error = smbfsgetattr(dvp, &va, cr);
+		if (error)
+			return (error);
+		vp = dnlc_lookup(dvp, nm);
+		if (vp != NULL) {
+			/*
+			 * NFS checks VEXEC access here,
+			 * but we've already done that
+			 * in the caller.
+			 */
+			if (vp == DNLC_NO_VNODE) {
+				VN_RELE(vp);
+#ifdef DEBUG
+				smbfs_lookup_dnlc_neg_hits++;
+#endif
+				return (ENOENT);
+			}
+			*vpp = vp;
+#ifdef DEBUG
+			smbfs_lookup_dnlc_hits++;
+#endif
+			return (0);
+		}
+#ifdef DEBUG
+		smbfs_lookup_dnlc_disappears++;
+#endif
+	}
+#ifdef DEBUG
+	else
+		smbfs_lookup_dnlc_misses++;
+#endif
+	*vpp = NULL;
+
+	return (0);
+}
+#endif /* USE_DNLC */
+
 /*
  * XXX
  * vsecattr_t is new to build 77, and we need to eventually support
@@ -1417,6 +1610,7 @@
 	int		nmlen = strlen(nm);
 	uint32_t	disp;
 	uint16_t	fid;
+	int		xattr;
 
 	vfsp = dvp->v_vfsp;
 	smi = VFTOSMI(vfsp);
@@ -1528,10 +1722,8 @@
 	if (error)
 		goto out;
 
-#ifdef NOT_YET
-	/* remove the entry from the negative entry from the dnlc */
-	dnlc_remove(dvp, name);
-#endif
+	/* remove possible negative entry from the dnlc */
+	dnlc_remove(dvp, nm);
 
 	/*
 	 * Now the code derived from Darwin,
@@ -1553,7 +1745,8 @@
 		else
 			disp = NTCREATEX_DISP_OPEN_IF;
 	}
-	error = smbfs_smb_create(dnp, name, nmlen, &scred, &fid, disp, 0);
+	xattr = (dnp->n_flag & N_XATTR) ? 1 : 0;
+	error = smbfs_smb_create(dnp, name, nmlen, &scred, &fid, disp, xattr);
 	if (error)
 		goto out;
 
@@ -1617,10 +1810,11 @@
 	if (error)
 		goto out;
 
-#ifdef NOT_YET
-	dnlc_update(dvp, name, vp);
+#ifdef USE_DNLC
+	dnlc_update(dvp, nm, vp);
+#endif
+
 	/* XXX invalidate pages if we truncated? */
-#endif
 
 	/* Success! */
 	*vpp = vp;
@@ -1688,7 +1882,6 @@
 		goto out;
 	}
 
-#ifdef NOT_YET
 	/*
 	 * First just remove the entry from the name cache, as it
 	 * is most likely the only entry for this vp.
@@ -1703,7 +1896,6 @@
 	 */
 	if (vp->v_count > 1)
 		dnlc_purge_vp(vp);
-#endif /* NOT_YET */
 
 	/*
 	 * Now we have the real reference count on the vnode
@@ -1721,7 +1913,6 @@
 		 */
 		mutex_exit(&np->r_statelock);
 		error = EBUSY;
-		goto out;
 	} else {
 		mutex_exit(&np->r_statelock);
 
@@ -1778,6 +1969,7 @@
 	vnode_t		*nvp = NULL;
 	vnode_t		*ovp = NULL;
 	smbnode_t	*onp;
+	smbnode_t	*nnp;
 	smbnode_t	*odnp;
 	smbnode_t	*ndnp;
 	struct smb_cred	scred;
@@ -1897,7 +2089,6 @@
 			goto out;
 		}
 
-#ifdef NOT_YET
 		/*
 		 * Purge the name cache of all references to this vnode
 		 * so that we can check the reference count to infer
@@ -1916,26 +2107,69 @@
 		 */
 		if (nvp->v_count > 1)
 			dnlc_purge_vp(nvp);
-#endif
-
-		if (nvp->v_count > 1 && nvp->v_type != VDIR) {
+		/*
+		 * when renaming directories to be a subdirectory of a
+		 * different parent, the dnlc entry for ".." will no
+		 * longer be valid, so it must be removed
+		 */
+		if (ndvp != odvp) {
+			if (ovp->v_type == VDIR) {
+				dnlc_remove(ovp, "..");
+			}
+		}
+
+		/*
+		 * CIFS gives a SHARING_VIOLATION error when
+		 * trying to rename onto an exising object,
+		 * so try to remove the target first.
+		 * (Only for files, not directories.)
+		 */
+		if (nvp->v_type == VDIR) {
+			error = EEXIST;
+			goto out;
+		}
+
+		/*
+		 * Nodes that are "not active" here appear to have
+		 * v_count=2 (should be 1. XXX investigate later)
+		 * Code here is similar to smbfs_remove.
+		 */
+		nnp = VTOSMB(nvp);
+		mutex_enter(&nnp->r_statelock);
+		if (nvp->v_count > 2) {
 			/*
 			 * The target file exists, is not the same as
 			 * the source file, and is active.  Other FS
 			 * implementations unlink the target here.
 			 * For SMB, we don't assume we can remove an
 			 * open file.  Return an error instead.
-			 * Darwin returned an error here too.
 			 */
-			error = EEXIST;
+			mutex_exit(&nnp->r_statelock);
+			error = EBUSY;
 			goto out;
 		}
+		mutex_exit(&nnp->r_statelock);
+
+		/*
+		 * Target file is not active. Try to remove it.
+		 */
+		smb_credinit(&scred, curproc, cr);
+		error = smbfs_smb_delete(nnp, &scred, NULL, 0, 0);
+		smb_credrele(&scred);
+		if (error)
+			goto out;
+		/*
+		 * OK, removed the target file.  Continue as if
+		 * lookup target had failed (nvp == NULL).
+		 */
+		vn_vfsunlock(nvp);
+		nvp_locked = 0;
+		VN_RELE(nvp);
+		nvp = NULL;
 	} /* nvp */
 
-#ifdef NOT_YET
 	dnlc_remove(odvp, onm);
 	dnlc_remove(ndvp, nnm);
-#endif
 
 	onp = VTOSMB(ovp);
 	smb_credinit(&scred, curproc, cr);
@@ -1990,6 +2224,10 @@
 	    (nmlen == 2 && name[0] == '.' && name[1] == '.'))
 		return (EEXIST);
 
+	/* Only plain files are allowed in V_XATTRDIR. */
+	if (dvp->v_flag & V_XATTRDIR)
+		return (EINVAL);
+
 	if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
 		return (EINTR);
 	smb_credinit(&scred, curproc, cr);
@@ -2006,6 +2244,9 @@
 	if (error)
 		goto out;
 
+	/* remove possible negative entry from the dnlc */
+	dnlc_remove(dvp, nm);
+
 	error = smbfs_smb_mkdir(dnp, name, nmlen, &scred);
 	if (error)
 		goto out;
@@ -2020,8 +2261,8 @@
 	if (error)
 		goto out;
 
-#ifdef NOT_YET
-	dnlc_update(dvp, name, vp);
+#ifdef USE_DNLC
+	dnlc_update(dvp, nm, vp);
 #endif
 
 	if (name[0] == '.')
@@ -2108,6 +2349,27 @@
 		goto out;
 	}
 
+	/*
+	 * First just remove the entry from the name cache, as it
+	 * is most likely an entry for this vp.
+	 */
+	dnlc_remove(dvp, nm);
+
+	/*
+	 * If there vnode reference count is greater than one, then
+	 * there may be additional references in the DNLC which will
+	 * need to be purged.  First, trying removing the entry for
+	 * the parent directory and see if that removes the additional
+	 * reference(s).  If that doesn't do it, then use dnlc_purge_vp
+	 * to completely remove any references to the directory which
+	 * might still exist in the DNLC.
+	 */
+	if (vp->v_count > 1) {
+		dnlc_remove(vp, "..");
+		if (vp->v_count > 1)
+			dnlc_purge_vp(vp);
+	}
+
 	error = smbfs_smb_rmdir(np, &scred);
 	if (error)
 		goto out;
@@ -2116,10 +2378,6 @@
 	dnp->n_flag |= NMODIFIED;
 	mutex_exit(&np->r_statelock);
 	smbfs_attr_touchdir(dnp);
-#ifdef NOT_YET
-	dnlc_remove(dvp, nm);
-	dnlc_purge_vp(vp);
-#endif
 	smb_rmhash(np);
 
 out:
@@ -2466,6 +2724,7 @@
 	if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
 		return (EIO);
 
+	/* Caller (fcntl) has checked v_type */
 	ASSERT(vp->v_type == VREG);
 	if (cmd != F_FREESP)
 		return (EINVAL);
@@ -2506,10 +2765,12 @@
 smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
 	caller_context_t *ct)
 {
+	vfs_t *vfs;
 	smbmntinfo_t *smi;
 	struct smb_share *ssp;
 
-	smi = VTOSMI(vp);
+	vfs = vp->v_vfsp;
+	smi = VFTOSMI(vfs);
 
 	if (curproc->p_zone != smi->smi_zone)
 		return (EIO);
@@ -2541,10 +2802,16 @@
 		break;
 
 	case _PC_SYMLINK_MAX:	/* No symlinks until we do Unix extensions */
-	case _PC_XATTR_EXISTS:	/* No xattrs yet */
 		*valp = 0;
 		break;
 
+	case _PC_XATTR_EXISTS:
+		if (vfs->vfs_flag & VFS_XATTR) {
+			*valp = smbfs_xa_exists(vp, cr);
+			break;
+		}
+		return (EINVAL);
+
 	default:
 		return (fs_pathconf(vp, cmd, valp, cr, ct));
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c	Fri Jul 04 06:02:33 2008 -0700
@@ -0,0 +1,491 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Functions supporting Solaris Extended Attributes,
+ * used to provide access to CIFS "named streams".
+ */
+
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/filio.h>
+#include <sys/uio.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/dnlc.h>
+#include <sys/vfs_opreg.h>
+#include <sys/u8_textprep.h>
+
+#include <netsmb/smb_osdep.h>
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_rq.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+#include <fs/fs_subr.h>
+
+/*
+ * Solaris wants there to be a directory node to contain
+ * all the extended attributes.  The SMB protocol does not
+ * really support a directory here, and uses very different
+ * operations to list attributes, etc. so we "fake up" an
+ * smbnode here to represent the attributes directory.
+ *
+ * We need to give this (fake) directory a unique identity,
+ * and since we're using the full remote pathname as the
+ * unique identity of all nodes, the easiest thing to do
+ * here is append a colon (:) to the given pathname.
+ *
+ * There are several places where smbfs_fullpath and its
+ * callers must decide what separator to use when building
+ * a remote path name, and the rule is now as follows:
+ * 1: When no XATTR involved, use "\\" as the separator.
+ * 2: Traversal into the (fake) XATTR dir adds one ":"
+ * 3: Children of the XATTR dir add nothing (sep=0)
+ * The result should be _one_ colon before the attr name.
+ */
+
+/* ARGSUSED */
+int
+smbfs_get_xattrdir(vnode_t *pvp, vnode_t **vpp, cred_t *cr, int flags)
+{
+	vnode_t *xvp;
+	smbnode_t *pnp, *xnp;
+
+	pnp = VTOSMB(pvp);
+
+	xvp = smbfs_make_node(pvp->v_vfsp,
+	    pnp->n_rpath, pnp->n_rplen,
+	    NULL, 0, ':', NULL);
+	ASSERT(xvp);
+	/* Note: xvp has a VN_HOLD, which our caller expects. */
+	xnp = VTOSMB(xvp);
+
+	/* If it's a new node, initialize. */
+	if (xvp->v_type == VNON) {
+
+		mutex_enter(&xvp->v_lock);
+		xvp->v_type = VDIR;
+		xvp->v_flag |= V_XATTRDIR;
+		mutex_exit(&xvp->v_lock);
+
+		mutex_enter(&xnp->r_statelock);
+		xnp->n_flag |= N_XATTR;
+		mutex_exit(&xnp->r_statelock);
+	}
+
+	/* Success! */
+	*vpp = xvp;
+	return (0);
+}
+
+/*
+ * Find the parent of an XATTR directory or file,
+ * by trimming off the ":attrname" part of rpath.
+ * Called on XATTR files to get the XATTR dir, and
+ * called on the XATTR dir to get the real object
+ * under which the (faked up) XATTR dir lives.
+ */
+int
+smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
+{
+	smbnode_t *np = VTOSMB(vp);
+	vnode_t *pvp;
+	int rplen;
+
+	if ((np->n_flag & N_XATTR) == 0)
+		return (EINVAL);
+
+	if (vp->v_flag & V_XATTRDIR) {
+		/*
+		 * Want the parent of the XATTR directory.
+		 * That's easy: just remove trailing ":"
+		 */
+		rplen = np->n_rplen - 1;
+		if (rplen < 1) {
+			SMBVDEBUG("rplen < 1?");
+			return (ENOENT);
+		}
+		if (np->n_rpath[rplen] != ':') {
+			SMBVDEBUG("last is not colon");
+			return (ENOENT);
+		}
+	} else {
+		/*
+		 * Want the XATTR directory given
+		 * one of its XATTR files (children).
+		 * Find the ":" and trim after it.
+		 */
+		for (rplen = 1; rplen < np->n_rplen; rplen++)
+			if (np->n_rpath[rplen] == ':')
+				break;
+		/* Should have found ":stream_name" */
+		if (rplen >= np->n_rplen) {
+			SMBVDEBUG("colon not found");
+			return (ENOENT);
+		}
+		rplen++; /* keep the ":" */
+		if (rplen >= np->n_rplen) {
+			SMBVDEBUG("no stream name");
+			return (ENOENT);
+		}
+	}
+
+	pvp = smbfs_make_node(vp->v_vfsp,
+	    np->n_rpath, rplen,
+	    NULL, 0, 0, NULL);
+	ASSERT(pvp);
+
+	/* Note: pvp has a VN_HOLD from _make_node */
+	*vpp = pvp;
+	return (0);
+}
+
+/*
+ * This is called by smbfs_pathconf to find out
+ * if some file has any extended attributes.
+ * There's no short-cut way to find out, so we
+ * just list the attributes the usual way and
+ * check for an empty result.
+ *
+ * Returns 1: (exists) or 0: (none found)
+ */
+int
+smbfs_xa_exists(vnode_t *vp, cred_t *cr)
+{
+	smbnode_t *xnp;
+	vnode_t *xvp;
+	struct smb_cred scred;
+	struct smbfs_fctx ctx;
+	int error, rc = 0;
+
+	/* Get the xattr dir */
+	error = smbfs_get_xattrdir(vp, &xvp, cr, LOOKUP_XATTR);
+	if (error)
+		return (0);
+	/* NB: have VN_HOLD on xpv */
+	xnp = VTOSMB(xvp);
+
+	smb_credinit(&scred, curproc, cr);
+
+	bzero(&ctx, sizeof (ctx));
+	ctx.f_flags = SMBFS_RDD_FINDFIRST;
+	ctx.f_dnp = xnp;
+	ctx.f_scred = &scred;
+	ctx.f_ssp = xnp->n_mount->smi_share;
+
+	error = smbfs_xa_findopen(&ctx, xnp, "*", 1);
+	if (error)
+		goto out;
+
+	error = smbfs_xa_findnext(&ctx, 1);
+	if (error)
+		goto out;
+
+	/* Have at least one named stream. */
+	SMBVDEBUG("ctx.f_name: %s\n", ctx.f_name);
+	rc = 1;
+
+out:
+	/* NB: Always call findclose, error or not. */
+	(void) smbfs_xa_findclose(&ctx);
+	smb_credrele(&scred);
+	VN_RELE(xvp);
+	return (rc);
+}
+
+
+/*
+ * This is called to get attributes (size, etc.) of either
+ * the "faked up" XATTR directory or a named stream.
+ */
+int
+smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap,
+	struct smb_cred *scrp)
+{
+	vnode_t *xvp;	/* xattr */
+	vnode_t *pvp;	/* parent */
+	smbnode_t *pnp;	/* parent */
+	int error, nlen;
+	const char *name, *sname;
+
+	xvp = SMBTOV(xnp);
+
+	/*
+	 * Simulate smbfs_smb_getfattr() for a named stream.
+	 * OK to leave a,c,m times zero (expected w/ XATTR).
+	 * The XATTR directory is easy (all fake).
+	 */
+	if (xvp->v_flag & V_XATTRDIR) {
+		fap->fa_attr = SMB_FA_DIR;
+		fap->fa_size = DEV_BSIZE;
+		return (0);
+	}
+
+	/*
+	 * Do a lookup in the XATTR directory,
+	 * using the stream name (last part)
+	 * from the xattr node.
+	 */
+	error = smbfs_xa_parent(xvp, &pvp);
+	if (error)
+		return (error);
+	/* Note: pvp has a VN_HOLD */
+	pnp = VTOSMB(pvp);
+
+	/* Get stream name (ptr and length) */
+	ASSERT(xnp->n_rplen > pnp->n_rplen);
+	nlen = xnp->n_rplen - pnp->n_rplen;
+	name = xnp->n_rpath + pnp->n_rplen;
+	sname = name;
+
+	/* Note: this can allocate a new "name" */
+	error = smbfs_smb_lookup(pnp, &name, &nlen, fap, scrp);
+	if (error == 0 && name != sname)
+		smbfs_name_free(name, nlen);
+
+	VN_RELE(pvp);
+
+	return (error);
+}
+
+/*
+ * Fetch the entire attribute list here in findopen.
+ * Will parse the results in findnext.
+ *
+ * This is called on the XATTR directory, so we
+ * have to get the (real) parent object first.
+ */
+/* ARGSUSED */
+int
+smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
+	const char *wildcard, int wclen)
+{
+	vnode_t *pvp;	/* parent */
+	smbnode_t *pnp;
+	struct smb_t2rq *t2p;
+	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
+	struct mbchain *mbp;
+	int error;
+
+	ASSERT(dnp->n_flag & N_XATTR);
+
+	ctx->f_type = ft_XA;
+
+	error = smbfs_xa_parent(SMBTOV(dnp), &pvp);
+	if (error)
+		return (error);
+	ASSERT(pvp);
+	/* Note: pvp has a VN_HOLD */
+	pnp = VTOSMB(pvp);
+
+	if (ctx->f_t2) {
+		smb_t2_done(ctx->f_t2);
+		ctx->f_t2 = NULL;
+	}
+
+	error = smb_t2_alloc(SSTOCP(ctx->f_ssp),
+	    SMB_TRANS2_QUERY_PATH_INFORMATION,
+	    ctx->f_scred, &t2p);
+	if (error)
+		goto out;
+	ctx->f_t2 = t2p;
+
+	mbp = &t2p->t2_tparam;
+	mb_init(mbp);
+	mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
+	mb_put_uint32le(mbp, 0);
+	error = smbfs_fullpath(mbp, vcp, pnp, NULL, NULL, 0);
+	if (error)
+		goto out;
+	t2p->t2_maxpcount = 2;
+	t2p->t2_maxdcount = INT16_MAX;
+	error = smb_t2_request(t2p);
+	if (error) {
+		if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
+			error = ENOTSUP;
+	}
+	/*
+	 * No returned parameters to parse.
+	 * Returned data are in t2_rdata,
+	 * which we'll parse in _findnext.
+	 * However, save the wildcard.
+	 */
+	ctx->f_wildcard = wildcard;
+	ctx->f_wclen = wclen;
+
+out:
+	VN_RELE(pvp);
+	return (error);
+}
+
+/*
+ * Get the next name in an XATTR directory into f_name
+ */
+/* ARGSUSED */
+int
+smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit)
+{
+	struct mdchain *mdp;
+	struct smb_t2rq *t2p;
+	uint32_t size, next;
+	uint64_t llongint;
+	int error, skip, used, nmlen;
+
+	t2p = ctx->f_t2;
+	mdp = &t2p->t2_rdata;
+
+	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
+		ASSERT(ctx->f_wildcard);
+		SMBVDEBUG("wildcard: %s\n", ctx->f_wildcard);
+	}
+
+again:
+	if (ctx->f_flags & SMBFS_RDD_EOF)
+		return (ENOENT);
+
+	/* Parse FILE_STREAM_INFORMATION */
+	if ((error = md_get_uint32le(mdp, &next)) != 0)	/* offset to */
+		return (ENOENT);
+	if ((error = md_get_uint32le(mdp, &size)) != 0) /* name len */
+		return (ENOENT);
+	md_get_uint64le(mdp, &llongint); /* file size */
+	ctx->f_attr.fa_size = llongint;
+	md_get_uint64le(mdp, NULL);	/* alloc. size */
+	used = 4 + 4 + 8 + 8;	/* how much we consumed */
+
+	/*
+	 * Copy the string, but skip the first char (":")
+	 * Watch out for zero-length strings here.
+	 */
+	if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
+		if (size >= 2) {
+			size -= 2; used += 2;
+			md_get_uint16le(mdp, NULL);
+		}
+		nmlen = min(size, SMB_MAXFNAMELEN * 2);
+	} else {
+		if (size >= 1) {
+			size -= 1; used += 1;
+			md_get_uint8(mdp, NULL);
+		}
+		nmlen = min(size, SMB_MAXFNAMELEN);
+	}
+
+	if (ctx->f_name)
+		kmem_free(ctx->f_name, ctx->f_namesz);
+	ctx->f_nmlen = nmlen;
+	/* Add one to prevent allocating size zero. */
+	ctx->f_namesz = nmlen + 1;
+	ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
+	error = md_get_mem(mdp, ctx->f_name, nmlen, MB_MSYSTEM);
+	if (error)
+		return (error);
+	used += nmlen;
+
+	/*
+	 * Convert UCS-2 to UTF-8
+	 */
+	smbfs_fname_tolocal(ctx);
+	if (nmlen)
+		SMBVDEBUG("name: %s\n", ctx->f_name);
+	else
+		SMBVDEBUG("null name!\n");
+
+	/*
+	 * Skip padding until next offset
+	 */
+	if (next > used) {
+		skip = next - used;
+		md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
+	}
+	if (next == 0)
+		ctx->f_flags |= SMBFS_RDD_EOF;
+
+	/*
+	 * Chop off the trailing ":$DATA"
+	 * The 6 here is strlen(":$DATA")
+	 */
+	if (ctx->f_nmlen >= 6) {
+		char *p = ctx->f_name + ctx->f_nmlen - 6;
+		if (strncmp(p, ":$DATA", 6) == 0) {
+			*p = '\0'; /* Chop! */
+			ctx->f_nmlen -= 6;
+		}
+	}
+
+	/*
+	 * The Chop above will typically leave
+	 * an empty name in the first slot,
+	 * which we will skip here.
+	 */
+	if (ctx->f_nmlen == 0)
+		goto again;
+
+	/*
+	 * If this is a lookup of a specific name,
+	 * skip past any non-matching names.
+	 */
+	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
+		if (ctx->f_wclen != ctx->f_nmlen)
+			goto again;
+		if (u8_strcmp(ctx->f_wildcard, ctx->f_name,
+		    ctx->f_nmlen, U8_STRCMP_CI_LOWER,
+		    U8_UNICODE_LATEST, &error) || error)
+			goto again;
+	}
+
+	return (0);
+}
+
+/*
+ * Find first/next/close for XATTR directories.
+ * NB: also used by smbfs_smb_lookup
+ */
+
+int
+smbfs_xa_findclose(struct smbfs_fctx *ctx)
+{
+
+	if (ctx->f_name)
+		kmem_free(ctx->f_name, ctx->f_namesz);
+	if (ctx->f_t2)
+		smb_t2_done(ctx->f_t2);
+
+	return (0);
+}