--- old/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c	Tue Aug 31 13:02:03 2010
+++ new/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c	Tue Aug 31 13:02:02 2010
@@ -68,9 +68,9 @@
 #include <vm/seg_map.h>
 #include <vm/seg_vn.h>
 
-static int smbfs_getattr_cache(vnode_t *, struct smbfattr *);
-static int smbfattr_to_vattr(vnode_t *, struct smbfattr *,
-	struct vattr *);
+static int smbfs_getattr_cache(vnode_t *, smbfattr_t *);
+static void smbfattr_to_vattr(vnode_t *, smbfattr_t *, vattr_t *);
+static void smbfattr_to_xvattr(vnode_t *, smbfattr_t *, vattr_t *);
 
 /*
  * The following code provide zone support in order to perform an action
@@ -441,16 +441,17 @@
 		error = smbfs_getattr_otw(vp, &fa, cr);
 	if (error)
 		return (error);
+	vap->va_mask |= mask;
 
 	/*
 	 * Re. client's view of the file size, see:
 	 * smbfs_attrcache_fa, smbfs_getattr_otw
 	 */
-
-	error = smbfattr_to_vattr(vp, &fa, vap);
-	vap->va_mask = mask;
-
-	return (error);
+	smbfattr_to_vattr(vp, &fa, vap);
+	if (vap->va_mask & AT_XVATTR)
+		smbfattr_to_xvattr(vp, &fa, vap);
+		
+	return (0);
 }
 
 
@@ -459,13 +460,11 @@
  * Returns 0 for success, error if failed (overflow, etc).
  * From NFS: nattr_to_vattr()
  */
-int
+void
 smbfattr_to_vattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap)
 {
 	struct smbnode *np = VTOSMB(vp);
 
-	/* Set va_mask in caller */
-
 	/*
 	 * Take type, mode, uid, gid from the smbfs node,
 	 * which has have been updated by _getattr_otw.
@@ -506,11 +505,52 @@
 	vap->va_blksize = MAXBSIZE;
 	vap->va_nblocks = (fsblkcnt64_t)btod(np->r_attr.fa_allocsz);
 	vap->va_seq = 0;
-
-	return (0);
 }
 
+/*
+ * smbfattr_to_xvattr: like smbfattr_to_vattr but for
+ * Extensible system attributes (PSARC 2007/315)
+ */
+static void
+smbfattr_to_xvattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap)
+{
+	struct smbnode *np = VTOSMB(vp);
+	xvattr_t *xvap = (xvattr_t *)vap;	/* *vap may be xvattr_t */
+	xoptattr_t *xoap = NULL;
 
+	if ((xoap = xva_getxoptattr(xvap)) == NULL)
+		return;
+
+	if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
+		xoap->xoa_createtime = fa->fa_createtime;
+		XVA_SET_RTN(xvap, XAT_CREATETIME);
+	}
+
+	if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
+		xoap->xoa_archive =
+		    ((fa->fa_attr & SMB_FA_ARCHIVE) != 0);
+		XVA_SET_RTN(xvap, XAT_ARCHIVE);
+	}
+
+	if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
+		xoap->xoa_system =
+		    ((fa->fa_attr & SMB_FA_SYSTEM) != 0);
+		XVA_SET_RTN(xvap, XAT_SYSTEM);
+	}
+
+	if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
+		xoap->xoa_readonly =
+		    ((fa->fa_attr & SMB_FA_RDONLY) != 0);
+		XVA_SET_RTN(xvap, XAT_READONLY);
+	}
+
+	if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
+		xoap->xoa_hidden =
+		    ((fa->fa_attr & SMB_FA_HIDDEN) != 0);
+		XVA_SET_RTN(xvap, XAT_HIDDEN);
+	}
+}
+
 /*
  * SMB Client initialization and cleanup.
  * Much of it is per-zone now.
--- old/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c	Tue Aug 31 13:02:04 2010
+++ new/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c	Tue Aug 31 13:02:03 2010
@@ -598,6 +598,10 @@
 	smi->smi_vfsp	= vfsp;
 	smbfs_zonelist_add(smi);	/* undo in smbfs_freevfs */
 
+	/* PSARC 2007/227 VFS Feature Registration */
+	vfs_set_feature(vfsp, VFSFT_XVATTR);
+	vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+
 	/*
 	 * Create the root vnode, which we need in unmount
 	 * for the call to smbfs_check_table(), etc.
--- old/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c	Tue Aug 31 13:02:05 2010
+++ new/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c	Tue Aug 31 13:02:04 2010
@@ -114,6 +114,7 @@
 static int	smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
 			caller_context_t *);
 static void	smbfs_rele_fid(smbnode_t *, struct smb_cred *);
+static uint32_t xvattr_to_dosattr(smbnode_t *, struct vattr *);
 
 /*
  * These are the vnode ops routines which implement the vnode interface to
@@ -838,10 +839,6 @@
 /*
  * Return either cached or remote attributes. If get remote attr
  * use them to check and invalidate caches, then cache the new attributes.
- *
- * XXX
- * This op should eventually support PSARC 2007/315, Extensible Attribute
- * Interfaces, for richer metadata.
  */
 /* ARGSUSED */
 static int
@@ -889,11 +886,6 @@
 
 /* smbfsgetattr() in smbfs_client.c */
 
-/*
- * XXX
- * This op should eventually support PSARC 2007/315, Extensible Attribute
- * Interfaces, for richer metadata.
- */
 /*ARGSUSED4*/
 static int
 smbfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
@@ -979,6 +971,7 @@
 	unsigned short	fid;
 	int have_fid = 0;
 	uint32_t rights = 0;
+	uint32_t dosattr = 0;
 
 	ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone_ref.zref_zone);
 
@@ -1009,10 +1002,18 @@
 	smb_credinit(&scred, cr);
 
 	/*
+	 * If the caller has provided extensible attributes,
+	 * map those into DOS attributes supported by SMB.
+	 * Note: zero means "no change".
+	 */
+	if (mask & AT_XVATTR)
+		dosattr = xvattr_to_dosattr(np, vap);
+
+	/*
 	 * Will we need an open handle for this setattr?
 	 * If so, what rights will we need?
 	 */
-	if (mask & (AT_ATIME | AT_MTIME)) {
+	if (dosattr || (mask & (AT_ATIME | AT_MTIME))) {
 		rights |=
 		    SA_RIGHT_FILE_WRITE_ATTRIBUTES;
 	}
@@ -1028,7 +1029,7 @@
 	 * Some servers like NT4 won't set times by path.
 	 * Also, we're usually setting everything anyway.
 	 */
-	if (mask & (AT_SIZE | AT_ATIME | AT_MTIME)) {
+	if (rights != 0) {
 		error = smbfs_smb_tmpopen(np, rights, &scred, &fid);
 		if (error) {
 			SMBVDEBUG("error %d opening %s\n",
@@ -1085,7 +1086,7 @@
 	mtime = ((mask & AT_MTIME) ? &vap->va_mtime : 0);
 	atime = ((mask & AT_ATIME) ? &vap->va_atime : 0);
 
-	if (mtime || atime) {
+	if (dosattr || mtime || atime) {
 		/*
 		 * Always use the handle-based set attr call now.
 		 * Not trying to set DOS attributes here so pass zero.
@@ -1092,7 +1093,7 @@
 		 */
 		ASSERT(have_fid);
 		error = smbfs_smb_setfattr(np, fid,
-		    0, mtime, atime, &scred);
+		    dosattr, mtime, atime, &scred);
 		if (error) {
 			SMBVDEBUG("set times error %d file %s\n",
 			    error, np->n_rpath);
@@ -1124,6 +1125,64 @@
 }
 
 /*
+ * Helper function for extensible system attributes (PSARC 2007/315)
+ * Compute the DOS attribute word to pass to _setfattr (see above).
+ * This returns zero IFF no change is being made to attributes.
+ * Otherwise return the new attributes or SMB_EFA_NORMAL.
+ */
+static uint32_t
+xvattr_to_dosattr(smbnode_t *np, struct vattr *vap)
+{
+	xvattr_t *xvap = (xvattr_t *)vap;
+	xoptattr_t *xoap = NULL;
+	uint32_t attr = np->r_attr.fa_attr;
+	boolean_t anyset = B_FALSE;
+
+	if ((xoap = xva_getxoptattr(xvap)) == NULL)
+		return (0);
+
+	if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
+		if (xoap->xoa_archive)
+			attr |= SMB_FA_ARCHIVE;
+		else
+			attr &= ~SMB_FA_ARCHIVE;
+		XVA_SET_RTN(xvap, XAT_ARCHIVE);
+		anyset = B_TRUE;
+	}
+	if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
+		if (xoap->xoa_system)
+			attr |= SMB_FA_SYSTEM;
+		else
+			attr &= ~SMB_FA_SYSTEM;
+		XVA_SET_RTN(xvap, XAT_SYSTEM);
+		anyset = B_TRUE;
+	}
+	if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
+		if (xoap->xoa_readonly)
+			attr |= SMB_FA_RDONLY;
+		else
+			attr &= ~SMB_FA_RDONLY;
+		XVA_SET_RTN(xvap, XAT_READONLY);
+		anyset = B_TRUE;
+	}
+	if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
+		if (xoap->xoa_hidden)
+			attr |= SMB_FA_HIDDEN;
+		else
+			attr &= ~SMB_FA_HIDDEN;
+		XVA_SET_RTN(xvap, XAT_HIDDEN);
+		anyset = B_TRUE;
+	}
+
+	if (anyset == B_FALSE)
+		return (0);	/* no change */
+	if (attr == 0)
+		attr = SMB_EFA_NORMAL;
+
+	return (attr);
+}
+
+/*
  * smbfs_access_rwx()
  * Common function for smbfs_access, etc.
  *
@@ -2992,6 +3051,11 @@
 		}
 		return (EINVAL);
 
+	case _PC_SATTR_ENABLED:
+	case _PC_SATTR_EXISTS:
+		*valp = 1;
+		break;
+
 	case _PC_TIMESTAMP_RESOLUTION:
 		/*
 		 * Windows times are tenths of microseconds
--- old/usr/src/uts/common/fs/xattr.c	Tue Aug 31 13:02:06 2010
+++ new/usr/src/uts/common/fs/xattr.c	Tue Aug 31 13:02:05 2010
@@ -54,15 +54,9 @@
 
 typedef struct {
 	gfs_dir_t	xattr_gfs_private;
-	vnode_t		*xattr_realvp;  /* Only used for VOP_REALVP */
+	vnode_t		*xattr_realvp;
 } xattr_dir_t;
 
-/*
- * xattr_realvp is only used for VOP_REALVP, this is so we don't
- * keep an unnecessary hold on the *real* xattr dir unless we have
- * no other choice.
- */
-
 /* ARGSUSED */
 static int
 xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
@@ -879,29 +873,24 @@
 xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
     cred_t *cr, caller_context_t *ct)
 {
-	vnode_t *pvp;
+	xattr_dir_t *xattr_dir;
 	int error;
-	struct pathname pn;
-	char *startnm = "";
 
 	*realdvp = NULL;
 
-	pvp = gfs_file_parent(dvp);
+	if (dvp->v_type != VDIR)
+		return (EINVAL);
 
-	error = pn_get(startnm, UIO_SYSSPACE, &pn);
-	if (error) {
-		VN_RELE(pvp);
-		return (error);
-	}
+	mutex_enter(&dvp->v_lock);
+	xattr_dir = dvp->v_data;
+	*realdvp = xattr_dir->xattr_realvp;
+	mutex_exit(&dvp->v_lock);
 
-	/*
-	 * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
-	 * infinite loop with fop_lookup calling back to xattr_dir_lookup.
-	 */
-	lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
-	error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
-	    rootvp, cr, ct, NULL, NULL);
-	pn_free(&pn);
+	if (*realdvp != NULL) {
+		VN_HOLD(*realdvp);
+		error = 0;
+	} else
+		error = ENOENT;
 
 	return (error);
 }
@@ -910,18 +899,46 @@
 static int
 xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
 {
+	vnode_t *realvp;
+	int error;
+
 	if (flags & FWRITE) {
 		return (EACCES);
 	}
 
+	/*
+	 * The underlying FS may need this VOP call.
+	 */
+	error = xattr_dir_realdir(*vpp, &realvp, LOOKUP_XATTR, cr, ct);
+	if (error == 0) {
+		error = VOP_OPEN(&realvp, flags, cr, ct);
+		VN_RELE(realvp);
+		if (error)
+			return (error);
+	} /* else ignore this error */
+
 	return (0);
 }
 
 /* ARGSUSED */
 static int
-xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
+xattr_dir_close(vnode_t *vp, int flags, int count, offset_t off, cred_t *cr,
     caller_context_t *ct)
 {
+	vnode_t *realvp;
+	int error;
+
+	/*
+	 * The underlying FS may need this VOP call.
+	 */
+	error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
+	if (error == 0) {
+		error = VOP_CLOSE(realvp, flags, count, off, cr, ct);
+		VN_RELE(realvp);
+		if (error)
+			return (error);
+	} /* else ignore this error */
+
 	return (0);
 }
 
@@ -1356,38 +1373,11 @@
 static int
 xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
 {
-	xattr_dir_t *xattr_dir;
+	int error;
 
-	mutex_enter(&vp->v_lock);
-	xattr_dir = vp->v_data;
-	if (xattr_dir->xattr_realvp) {
-		*realvp = xattr_dir->xattr_realvp;
-		mutex_exit(&vp->v_lock);
-		return (0);
-	} else {
-		vnode_t *xdvp;
-		int error;
+	error = xattr_dir_realdir(vp, realvp, LOOKUP_XATTR, kcred, NULL);
+	return (error);
 
-		mutex_exit(&vp->v_lock);
-		if ((error = xattr_dir_realdir(vp, &xdvp,
-		    LOOKUP_XATTR, kcred, NULL)) == 0) {
-			/*
-			 * verify we aren't racing with another thread
-			 * to find the xattr_realvp
-			 */
-			mutex_enter(&vp->v_lock);
-			if (xattr_dir->xattr_realvp == NULL) {
-				xattr_dir->xattr_realvp = xdvp;
-				*realvp = xdvp;
-				mutex_exit(&vp->v_lock);
-			} else {
-				*realvp = xattr_dir->xattr_realvp;
-				mutex_exit(&vp->v_lock);
-				VN_RELE(xdvp);
-			}
-		}
-		return (error);
-	}
 }
 
 static const fs_operation_def_t xattr_dir_tops[] = {
@@ -1470,10 +1460,16 @@
 	VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
 }
 
+/* See vnode.c: fop_lookup() */
 int
 xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
 {
 	int error = 0;
+	vnode_t *gfs_vp = NULL;
+	vnode_t *real_vp = NULL;
+	xattr_dir_t *xattr_dir;
+	struct pathname pn;
+	char *nm = "";
 
 	*vpp = NULL;
 
@@ -1492,8 +1488,8 @@
 	}
 
 	if (dvp->v_xattrdir != NULL) {
-		*vpp = dvp->v_xattrdir;
-		VN_HOLD(*vpp);
+		gfs_vp = dvp->v_xattrdir;
+		VN_HOLD(gfs_vp);
 	} else {
 		ulong_t val;
 		int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
@@ -1519,9 +1515,6 @@
 			return (EINVAL);
 
 		if (!sysattrs_allowed) {
-			struct pathname pn;
-			char *nm = "";
-
 			error = pn_get(nm, UIO_SYSSPACE, &pn);
 			if (error)
 				return (error);
@@ -1536,7 +1529,7 @@
 		 * Note that we act as if we were given CREATE_XATTR_DIR,
 		 * but only for creation of the GFS directory.
 		 */
-		*vpp = gfs_dir_create(
+		gfs_vp = gfs_dir_create(
 		    sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
 		    xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
 		mutex_enter(&dvp->v_lock);
@@ -1547,10 +1540,10 @@
 			 * just call VN_RELE(*vpp), because the vnode
 			 * is only partially initialized.
 			 */
-			gfs_dir_t *dp = (*vpp)->v_data;
+			gfs_dir_t *dp = gfs_vp->v_data;
 
-			ASSERT((*vpp)->v_count == 1);
-			vn_free(*vpp);
+			ASSERT(gfs_vp->v_count == 1);
+			vn_free(gfs_vp);
 
 			mutex_destroy(&dp->gfsd_lock);
 			kmem_free(dp->gfsd_static,
@@ -1560,20 +1553,49 @@
 			/*
 			 * There is an implied VN_HOLD(dvp) here.  We should
 			 * be doing a VN_RELE(dvp) to clean up the reference
-			 * from *vpp, and then a VN_HOLD(dvp) for the new
+			 * from gfs_vp, and then a VN_HOLD(dvp) for the new
 			 * reference.  Instead, we just leave the count alone.
 			 */
 
-			*vpp = dvp->v_xattrdir;
-			VN_HOLD(*vpp);
+			gfs_vp = dvp->v_xattrdir;
+			VN_HOLD(gfs_vp);
 		} else {
-			(*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
-			dvp->v_xattrdir = *vpp;
+			gfs_vp->v_flag |= (V_XATTRDIR|V_SYSATTR);
+			dvp->v_xattrdir = gfs_vp;
 		}
 	}
 	mutex_exit(&dvp->v_lock);
 
-	return (error);
+	/*
+	 * In order to make this module relatively transparent
+	 * to the underlying filesystem, we need to lookup the
+	 * xattr dir in the lower filesystem and (if found)
+	 * keep a hold on it for as long as there is a hold
+	 * on the gfs_vp we're about to return.  This hold is
+	 * released in xattr_dir_inactive.
+	 */
+	xattr_dir = gfs_vp->v_data;
+	if ((dvp->v_vfsp->vfs_flag & VFS_XATTR) &&
+	    (xattr_dir->xattr_realvp == NULL)) {
+		error = pn_get(nm, UIO_SYSSPACE, &pn);
+		if (error == 0) {
+			error = VOP_LOOKUP(dvp, nm, &real_vp, &pn,
+			    flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
+			    NULL, NULL);
+			pn_free(&pn);
+		}
+		if (error == 0) {
+			mutex_enter(&gfs_vp->v_lock);
+			if (xattr_dir->xattr_realvp == NULL)
+				xattr_dir->xattr_realvp = real_vp;
+			else
+				VN_RELE(real_vp);
+			mutex_exit(&gfs_vp->v_lock);
+		}
+	}
+
+	*vpp = gfs_vp;
+	return (0);
 }
 
 int
