changeset 9914:15092dda0737

6847056 smb_ads_computer_op() dumps core when joining Win 2008 R2 domain 6848702 NDR buffer decode support 6797780 return code mismatches 6850931 SMB volume properties Quota tab display 6851425 Unable to create hard links via SMB/CIFS using cygwin 6840353 Fail to set or retrieve the share ACL using Windows test program
author Alan Wright <amw@Sun.COM>
date Fri, 19 Jun 2009 12:13:15 -0600
parents b5cbbe12c671
children bc9126487a5f
files usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c usr/src/lib/smbsrv/libmlrpc/common/ndr_marshal.c usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c usr/src/lib/smbsrv/libmlrpc/common/ndr_svc.c usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c usr/src/lib/smbsrv/libmlsvc/common/winreg_svc.c usr/src/lib/smbsrv/libsmb/common/smb_sd.c usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c usr/src/uts/common/fs/smbsrv/smb_common_transact.c usr/src/uts/common/fs/smbsrv/smb_dispatch.c usr/src/uts/common/fs/smbsrv/smb_fsops.c usr/src/uts/common/fs/smbsrv/smb_init.c usr/src/uts/common/fs/smbsrv/smb_node.c usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c usr/src/uts/common/fs/smbsrv/smb_rename.c usr/src/uts/common/fs/smbsrv/smb_trans2_query_fs_information.c usr/src/uts/common/fs/smbsrv/smb_util.c usr/src/uts/common/fs/smbsrv/smb_vops.c usr/src/uts/common/fs/smbsrv/smb_vss.c usr/src/uts/common/smbsrv/ndl/winreg.ndl usr/src/uts/common/smbsrv/ndr.h usr/src/uts/common/smbsrv/smb_fsops.h usr/src/uts/common/smbsrv/smb_kproto.h usr/src/uts/common/smbsrv/smb_ktypes.h usr/src/uts/common/smbsrv/smb_vops.h
diffstat 28 files changed, 773 insertions(+), 284 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h	Fri Jun 19 12:13:15 2009 -0600
@@ -477,6 +477,16 @@
 	void			(*nh_data_free)(void *);
 } ndr_handle_t;
 
+#define	NDR_PDU_SIZE_HINT_DEFAULT	(16*1024)
+#define	NDR_BUF_MAGIC			0x4E425546	/* NBUF */
+
+typedef struct ndr_buf {
+	uint32_t		nb_magic;
+	ndr_stream_t		nb_nds;
+	ndr_heap_t		*nb_heap;
+	ndr_typeinfo_t		*nb_ti;
+} ndr_buf_t;
+
 /* ndr_ops.c */
 void nds_initialize(ndr_stream_t *, unsigned, int, ndr_heap_t *);
 void nds_finalize(ndr_stream_t *, ndr_fraglist_t *);
@@ -488,8 +498,9 @@
 void ndr_clnt_free_heap(ndr_client_t *);
 
 /* ndr_marshal.c */
-int ndr_encode_decode_common(ndr_xa_t *, int, unsigned, ndr_typeinfo_t *,
-    void *);
+ndr_buf_t *ndr_buf_init(ndr_typeinfo_t *);
+void ndr_buf_fini(ndr_buf_t *);
+int ndr_buf_decode(ndr_buf_t *, unsigned, const char *data, size_t, void *);
 int ndr_decode_call(ndr_xa_t *, void *);
 int ndr_encode_return(ndr_xa_t *, void *);
 int ndr_encode_call(ndr_xa_t *, void *);
--- a/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers	Fri Jun 19 12:13:15 2009 -0600
@@ -40,6 +40,9 @@
 
 SUNWprivate {
     global:
+        ndr_buf_decode;
+        ndr_buf_fini;
+        ndr_buf_init;
         ndr_clnt_bind;
         ndr_clnt_call;
         ndr_clnt_free_heap;
--- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_client.c	Fri Jun 19 12:13:15 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.
  */
 
@@ -30,7 +30,6 @@
 #include <smbsrv/libsmb.h>
 #include <smbsrv/libmlrpc.h>
 
-#define	NDR_IS_LAST_FRAG(F)	((F) & NDR_PFC_LAST_FRAG)
 #define	NDR_DEFAULT_FRAGSZ	8192
 
 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *);
@@ -147,8 +146,8 @@
 	unsigned long		recv_pdu_scan_offset;
 	int			rc;
 
-	if (ndr_svc_find_stub(msvc, opnum) == NULL)
-		return (NDR_DRC_FAULT_API_OPNUM_INVALID);
+	if (ndr_svc_lookup_name(msvc->name) == NULL)
+		return (NDR_DRC_FAULT_API_SERVICE_INVALID);
 
 	bzero(&mxa, sizeof (mxa));
 	mxa.ptype = NDR_PTYPE_REQUEST;
--- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_marshal.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_marshal.c	Fri Jun 19 12:13:15 2009 -0600
@@ -19,10 +19,11 @@
  * 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.
  */
 
+#include <assert.h>
 #include <strings.h>
 #include <sys/param.h>
 
@@ -35,24 +36,13 @@
 static const int ndr_native_byte_order = NDR_REPLAB_INTG_LITTLE_ENDIAN;
 #endif
 
-int
-ndr_encode_decode_common(ndr_xa_t *mxa, int mode, unsigned opnum,
+static int ndr_decode_hdr_common(ndr_stream_t *, ndr_common_header_t *);
+
+static int
+ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum,
     ndr_typeinfo_t *ti, void *datum)
 {
-	ndr_stream_t	*nds;
-	int		m_op = NDR_MODE_TO_M_OP(mode);
-	int		rc;
-
-	if (m_op == NDR_M_OP_MARSHALL)
-		nds = &mxa->send_nds;
-	else
-		nds = &mxa->recv_nds;
-
-	/*
-	 * Make sure that nds is in the correct mode
-	 */
-	if (!NDR_MODE_MATCH(nds, mode))
-		return (NDR_DRC_FAULT_MODE_MISMATCH);
+	int rc;
 
 	/*
 	 * Perform the (un)marshalling
@@ -78,7 +68,7 @@
 		break;
 
 	default:
-		if (m_op == NDR_M_OP_MARSHALL)
+		if (nds->m_op == NDR_M_OP_MARSHALL)
 			rc = NDR_DRC_FAULT_ENCODE_FAILED;
 		else
 			rc = NDR_DRC_FAULT_DECODE_FAILED;
@@ -88,46 +78,151 @@
 	return (rc);
 }
 
+ndr_buf_t *
+ndr_buf_init(ndr_typeinfo_t *ti)
+{
+	ndr_buf_t		*nbuf;
+
+	if ((nbuf = calloc(1, sizeof (ndr_buf_t))) == NULL)
+		return (NULL);
+
+	if ((nbuf->nb_heap = ndr_heap_create()) == NULL) {
+		free(nbuf);
+		return (NULL);
+	}
+
+	nbuf->nb_ti = ti;
+	nbuf->nb_magic = NDR_BUF_MAGIC;
+	return (nbuf);
+}
+
+void
+ndr_buf_fini(ndr_buf_t *nbuf)
+{
+	assert(nbuf->nb_magic == NDR_BUF_MAGIC);
+
+	nds_destruct(&nbuf->nb_nds);
+	ndr_heap_destroy(nbuf->nb_heap);
+	nbuf->nb_magic = 0;
+	free(nbuf);
+}
+
+/*
+ * Decode an NDR encoded buffer.  The buffer is expected to contain
+ * a single fragment packet with a valid PDU header followed by NDR
+ * encoded data.  The structure to which result points should be
+ * of the appropriate type to hold the decoded output.  For example:
+ *
+ *	pac_info_t info;
+ *
+ * 	if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) {
+ *		rc = ndr_decode_buf(nbuf, opnum, data, datalen, &info);
+ *		...
+ *		ndr_buf_fini(nbuf);
+ *	}
+ */
+int
+ndr_buf_decode(ndr_buf_t *nbuf, unsigned opnum, const char *data,
+    size_t datalen, void *result)
+{
+	ndr_common_header_t	hdr;
+	unsigned		pdu_size_hint;
+	int			rc;
+
+	assert(nbuf->nb_magic == NDR_BUF_MAGIC);
+	assert(nbuf->nb_heap != NULL);
+	assert(nbuf->nb_ti != NULL);
+
+	if (datalen < NDR_PDU_SIZE_HINT_DEFAULT)
+		pdu_size_hint = NDR_PDU_SIZE_HINT_DEFAULT;
+	else
+		pdu_size_hint = datalen;
+
+	nds_destruct(&nbuf->nb_nds);
+	nds_initialize(&nbuf->nb_nds, pdu_size_hint, NDR_MODE_BUF_DECODE,
+	    nbuf->nb_heap);
+	bcopy(data, nbuf->nb_nds.pdu_base_addr, datalen);
+
+	rc = ndr_decode_hdr_common(&nbuf->nb_nds, &hdr);
+	if (NDR_DRC_IS_FAULT(rc))
+		return (rc);
+
+	if (!NDR_IS_SINGLE_FRAG(hdr.pfc_flags))
+		return (rc);
+
+	rc = ndr_encode_decode_common(&nbuf->nb_nds, opnum, nbuf->nb_ti,
+	    result);
+	return (rc);
+}
+
+/*
+ * Use the receive stream to unmarshall data (NDR_MODE_CALL_RECV).
+ */
 int
 ndr_decode_call(ndr_xa_t *mxa, void *params)
 {
-	int rc;
+	ndr_stream_t	*nds = &mxa->recv_nds;
+	int		rc;
 
-	rc = ndr_encode_decode_common(mxa, NDR_MODE_CALL_RECV,
-	    mxa->opnum, mxa->binding->service->interface_ti, params);
+	if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_RECV))
+		return (NDR_DRC_FAULT_MODE_MISMATCH);
+
+	rc = ndr_encode_decode_common(nds, mxa->opnum,
+	    mxa->binding->service->interface_ti, params);
 
 	return (rc + NDR_PTYPE_REQUEST);
 }
 
+/*
+ * Use the send stream to marshall data (NDR_MODE_RETURN_SEND).
+ */
 int
 ndr_encode_return(ndr_xa_t *mxa, void *params)
 {
-	int rc;
+	ndr_stream_t	*nds = &mxa->send_nds;
+	int		rc;
 
-	rc = ndr_encode_decode_common(mxa, NDR_MODE_RETURN_SEND,
-	    mxa->opnum, mxa->binding->service->interface_ti, params);
+	if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND))
+		return (NDR_DRC_FAULT_MODE_MISMATCH);
+
+	rc = ndr_encode_decode_common(nds, mxa->opnum,
+	    mxa->binding->service->interface_ti, params);
 
 	return (rc + NDR_PTYPE_RESPONSE);
 }
 
+/*
+ * Use the send stream to marshall data (NDR_MODE_CALL_SEND).
+ */
 int
 ndr_encode_call(ndr_xa_t *mxa, void *params)
 {
-	int rc;
+	ndr_stream_t	*nds = &mxa->send_nds;
+	int		rc;
 
-	rc = ndr_encode_decode_common(mxa, NDR_MODE_CALL_SEND,
-	    mxa->opnum, mxa->binding->service->interface_ti, params);
+	if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_SEND))
+		return (NDR_DRC_FAULT_MODE_MISMATCH);
+
+	rc = ndr_encode_decode_common(nds, mxa->opnum,
+	    mxa->binding->service->interface_ti, params);
 
 	return (rc + NDR_PTYPE_REQUEST);
 }
 
+/*
+ * Use the receive stream to unmarshall data (NDR_MODE_RETURN_RECV).
+ */
 int
 ndr_decode_return(ndr_xa_t *mxa, void *params)
 {
-	int rc;
+	ndr_stream_t	*nds = &mxa->recv_nds;
+	int		rc;
 
-	rc = ndr_encode_decode_common(mxa, NDR_MODE_RETURN_RECV,
-	    mxa->opnum, mxa->binding->service->interface_ti, params);
+	if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_RECV))
+		return (NDR_DRC_FAULT_MODE_MISMATCH);
+
+	rc = ndr_encode_decode_common(nds, mxa->opnum,
+	    mxa->binding->service->interface_ti, params);
 
 	return (rc + NDR_PTYPE_RESPONSE);
 }
@@ -137,6 +232,25 @@
 {
 	ndr_common_header_t	*hdr = &mxa->recv_hdr.common_hdr;
 	ndr_stream_t		*nds = &mxa->recv_nds;
+	int			rc;
+
+	rc = ndr_decode_hdr_common(nds, hdr);
+	if (NDR_DRC_IS_FAULT(rc))
+		return (rc);
+
+	/*
+	 * Verify the protocol version.
+	 */
+	if ((hdr->rpc_vers != 5) || (hdr->rpc_vers_minor != 0))
+		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_DECODE_FAILED));
+
+	mxa->ptype = hdr->ptype;
+	return (NDR_DRC_OK);
+}
+
+static int
+ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr)
+{
 	int			ptype;
 	int			rc;
 	int			charset;
@@ -160,12 +274,6 @@
 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_DECODE_FAILED));
 
 	/*
-	 * Verify the protocol version.
-	 */
-	if ((hdr->rpc_vers != 5) || (hdr->rpc_vers_minor != 0))
-		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_DECODE_FAILED));
-
-	/*
 	 * Check for ASCII as the character set.  This is an ASCII
 	 * versus EBCDIC option and has nothing to do with Unicode.
 	 */
@@ -186,11 +294,7 @@
 		ptype = NDR_PTYPE_REQUEST_WITH;	/* fake for sizing */
 	}
 
-	mxa->ptype = hdr->ptype;
-
-	rc = ndr_encode_decode_common(mxa,
-	    NDR_M_OP_AND_DIR_TO_MODE(nds->m_op, nds->dir),
-	    ptype, &TYPEINFO(ndr_hdr), hdr);
+	rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
 
 	return (NDR_DRC_PTYPE_RPCHDR(rc));
 }
@@ -245,9 +349,7 @@
 		ptype = NDR_PTYPE_REQUEST_WITH;	/* fake for sizing */
 	}
 
-	rc = ndr_encode_decode_common(mxa,
-	    NDR_M_OP_AND_DIR_TO_MODE(nds->m_op, nds->dir),
-	    ptype, &TYPEINFO(ndr_hdr), hdr);
+	rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
 
 	return (NDR_DRC_PTYPE_RPCHDR(rc));
 }
--- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c	Fri Jun 19 12:13:15 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.
  */
 
@@ -488,8 +488,8 @@
 		(void) strlcpy(ref_name, "----", NDOBUFSZ);
 	}
 
-	(void) snprintf(buf, NDOBUFSZ, "%c%c %02d %-.*s %-*s  %s",
-	    m_op_c, dir_c, indent, indent,
+	(void) snprintf(buf, NDOBUFSZ, "%c%c %-.*s %-*s  %s",
+	    m_op_c, dir_c, indent,
 	    "....+....+....+....+....+....",
 	    20 - indent, ref_name, note);
 
--- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_svc.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_svc.c	Fri Jun 19 12:13:15 2009 -0600
@@ -99,25 +99,6 @@
 	}
 }
 
-int
-ndr_svc_list(char *buffer, int bufsize)
-{
-	ndr_service_t *svc;
-	smb_ctxbuf_t ctx;
-	int i;
-
-	(void) smb_ctxbuf_init(&ctx, (uint8_t *)buffer, bufsize);
-
-	for (i = 0; i < NDR_MAX_SERVICES; i++) {
-		if ((svc = ndr_services[i]) != 0) {
-			(void) smb_ctxbuf_printf(&ctx, "%-16s %s\n",
-			    svc->name, svc->desc);
-		}
-	}
-
-	return (smb_ctxbuf_len(&ctx));
-}
-
 ndr_stub_table_t *
 ndr_svc_find_stub(ndr_service_t *svc, int opnum)
 {
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c	Fri Jun 19 12:13:15 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.
  */
 
@@ -303,7 +303,8 @@
 	mxa->heap = heap;
 
 	nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap);
-	nds_initialize(recv_nds, 16 * 1024, NDR_MODE_RETURN_RECV, heap);
+	nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT,
+	    NDR_MODE_RETURN_RECV, heap);
 	return (0);
 }
 
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c	Fri Jun 19 12:13:15 2009 -0600
@@ -123,9 +123,8 @@
 	}
 
 	do {
-		status = netr_open(di.d_dc, di.d_info.di_nbname, &netr_handle);
-		if (status != 0)
-			return (status);
+		if (netr_open(di.d_dc, di.d_info.di_nbname, &netr_handle) != 0)
+			return (NT_STATUS_OPEN_FAILED);
 
 		if (di.d_dc && (*netr_global_info.server != '\0')) {
 			(void) snprintf(server, sizeof (server),
--- a/usr/src/lib/smbsrv/libmlsvc/common/winreg_svc.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libmlsvc/common/winreg_svc.c	Fri Jun 19 12:13:15 2009 -0600
@@ -40,7 +40,7 @@
  *
  * For example:  HKEY_LOCAL_MACHINE\System\CurrentControlSet
  *
- * The HKEY_LOCAL_MACHINE root key contains a subkey call System, and
+ * The HKEY_LOCAL_MACHINE root key contains a subkey called System, and
  * System contains a subkey called CurrentControlSet.
  *
  * The WINREG RPC interface returns Win32 error codes.
@@ -60,11 +60,18 @@
  * List of supported registry keys (case-insensitive).
  */
 static char *winreg_keys[] = {
+	"HKLM",
+	"HKU",
+	"HKLM\\SOFTWARE",
+	"HKLM\\SYSTEM",
+	"System",
+	"CurrentControlSet",
 	"System\\CurrentControlSet\\Services\\Eventlog",
 	"System\\CurrentControlSet\\Services\\Eventlog\\Application",
 	"System\\CurrentControlSet\\Services\\Eventlog\\Security",
 	"System\\CurrentControlSet\\Services\\Eventlog\\System",
 	"System\\CurrentControlSet\\Control\\ProductOptions",
+	"SOFTWARE",
 	"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
 };
 
@@ -665,11 +672,19 @@
 winreg_s_OpenKey(void *arg, ndr_xa_t *mxa)
 {
 	struct winreg_OpenKey *param = arg;
+	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
+	ndr_handle_t *hd;
 	char *subkey = (char *)param->name.str;
-	ndr_hdid_t *id = NULL;
 	winreg_subkey_t *key;
 	char *dupkey;
 
+	if (subkey == NULL || *subkey == '\0') {
+		if ((hd = ndr_hdlookup(mxa, id)) != NULL)
+			subkey = hd->nh_data;
+	}
+
+	id = NULL;
+
 	if (subkey == NULL || list_is_empty(&winreg_keylist.kl_list)) {
 		bzero(&param->result_handle, sizeof (winreg_handle_t));
 		param->status = ERROR_FILE_NOT_FOUND;
--- a/usr/src/lib/smbsrv/libsmb/common/smb_sd.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_sd.c	Fri Jun 19 12:13:15 2009 -0600
@@ -32,10 +32,27 @@
 #include <smbsrv/ntifs.h>
 #include <smbsrv/smb_idmap.h>
 #include <smbsrv/ntstatus.h>
+#include <smbsrv/libsmb.h>
 
 #define	SMB_SHR_ACE_READ_PERMS	(ACE_READ_PERMS | ACE_EXECUTE | ACE_SYNCHRONIZE)
 #define	SMB_SHR_ACE_CONTROL_PERMS	(ACE_MODIFY_PERMS & (~ACE_DELETE_CHILD))
 
+#define	SMB_SHR_ACE_MODIFY_PERMS	(ACE_MODIFY_PERMS &		\
+	(~(ACE_READ_DATA | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS |	\
+	ACE_EXECUTE | ACE_DELETE_CHILD)))
+
+static struct {
+	int am_ace_perms;
+	int am_share_perms;
+} smb_ace_map[] = {
+	{ ACE_ALL_PERMS,	ACE_ALL_PERMS },
+	{ ACE_MODIFY_PERMS,	SMB_SHR_ACE_CONTROL_PERMS },
+	{ ACE_MODIFY_PERMS,	SMB_SHR_ACE_MODIFY_PERMS },
+	{ ACE_READ_PERMS,	SMB_SHR_ACE_READ_PERMS }
+};
+
+#define	SMB_ACE_MASK_MAP_SIZE	(sizeof (smb_ace_map)/sizeof (smb_ace_map[0]))
+
 static void smb_sd_set_sacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
 static void smb_sd_set_dacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
 static uint32_t smb_sd_fromfs(smb_fssd_t *, smb_sd_t *);
@@ -122,12 +139,13 @@
 static int
 smb_sd_adjust_read_mask(int mask)
 {
-	if (mask == ACE_ALL_PERMS)
-		return (ACE_ALL_PERMS);
-	if (mask == ACE_MODIFY_PERMS)
-		return (SMB_SHR_ACE_CONTROL_PERMS);
-	if (mask == ACE_READ_PERMS)
-		return (SMB_SHR_ACE_READ_PERMS);
+	int i;
+
+	for (i = 0; i < SMB_ACE_MASK_MAP_SIZE; ++i) {
+		if (smb_ace_map[i].am_ace_perms == mask)
+			return (smb_ace_map[i].am_share_perms);
+	}
+
 	return (-1);
 }
 
@@ -201,12 +219,13 @@
 static int
 smb_sd_adjust_write_mask(int mask)
 {
-	if (mask == ACE_ALL_PERMS)
-		return (ACE_ALL_PERMS);
-	if (mask == SMB_SHR_ACE_CONTROL_PERMS)
-		return (ACE_MODIFY_PERMS);
-	if (mask == SMB_SHR_ACE_READ_PERMS)
-		return (ACE_READ_PERMS);
+	int i;
+
+	for (i = 0; i < SMB_ACE_MASK_MAP_SIZE; ++i) {
+		if (smb_ace_map[i].am_share_perms == mask)
+			return (smb_ace_map[i].am_ace_perms);
+	}
+
 	return (-1);
 }
 
--- a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c	Fri Jun 19 12:13:15 2009 -0600
@@ -69,6 +69,7 @@
 #define	SMB_ADS_DCLEVEL_W2K	0
 #define	SMB_ADS_DCLEVEL_W2K3	2
 #define	SMB_ADS_DCLEVEL_W2K8	3
+#define	SMB_ADS_DCLEVEL_W2K8_R2 4
 
 /*
  * msDs-supportedEncryptionTypes (Windows Server 2008 only)
@@ -2039,7 +2040,7 @@
 	 * Windows 2008 DC expects the UPN attribute to be host/fqhn while
 	 * both Windows 2000 & 2003 expect it to be host/fqhn@realm.
 	 */
-	if (dclevel == SMB_ADS_DCLEVEL_W2K8)
+	if (dclevel >= SMB_ADS_DCLEVEL_W2K8)
 		user_principal = smb_krb5_get_spn(SMBKRB5_SPN_IDX_HOST, fqhost);
 	else
 		user_principal = smb_krb5_get_upn(spn_set[SMBKRB5_SPN_IDX_HOST],
@@ -2051,7 +2052,7 @@
 	}
 
 	max = (SMB_ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0))
-	    - (dclevel == SMB_ADS_DCLEVEL_W2K8 ?  0 : 1);
+	    - (dclevel >= SMB_ADS_DCLEVEL_W2K8 ?  0 : 1);
 
 	if (smb_ads_alloc_attr(attrs, max) != 0) {
 		free(user_principal);
@@ -2580,7 +2581,7 @@
 		goto adjoin_cleanup;
 	}
 
-	if (dclevel == SMB_ADS_DCLEVEL_W2K8) {
+	if (dclevel >= SMB_ADS_DCLEVEL_W2K8) {
 		num = sizeof (w2k8enctypes) / sizeof (krb5_enctype);
 		encptr = w2k8enctypes;
 	} else {
--- a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c	Fri Jun 19 12:13:15 2009 -0600
@@ -32,6 +32,8 @@
 #include <smbsrv/lmerr.h>
 #include <smbsrv/nterror.h>
 
+#define	SMB_QUOTA_UNLIMITED	0xFFFFFFFFFFFFFFFF;
+
 /*
  * count of bytes in server response packet
  * except parameters and data. Note that setup
@@ -40,7 +42,7 @@
 #define	RESP_HEADER_LEN		24
 
 /*
- * NB. I started by using common functions for transaction/transaction2
+ * We started by using common functions for transaction/transaction2
  * and transaction_secondary/transaction2_secondary because they
  * are respectively so similar. However, it turned out to be a bad
  * idea because of quirky differences. Be sure if you modify one
@@ -452,8 +454,7 @@
 
 	case NT_TRANSACT_QUERY_QUOTA:
 		(void) smb_nt_transact_query_quota(sr, xa);
-		smbsr_error(sr, 0, ERRSRV, ERRaccess);
-		return (SDRC_ERROR);
+		break;
 
 	case NT_TRANSACT_SET_QUOTA:
 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
@@ -523,21 +524,78 @@
 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 }
 
-
 /*
  * smb_nt_transact_query_quota
  *
- * There are 16 parameter bytes: fid, flags and 12 zero bytes.
+ * Request                    Description
+ * ========================== ==================================
+ * WORD fid
+ * BYTE ReturnSingleEntry     A boolean indicating whether to return
+ *                            a single entry or multiple entries.
+ * BYTE RestartScan           A boolean indicating whether to continue from
+ *                            the previous request or restart a new sequence.
+ * DWORD SidListLength        The length in bytes of the SidList or 0 if
+ *                            there is no SidList.
+ * DWORD StartSidLength       The length in bytes of the StartSid or 0 if
+ *                            there is no StartSid.  The server must ignore
+ *                            StartSidLength if SidListLength is non-zero.
+ * DWORD StartSidOffset       The offset, in bytes, to the StartSid in the
+ *                            parameter block.
+ *
+ * If SidListLength is non-zero, the request contains a list of SIDs
+ * for which information is requested.  If StartSidLength is nonzero,
+ * the request contains the SID at which the enumeration should start.
+ *
+ * One of SidListLength and StartSidLength must be 0.  If both are 0,
+ * all SIDs are to be enumerated by the server as if they were passed
+ * the SidList.
  */
 static smb_sdrc_t
 smb_nt_transact_query_quota(struct smb_request *sr, struct smb_xa *xa)
 {
-	uint16_t fid;
-	uint16_t flags;
+	smb_sid_t	*sid;
+	uint8_t		single, restart;
+	uint16_t	fid;
+	uint32_t	sidlen, listlen, startlen, startoff;
+	uint64_t	limit, used, mtime;
 
-	if (smb_mbc_decodef(&xa->req_param_mb, "%ww", sr, &fid, &flags))
+	if (smb_mbc_decodef(&xa->req_param_mb, "%wbblll", sr,
+	    &fid, &single, &restart, &listlen, &startlen, &startoff))
 		return (SDRC_ERROR);
 
+	if (restart == 0) {
+		(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0);
+		return (SDRC_SUCCESS);
+	}
+
+	/*
+	 * BUILTIN\Administrators
+	 */
+	if ((sid = smb_sid_fromstr("S-1-5-32-544")) == NULL) {
+		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 0, 0);
+		return (SDRC_ERROR);
+	}
+
+	sidlen = smb_sid_len(sid);
+	used = 0;
+	mtime = 0xBA7ADAAC0436C601; /* canned dummy timestamp */
+	limit = SMB_QUOTA_UNLIMITED;
+
+	/*
+	 * The encoded length of "llqqqq" is 40 bytes.
+	 */
+	(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 40 + sidlen);
+
+	(void) smb_mbc_encodef(&xa->rep_data_mb, "llqqqq",
+	    0,		/* next offset */
+	    sidlen,	/* sid length */
+	    mtime,	/* change time */
+	    used,	/* quota used */
+	    limit,	/* soft limit */
+	    limit);	/* hard limit */
+
+	smb_encode_sid(xa, sid);
+	smb_sid_free(sid);
 	return (SDRC_SUCCESS);
 }
 
--- a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c	Fri Jun 19 12:13:15 2009 -0600
@@ -420,7 +420,9 @@
 	{ SMB_SDT_OPS(nt_cancel),				/* 0xA4 164 */
 	    NT_LM_0_12, 0,
 	    { "SmbNtCancel",	KSTAT_DATA_UINT64 } },
-	{ SMB_SDT_OPS(invalid), 0, 0, 0 },			/* 0xA5 165 */
+	{ SMB_SDT_OPS(nt_rename),				/* 0xA5 165 */
+	    NT_LM_0_12, 0,
+	    { "SmbNtRename",	KSTAT_DATA_UINT64 } },
 	{ SMB_SDT_OPS(invalid), 0, 0, 0 },			/* 0xA6 166 */
 	{ SMB_SDT_OPS(invalid), 0, 0, 0 },			/* 0xA7 167 */
 	{ SMB_SDT_OPS(invalid), 0, 0, 0 },			/* 0xA8 168 */
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c	Fri Jun 19 12:13:15 2009 -0600
@@ -951,6 +951,64 @@
 }
 
 /*
+ * smb_fsop_link
+ *
+ * All SMB functions should use this smb_vop_link wrapper to ensure that
+ * the smb_vop_link is performed with the appropriate credentials.
+ * Please document any direct call to smb_vop_link to explain the reason
+ * for avoiding this wrapper.
+ *
+ * It is assumed that references exist on from_dnode and to_dnode coming
+ * into this routine.
+ */
+int
+smb_fsop_link(smb_request_t *sr, cred_t *cr, smb_node_t *to_dnode,
+    smb_node_t *from_fnode, char *to_name)
+{
+	char	*longname = NULL;
+	int	flags = 0;
+	int	rc;
+
+	ASSERT(sr);
+	ASSERT(sr->tid_tree);
+	ASSERT(cr);
+	ASSERT(to_dnode);
+	ASSERT(to_dnode->n_magic == SMB_NODE_MAGIC);
+	ASSERT(to_dnode->n_state != SMB_NODE_STATE_DESTROYING);
+	ASSERT(from_fnode);
+	ASSERT(from_fnode->n_magic == SMB_NODE_MAGIC);
+	ASSERT(from_fnode->n_state != SMB_NODE_STATE_DESTROYING);
+
+	if (SMB_TREE_CONTAINS_NODE(sr, from_fnode) == 0)
+		return (EACCES);
+
+	if (SMB_TREE_CONTAINS_NODE(sr, to_dnode) == 0)
+		return (EACCES);
+
+	if (SMB_TREE_IS_READONLY(sr))
+		return (EROFS);
+
+	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
+		flags = SMB_IGNORE_CASE;
+	if (SMB_TREE_SUPPORTS_CATIA(sr))
+		flags |= SMB_CATIA;
+
+	if (smb_maybe_mangled_name(to_name)) {
+		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+		rc = smb_unmangle_name(to_dnode, to_name, longname, MAXNAMELEN);
+		kmem_free(longname, MAXNAMELEN);
+
+		if (rc == 0)
+			rc = EEXIST;
+		if (rc != ENOENT)
+			return (rc);
+	}
+
+	rc = smb_vop_link(to_dnode->vp, from_fnode->vp, to_name, flags, cr);
+	return (rc);
+}
+
+/*
  * smb_fsop_rename
  *
  * All SMB functions should use this smb_vop_rename wrapper to ensure that
@@ -2004,7 +2062,7 @@
 			error = smb_fsop_access(sr, ga_cred, snode,
 			    READ_CONTROL);
 			if (error)
-				return (error);
+				return (EACCES);
 		}
 
 		attr.sa_mask = SMB_AT_UID | SMB_AT_GID;
--- a/usr/src/uts/common/fs/smbsrv/smb_init.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_init.c	Fri Jun 19 12:13:15 2009 -0600
@@ -62,7 +62,6 @@
 int	smb_oplock_timeout = OPLOCK_STD_TIMEOUT;
 int	smb_flush_required = 1;
 int	smb_dirsymlink_enable = 1;
-int	smb_announce_quota = 0;
 int	smb_sign_debug = 0;
 uint_t	smb_audit_flags =
 #ifdef	DEBUG
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.c	Fri Jun 19 12:13:15 2009 -0600
@@ -972,6 +972,17 @@
 		return (NT_STATUS_SUCCESS);
 }
 
+void
+smb_node_notify_change(smb_node_t *node)
+{
+	SMB_NODE_VALID(node);
+
+	if (node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
+		node->flags |= NODE_FLAGS_CHANGED;
+		smb_process_node_notify_change_queue(node);
+	}
+}
+
 /*
  * smb_node_start_crit()
  *
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c	Fri Jun 19 12:13:15 2009 -0600
@@ -30,11 +30,9 @@
 #include <smbsrv/cifs.h>
 
 static void smb_encode_sd(struct smb_xa *, smb_sd_t *, uint32_t);
-static void smb_encode_sid(struct smb_xa *, smb_sid_t *);
 static void smb_encode_sacl(struct smb_xa *, smb_acl_t *);
 static void smb_encode_dacl(struct smb_xa *, smb_acl_t *);
 
-uint32_t smb_decode_sd(struct smb_xa *, smb_sd_t *);
 static smb_sid_t *smb_decode_sid(struct smb_xa *, uint32_t);
 static smb_acl_t *smb_decode_acl(struct smb_xa *, uint32_t);
 
@@ -288,7 +286,7 @@
  *
  * Encodes given SID in the reply buffer.
  */
-static void
+void
 smb_encode_sid(struct smb_xa *xa, smb_sid_t *sid)
 {
 	int i;
--- a/usr/src/uts/common/fs/smbsrv/smb_rename.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_rename.c	Fri Jun 19 12:13:15 2009 -0600
@@ -29,7 +29,23 @@
 #include <smbsrv/smb_fsops.h>
 #include <sys/nbmlock.h>
 
+/*
+ * NT_RENAME InformationLevels:
+ *
+ * SMB_NT_RENAME_MOVE_CLUSTER_INFO	Server returns invalid parameter.
+ * SMB_NT_RENAME_SET_LINK_INFO		Create a hard link to a file.
+ * SMB_NT_RENAME_RENAME_FILE		In-place rename of a file.
+ * SMB_NT_RENAME_MOVE_FILE		Move (rename) a file.
+ */
+#define	SMB_NT_RENAME_MOVE_CLUSTER_INFO	0x0102
+#define	SMB_NT_RENAME_SET_LINK_INFO	0x0103
+#define	SMB_NT_RENAME_RENAME_FILE	0x0104
+#define	SMB_NT_RENAME_MOVE_FILE		0x0105
+
 static int smb_do_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
+static int smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
+static int smb_rename_check_attr(smb_node_t *, uint16_t);
+static void smb_rename_set_error(smb_request_t *, int);
 
 /*
  * smb_com_rename
@@ -78,10 +94,8 @@
 smb_sdrc_t
 smb_com_rename(smb_request_t *sr)
 {
-	static kmutex_t mutex;
 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
-	struct smb_node *dst_node;
 	int rc;
 
 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
@@ -90,54 +104,13 @@
 		return (SDRC_ERROR);
 	}
 
-	mutex_enter(&mutex);
 	rc = smb_do_rename(sr, src_fqi, dst_fqi);
-	mutex_exit(&mutex);
 
 	if (rc != 0) {
-		/*
-		 * The following values are based on observed WFWG,
-		 * Windows 9x, NT and Windows 2000 behaviour.
-		 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
-		 * Windows 95 clients don't see the problem because the
-		 * target is deleted before the rename request.
-		 */
-		switch (rc) {
-		case EEXIST:
-			smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION,
-			    ERRDOS, ERROR_ALREADY_EXISTS);
-			break;
-		case EPIPE:
-			smbsr_error(sr, NT_STATUS_SHARING_VIOLATION,
-			    ERRDOS, ERROR_SHARING_VIOLATION);
-			break;
-		case ENOENT:
-			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
-			    ERRDOS, ERROR_FILE_NOT_FOUND);
-			break;
-		default:
-			smbsr_errno(sr, rc);
-			break;
-		}
-
+		smb_rename_set_error(sr, rc);
 		return (SDRC_ERROR);
 	}
 
-	if (src_fqi->fq_dnode)
-		smb_node_release(src_fqi->fq_dnode);
-
-	dst_node = dst_fqi->fq_dnode;
-	if (dst_node) {
-		if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
-			dst_node->flags |= NODE_FLAGS_CHANGED;
-			smb_process_node_notify_change_queue(dst_node);
-		}
-		smb_node_release(dst_node);
-	}
-
-	SMB_NULL_FQI_NODES(*src_fqi);
-	SMB_NULL_FQI_NODES(*dst_fqi);
-
 	rc = smbsr_encode_empty_result(sr);
 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 }
@@ -145,23 +118,17 @@
 /*
  * smb_do_rename
  *
- * Backend to smb_com_rename to ensure that the rename operation is atomic.
- * This function should be called within a mutual exclusion region. If the
- * source and destination are identical, we don't actually do a rename, we
- * just check that the conditions are right. If the source and destination
- * files differ only in case, we a case-sensitive rename. Otherwise, we do
- * a full case-insensitive rename.
+ * Common code for renaming a file.
  *
- * This function should always return errno values.
+ * If the source and destination are identical, we go through all
+ * the checks but we don't actually do the rename.  If the source
+ * and destination files differ only in case, we do a case-sensitive
+ * rename.  Otherwise, we do a full case-insensitive rename.
  *
- * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi
- * are not released in this routine but in smb_com_rename().
+ * Returns errno values.
  */
 static int
-smb_do_rename(
-    smb_request_t *sr,
-    smb_fqi_t *src_fqi,
-    smb_fqi_t *dst_fqi)
+smb_do_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
 {
 	smb_node_t *src_node;
 	char *dstname;
@@ -169,12 +136,14 @@
 	int rc;
 	int count;
 
-	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) {
+	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0)
 		return (rc);
-	}
 
 	src_node = src_fqi->fq_fnode;
 
+	if ((rc = smb_rename_check_attr(src_node, src_fqi->fq_sattr)) != 0)
+		goto rename_cleanup_nodes;
+
 	/*
 	 * Break the oplock before access checks. If a client
 	 * has a file open, this will force a flush or close,
@@ -198,45 +167,28 @@
 
 	if (status == NT_STATUS_SHARING_VIOLATION) {
 		smb_node_end_crit(src_node);
-
-		smb_node_release(src_node);
-		smb_node_release(src_fqi->fq_dnode);
-
-		SMB_NULL_FQI_NODES(*src_fqi);
-		SMB_NULL_FQI_NODES(*dst_fqi);
-		return (EPIPE); /* = ERRbadshare */
+		rc = EPIPE;	/* = ERRbadshare */
+		goto rename_cleanup_nodes;
 	}
 
 	status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE);
 
 	if (status != NT_STATUS_SUCCESS) {
 		smb_node_end_crit(src_node);
-
-		smb_node_release(src_node);
-		smb_node_release(src_fqi->fq_dnode);
-
-		SMB_NULL_FQI_NODES(*src_fqi);
-		SMB_NULL_FQI_NODES(*dst_fqi);
-		return (EACCES);
+		rc = EACCES;
+		goto rename_cleanup_nodes;
 	}
 
 	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
 	    dst_fqi->fq_path.pn_path) == 0) {
 		if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) {
 			smb_node_end_crit(src_node);
-
-			smb_node_release(src_node);
-			smb_node_release(src_fqi->fq_dnode);
-
-			SMB_NULL_FQI_NODES(*src_fqi);
-			SMB_NULL_FQI_NODES(*dst_fqi);
-			return (rc);
+			goto rename_cleanup_nodes;
 		}
 
 		/*
 		 * Because the fqm parameter to smbd_fs_query() was 0,
-		 * a successful return value means that dst_fqi->fq_fnode
-		 * may be NULL.
+		 * dst_fqi->fq_fnode may be NULL.
 		 */
 		if (dst_fqi->fq_fnode)
 			smb_node_release(dst_fqi->fq_fnode);
@@ -244,61 +196,34 @@
 		rc = strcmp(src_fqi->fq_od_name, dst_fqi->fq_last_comp);
 		if (rc == 0) {
 			smb_node_end_crit(src_node);
-
-			smb_node_release(src_node);
-			smb_node_release(src_fqi->fq_dnode);
-			smb_node_release(dst_fqi->fq_dnode);
-
-			SMB_NULL_FQI_NODES(*src_fqi);
-			SMB_NULL_FQI_NODES(*dst_fqi);
-			return (0);
+			goto rename_cleanup_nodes;
 		}
 
 		rc = smb_fsop_rename(sr, sr->user_cr,
-		    src_fqi->fq_dnode,
-		    src_fqi->fq_od_name,
-		    dst_fqi->fq_dnode,
-		    dst_fqi->fq_last_comp);
-
-		if (rc != 0) {
-			smb_node_release(src_fqi->fq_dnode);
-			smb_node_release(dst_fqi->fq_dnode);
-
-			SMB_NULL_FQI_NODES(*src_fqi);
-			SMB_NULL_FQI_NODES(*dst_fqi);
-		}
+		    src_fqi->fq_dnode, src_fqi->fq_od_name,
+		    dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
 
 		smb_node_end_crit(src_node);
-
-		smb_node_release(src_node);
-		return (rc);
+		if (rc == 0)
+			smb_node_notify_change(dst_fqi->fq_dnode);
+		goto rename_cleanup_nodes;
 	}
 
 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
 	if (rc != 0) {
 		smb_node_end_crit(src_node);
-
-		smb_node_release(src_node);
-		smb_node_release(src_fqi->fq_dnode);
-
-		SMB_NULL_FQI_NODES(*src_fqi);
-		SMB_NULL_FQI_NODES(*dst_fqi);
-		return (rc);
+		goto rename_cleanup_nodes;
 	}
 
 	/*
-	 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return
-	 * value, only dst_fqi->fq_dnode is valid (dst_fqi->fq_fnode
-	 * is NULL).
+	 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode
+	 * is valid (dst_fqi->fq_fnode is NULL).
 	 */
 
 	/*
-	 * Use the unmangled form of the destination name if the
-	 * source and destination names are the same and the source
-	 * name is mangled.  (We are taking a chance here, assuming
-	 * that this is what the user wants.)
+	 * If the source name is mangled but the source and destination
+	 * on-disk names are identical, we'll use the on-disk name.
 	 */
-
 	if ((smb_maybe_mangled_name(src_fqi->fq_last_comp)) &&
 	    (strcmp(src_fqi->fq_last_comp, dst_fqi->fq_last_comp) == 0)) {
 		dstname = src_fqi->fq_od_name;
@@ -307,22 +232,286 @@
 	}
 
 	rc = smb_fsop_rename(sr, sr->user_cr,
-	    src_fqi->fq_dnode,
-	    src_fqi->fq_od_name,
-	    dst_fqi->fq_dnode,
-	    dstname);
-
-	if (rc != 0) {
-		smb_node_release(src_fqi->fq_dnode);
-		smb_node_release(dst_fqi->fq_dnode);
-
-		SMB_NULL_FQI_NODES(*src_fqi);
-		SMB_NULL_FQI_NODES(*dst_fqi);
-	}
+	    src_fqi->fq_dnode, src_fqi->fq_od_name,
+	    dst_fqi->fq_dnode, dstname);
 
 	smb_node_end_crit(src_node);
 
+	if (rc == 0)
+		smb_node_notify_change(dst_fqi->fq_dnode);
+
+rename_cleanup_nodes:
 	smb_node_release(src_node);
+	smb_node_release(src_fqi->fq_dnode);
 
+	if (dst_fqi->fq_dnode)
+		smb_node_release(dst_fqi->fq_dnode);
+
+	SMB_NULL_FQI_NODES(*src_fqi);
+	SMB_NULL_FQI_NODES(*dst_fqi);
 	return (rc);
 }
+
+/*
+ * smb_com_nt_rename
+ *
+ * Rename a file. Files OldFileName must exist and NewFileName must not.
+ * Both pathnames must be relative to the Tid specified in the request.
+ * Open files may be renamed.
+ *
+ * Multiple files may be renamed in response to a single request as Rename
+ * File supports wildcards in the file name (last component of the path).
+ * NOTE: we don't support rename with wildcards.
+ *
+ * SearchAttributes indicates the attributes that the target file(s) must
+ * have. If SearchAttributes is zero then only normal files are renamed.
+ * If the system file or hidden attributes are specified then the rename
+ * is inclusive - both the specified type(s) of files and normal files are
+ * renamed. The encoding of SearchAttributes is described in section 3.10
+ * - File Attribute Encoding.
+ *
+ *  Client Request                     Description
+ *  =================================  ==================================
+ *  UCHAR WordCount;                   Count of parameter words = 4
+ *  USHORT SearchAttributes;
+ *  USHORT InformationLevel;           0x0103 Create a hard link
+ *                                     0x0104 In-place rename
+ *                                     0x0105 Move (rename) a file
+ *  ULONG ClusterCount                 Servers should ignore this value
+ *  USHORT ByteCount;                  Count of data bytes; min = 4
+ *  UCHAR Buffer[];                    Buffer containing:
+ *                                     UCHAR BufferFormat1 0x04
+ *                                     UCHAR OldFileName[] OldFileName
+ *                                     UCHAR BufferFormat1 0x04
+ *                                     UCHAR OldFileName[] NewFileName
+ *
+ *  Server Response                    Description
+ *  =================================  ==================================
+ *  UCHAR WordCount;                   Count of parameter words = 0
+ *  UCHAR ByteCount;                   Count of data bytes = 0
+ */
+smb_sdrc_t
+smb_pre_nt_rename(smb_request_t *sr)
+{
+	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
+	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
+	uint32_t clusters;
+	int rc;
+
+	rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr,
+	    &sr->arg.dirop.info_level, &clusters);
+	if (rc == 0) {
+		rc = smbsr_decode_data(sr, "%SS", sr,
+		    &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path);
+
+		dst_fqi->fq_sattr = 0;
+	}
+
+	DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr,
+	    struct dirop *, &sr->arg.dirop);
+
+	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
+}
+
+void
+smb_post_nt_rename(smb_request_t *sr)
+{
+	DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr);
+}
+
+smb_sdrc_t
+smb_com_nt_rename(smb_request_t *sr)
+{
+	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
+	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
+	int rc;
+
+	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
+		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
+		    ERRDOS, ERROR_ACCESS_DENIED);
+		return (SDRC_ERROR);
+	}
+
+	if (smb_convert_wildcards(src_fqi->fq_path.pn_path) != 0) {
+		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
+		    ERRDOS, ERROR_BAD_PATHNAME);
+		return (SDRC_ERROR);
+	}
+
+	switch (sr->arg.dirop.info_level) {
+	case SMB_NT_RENAME_SET_LINK_INFO:
+		rc = smb_make_link(sr, src_fqi, dst_fqi);
+		break;
+	case SMB_NT_RENAME_RENAME_FILE:
+	case SMB_NT_RENAME_MOVE_FILE:
+		rc = smb_do_rename(sr, src_fqi, dst_fqi);
+		break;
+	case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
+		rc = EINVAL;
+		break;
+	default:
+		rc = EACCES;
+		break;
+	}
+
+	if (rc != 0) {
+		smb_rename_set_error(sr, rc);
+		return (SDRC_ERROR);
+	}
+
+	rc = smbsr_encode_empty_result(sr);
+	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
+}
+
+/*
+ * smb_make_link
+ *
+ * Common code for creating a hard link (adding an additional name
+ * for a file.
+ *
+ * If the source and destination are identical, we go through all
+ * the checks but we don't create a link.
+ *
+ * Returns errno values.
+ */
+static int
+smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
+{
+	smb_node_t *src_fnode;
+	DWORD status;
+	int rc;
+	int count;
+
+	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0)
+		return (rc);
+
+	src_fnode = src_fqi->fq_fnode;
+
+	if ((rc = smb_rename_check_attr(src_fnode, src_fqi->fq_sattr)) != 0)
+		goto link_cleanup_nodes;
+
+	/*
+	 * Break the oplock before access checks. If a client
+	 * has a file open, this will force a flush or close,
+	 * which may affect the outcome of any share checking.
+	 */
+	(void) smb_oplock_break(src_fnode, sr->session, B_FALSE);
+
+	for (count = 0; count <= 3; count++) {
+		if (count) {
+			smb_node_end_crit(src_fnode);
+			delay(MSEC_TO_TICK(400));
+		}
+
+		smb_node_start_crit(src_fnode, RW_READER);
+		status = smb_node_rename_check(src_fnode);
+
+		if (status != NT_STATUS_SHARING_VIOLATION)
+			break;
+	}
+
+	if (status == NT_STATUS_SHARING_VIOLATION) {
+		smb_node_end_crit(src_fnode);
+		rc = EPIPE;	/* = ERRbadshare */
+		goto link_cleanup_nodes;
+	}
+
+	status = smb_range_check(sr, src_fnode, 0, UINT64_MAX, B_TRUE);
+
+	if (status != NT_STATUS_SUCCESS) {
+		smb_node_end_crit(src_fnode);
+		rc = EACCES;
+		goto link_cleanup_nodes;
+	}
+
+	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
+	    dst_fqi->fq_path.pn_path) == 0) {
+		smb_node_end_crit(src_fnode);
+		rc = 0;
+		goto link_cleanup_nodes;
+	}
+
+	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
+	if (rc != 0) {
+		smb_node_end_crit(src_fnode);
+		goto link_cleanup_nodes;
+	}
+
+	/*
+	 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode
+	 * is valid (dst_fqi->fq_fnode is NULL).
+	 */
+	rc = smb_fsop_link(sr, sr->user_cr, dst_fqi->fq_dnode, src_fnode,
+	    dst_fqi->fq_last_comp);
+
+	smb_node_end_crit(src_fnode);
+
+	if (rc == 0)
+		smb_node_notify_change(dst_fqi->fq_dnode);
+
+link_cleanup_nodes:
+	smb_node_release(src_fnode);
+	smb_node_release(src_fqi->fq_dnode);
+
+	if (dst_fqi->fq_dnode)
+		smb_node_release(dst_fqi->fq_dnode);
+
+	SMB_NULL_FQI_NODES(*src_fqi);
+	SMB_NULL_FQI_NODES(*dst_fqi);
+	return (rc);
+}
+
+static int
+smb_rename_check_attr(smb_node_t *node, uint16_t sattr)
+{
+	uint16_t dosattr = smb_node_get_dosattr(node);
+
+	if ((dosattr & FILE_ATTRIBUTE_HIDDEN) && !(SMB_SEARCH_HIDDEN(sattr)))
+		return (ESRCH);
+
+	if ((dosattr & FILE_ATTRIBUTE_SYSTEM) && !(SMB_SEARCH_SYSTEM(sattr)))
+		return (ESRCH);
+
+	return (0);
+}
+
+/*
+ * The following values are based on observed WFWG, Windows 9x, Windows NT
+ * and Windows 2000 behaviour.
+ *
+ * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
+ *
+ * Windows 95 clients don't see the problem because the target is deleted
+ * before the rename request.
+ */
+static void
+smb_rename_set_error(smb_request_t *sr, int errnum)
+{
+	static struct {
+		int errnum;
+		uint16_t errcode;
+		uint32_t status32;
+	} rc_map[] = {
+	{ EEXIST, ERROR_ALREADY_EXISTS,	NT_STATUS_OBJECT_NAME_COLLISION },
+	{ EPIPE,  ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION },
+	{ ENOENT, ERROR_FILE_NOT_FOUND,	NT_STATUS_OBJECT_NAME_NOT_FOUND },
+	{ ESRCH,  ERROR_FILE_NOT_FOUND,	NT_STATUS_NO_SUCH_FILE },
+	{ EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER },
+	{ EACCES, ERROR_ACCESS_DENIED,	NT_STATUS_ACCESS_DENIED }
+	};
+
+	int i;
+
+	if (errnum == 0)
+		return;
+
+	for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
+		if (rc_map[i].errnum == errnum) {
+			smbsr_error(sr, rc_map[i].status32,
+			    ERRDOS, rc_map[i].errcode);
+			return;
+		}
+	}
+
+	smbsr_errno(sr, errnum);
+}
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_fs_information.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_fs_information.c	Fri Jun 19 12:13:15 2009 -0600
@@ -383,11 +383,10 @@
 		if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0)
 			flags |= FILE_CASE_SENSITIVE_SEARCH;
 
-		if (tree->t_flags & SMB_TREE_STREAMS)
+		if (tree->t_flags & SMB_TREE_STREAMS) {
 			flags |= FILE_NAMED_STREAMS;
-
-		if (smb_announce_quota)
 			flags |= FILE_VOLUME_QUOTAS;
+		}
 
 		(void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr,
 		    flags,
--- a/usr/src/uts/common/fs/smbsrv/smb_util.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_util.c	Fri Jun 19 12:13:15 2009 -0600
@@ -284,8 +284,10 @@
  *
  * Find the last component of path and split it into filename
  * and stream name.
- * On return the named stream always has type ":$DATA",
- * i.e. 'stream' contains :<sname>:$DATA
+ *
+ * On return the named stream type will be present.  The stream
+ * type defaults to ":$DATA", if it has not been defined
+ * For exmaple, 'stream' contains :<sname>:$DATA
  */
 void
 smb_stream_parse_name(char *path, char *filename, char *stream)
@@ -350,11 +352,18 @@
  * - the path is not a stream name
  * - a path is specified but the fname is ommitted.
  * - the stream_type is specified but not valid.
- *   Only type $DATA is supported, where $DATA is case-insensitive.
+ *
+ * Note: the stream type is case-insensitive.
  */
 uint32_t
 smb_validate_stream_name(smb_pathname_t *pn)
 {
+	static char *strmtype[] = {
+		"$DATA",
+		"$INDEX_ALLOCATION"
+	};
+	int i;
+
 	ASSERT(pn);
 	ASSERT(pn->pn_sname);
 
@@ -364,8 +373,12 @@
 	if ((pn->pn_pname) && !(pn->pn_fname))
 		return (NT_STATUS_OBJECT_NAME_INVALID);
 
-	if ((pn->pn_stype != NULL) &&
-	    (strcasecmp(pn->pn_stype, "$DATA") != 0)) {
+	if (pn->pn_stype != NULL) {
+		for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
+			if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
+				return (NT_STATUS_SUCCESS);
+		}
+
 		return (NT_STATUS_OBJECT_NAME_INVALID);
 	}
 
--- a/usr/src/uts/common/fs/smbsrv/smb_vops.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c	Fri Jun 19 12:13:15 2009 -0600
@@ -652,6 +652,39 @@
 }
 
 /*
+ * smb_vop_link(target-dir-vp, source-file-vp, target-name)
+ *
+ * Create a link - same tree (identical TID) only.
+ */
+int
+smb_vop_link(vnode_t *to_dvp, vnode_t *from_vp, char *to_name,
+    int flags, cred_t *cr)
+{
+	int option_flags = 0;
+	char *np, *buf;
+	int rc;
+
+	if (flags & SMB_IGNORE_CASE)
+		option_flags = FIGNORECASE;
+
+	if (flags & SMB_CATIA) {
+		buf = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+		np = smb_vop_catia_v5tov4(to_name, buf, MAXNAMELEN);
+		if (strchr(np, '/') != NULL) {
+			kmem_free(buf, MAXNAMELEN);
+			return (EILSEQ);
+		}
+
+		rc = VOP_LINK(to_dvp, from_vp, np, cr, &smb_ct, option_flags);
+		kmem_free(buf, MAXNAMELEN);
+		return (rc);
+	}
+
+	rc = VOP_LINK(to_dvp, from_vp, to_name, cr, &smb_ct, option_flags);
+	return (rc);
+}
+
+/*
  * smb_vop_rename()
  *
  * The rename is for files in the same tree (identical TID) only.
--- a/usr/src/uts/common/fs/smbsrv/smb_vss.c	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/fs/smbsrv/smb_vss.c	Fri Jun 19 12:13:15 2009 -0600
@@ -71,45 +71,37 @@
 {
 	uint32_t count = 0;
 	char *root_path;
-	uint32_t err = SDRC_SUCCESS;
+	uint32_t status = NT_STATUS_SUCCESS;
 	smb_dr_return_gmttokens_t gmttokens;
 
-	if (xa->smb_mdrcnt < SMB_VSS_COUNT_SIZE) {
-		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
-		return (SDRC_ERROR);
-	}
+	if (xa->smb_mdrcnt < SMB_VSS_COUNT_SIZE)
+		return (NT_STATUS_INVALID_PARAMETER);
 
 	root_path  = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
-	err = smb_vss_get_fsmountpath(sr, root_path, MAXPATHLEN);
+	if (smb_vss_get_fsmountpath(sr, root_path, MAXPATHLEN) != 0)
+		return (NT_STATUS_INVALID_PARAMETER);
 
-	if (err != SDRC_SUCCESS) {
-		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
-		return (SDRC_ERROR);
-	}
 	if (xa->smb_mdrcnt == SMB_VSS_COUNT_SIZE) {
 		count = smb_upcall_vss_get_count(root_path);
 		if (smb_mbc_encodef(&xa->rep_data_mb, "lllw", count, 0,
 		    (count * SMB_VSS_GMT_NET_SIZE(sr) +
 		    smb_ascii_or_unicode_null_len(sr)), 0) != 0) {
-			smbsr_error(sr, 0, ERRSRV, ERRerror);
-			err = SDRC_ERROR;
+			status = NT_STATUS_INVALID_PARAMETER;
 		}
 	} else {
 		count = xa->smb_mdrcnt / SMB_VSS_GMT_NET_SIZE(sr);
 
 		smb_upcall_vss_get_snapshots(root_path, count, &gmttokens);
 
-		err = smb_vss_encode_gmttokens(sr, xa, count, &gmttokens);
+		status = smb_vss_encode_gmttokens(sr, xa, count, &gmttokens);
 
 		smb_upcall_vss_get_snapshots_free(&gmttokens);
 	}
 
 	kmem_free(root_path, MAXPATHLEN);
-
-	return (err);
+	return (status);
 }
 
-
 /*
  * sr - the request info, used to find root of dataset,
  *      unicode or ascii, where the share is rooted in the
@@ -133,7 +125,6 @@
  * One the new smb node is found, the path is modified by
  * removing the @GMT token from the path in the buf.
  */
-
 int
 smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
     smb_node_t *cur_node, char *buf, smb_node_t **vss_cur_node,
@@ -171,7 +162,6 @@
 	nodepath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 
 	err = smb_vss_get_fsmountpath(sr, rootpath, MAXPATHLEN);
-
 	if (err != 0)
 		goto error;
 
@@ -248,7 +238,6 @@
 	return (err);
 }
 
-
 static boolean_t
 smb_vss_is_gmttoken(const char *s)
 {
@@ -329,38 +318,33 @@
 	uint32_t returned_count;
 	uint32_t num_gmttokens;
 	char **gmttokens;
-	uint32_t err = SDRC_SUCCESS;
+	uint32_t status = NT_STATUS_SUCCESS;
 	uint32_t data_size;
 
 	returned_count = snap_data->rg_count;
 	num_gmttokens = snap_data->rg_gmttokens.rg_gmttokens_len;
 	gmttokens = snap_data->rg_gmttokens.rg_gmttokens_val;
 
-	if (returned_count > count) {
-		err = NT_STATUS_BUFFER_TOO_SMALL;
-	}
+	if (returned_count > count)
+		status = NT_STATUS_BUFFER_TOO_SMALL;
 
 	data_size = returned_count * SMB_VSS_GMT_NET_SIZE(sr) +
 	    smb_ascii_or_unicode_null_len(sr);
 
 	if (smb_mbc_encodef(&xa->rep_data_mb, "lll", returned_count,
-	    num_gmttokens, data_size) != 0) {
-			smbsr_error(sr, 0, ERRSRV, ERRerror);
-			err = SDRC_ERROR;
-		}
+	    num_gmttokens, data_size) != 0)
+		return (NT_STATUS_INVALID_PARAMETER);
 
-	if (err == SDRC_SUCCESS) {
+	if (status == NT_STATUS_SUCCESS) {
 		for (i = 0; i < num_gmttokens; i++) {
 			if (smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
-			    *gmttokens) != 0) {
-				smbsr_error(sr, 0, ERRSRV, ERRerror);
-				err = SDRC_ERROR;
-			}
+			    *gmttokens) != 0)
+				status = NT_STATUS_INVALID_PARAMETER;
 			gmttokens++;
 		}
 	}
 
-	return (err);
+	return (status);
 }
 
 /* This removes the first @GMT from the path */
--- a/usr/src/uts/common/smbsrv/ndl/winreg.ndl	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/smbsrv/ndl/winreg.ndl	Fri Jun 19 12:13:15 2009 -0600
@@ -246,7 +246,7 @@
 	IN	winreg_handle_t	handle;
 	IN	DWORD	index;
 	IN	winreg_string_t	name_in;
-	IN	winreg_string_t	class_in;
+	IN	winreg_string_t	*class_in;
 	OUT	winreg_string_t	name_out;
 	OUT	winreg_string_t	*class_out;
 	INOUT	file_time_t	*change_time;
--- a/usr/src/uts/common/smbsrv/ndr.h	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/smbsrv/ndr.h	Fri Jun 19 12:13:15 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.
  */
 
@@ -277,6 +277,8 @@
 #define	NDR_MODE_CALL_RECV	(NDR_M_OP_UNMARSHALL + NDR_DIR_IN)
 #define	NDR_MODE_RETURN_SEND	(NDR_M_OP_MARSHALL + NDR_DIR_OUT)
 #define	NDR_MODE_RETURN_RECV	(NDR_M_OP_UNMARSHALL + NDR_DIR_OUT)
+#define	NDR_MODE_BUF_ENCODE	NDR_MODE_CALL_SEND
+#define	NDR_MODE_BUF_DECODE	NDR_MODE_RETURN_RECV
 
 #define	NDR_MODE_TO_M_OP(MODE)	((MODE) & 0x0F)
 #define	NDR_MODE_TO_DIR(MODE)	((MODE) & 0xF0)
@@ -285,6 +287,11 @@
 #define	NDR_MODE_MATCH(NDS, MODE) \
 	(NDR_M_OP_AND_DIR_TO_MODE((NDS)->m_op, (NDS)->dir) == (MODE))
 
+#define	NDR_IS_FIRST_FRAG(F)	((F) & NDR_PFC_FIRST_FRAG)
+#define	NDR_IS_LAST_FRAG(F)	((F) & NDR_PFC_LAST_FRAG)
+#define	NDR_IS_SINGLE_FRAG(F)	\
+	(NDR_IS_FIRST_FRAG((F)) && NDR_IS_LAST_FRAG((F)))
+
 #define	NDS_F_NONE		0x00
 #define	NDS_F_NOTERM		0x01	/* strings are not null terminated */
 #define	NDS_SETF(S, F)		((S)->flags |= (F))
--- a/usr/src/uts/common/smbsrv/smb_fsops.h	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/smbsrv/smb_fsops.h	Fri Jun 19 12:13:15 2009 -0600
@@ -68,6 +68,9 @@
 
 int smb_maybe_mangled_name(char *name);
 
+int smb_fsop_link(smb_request_t *, cred_t *, smb_node_t *, smb_node_t *,
+    char *);
+
 int smb_fsop_rename(struct smb_request *sr, cred_t *cr,
     smb_node_t *from_snode, char *from_name, smb_node_t *to_snode,
     char *to_name);
--- a/usr/src/uts/common/smbsrv/smb_kproto.h	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h	Fri Jun 19 12:13:15 2009 -0600
@@ -49,7 +49,6 @@
 extern	int smb_maxbufsize;
 extern	int smb_flush_required;
 extern	int smb_dirsymlink_enable;
-extern	int smb_announce_quota;
 extern	int smb_oplock_timeout;
 extern	int smb_sign_debug;
 extern	uint_t smb_audit_flags;
@@ -105,6 +104,7 @@
 SMB_COM_DECL(negotiate);
 SMB_COM_DECL(nt_cancel);
 SMB_COM_DECL(nt_create_andx);
+SMB_COM_DECL(nt_rename);
 SMB_COM_DECL(nt_transact);
 SMB_COM_DECL(nt_transact_secondary);
 SMB_COM_DECL(open);
@@ -291,6 +291,7 @@
 int smb_lock_range_access(struct smb_request *, struct smb_node *,
     uint64_t, uint64_t, boolean_t);
 
+void	smb_encode_sid(struct smb_xa *, smb_sid_t *);
 uint32_t smb_decode_sd(struct smb_xa *, smb_sd_t *);
 
 /*
@@ -385,6 +386,7 @@
     uint32_t, uint32_t);
 DWORD smb_node_rename_check(smb_node_t *);
 DWORD smb_node_delete_check(smb_node_t *);
+void smb_node_notify_change(smb_node_t *);
 
 u_offset_t smb_node_get_size(smb_node_t *, smb_attr_t *);
 void smb_node_set_time(struct smb_node *node, timestruc_t *crtime,
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h	Fri Jun 19 12:13:15 2009 -0600
@@ -1441,6 +1441,7 @@
 	    struct dirop {
 		smb_fqi_t	fqi;
 		smb_fqi_t	dst_fqi;
+		uint16_t	info_level;
 	    } dirop;
 
 	    open_param_t	open;
--- a/usr/src/uts/common/smbsrv/smb_vops.h	Fri Jun 19 10:28:43 2009 -0700
+++ b/usr/src/uts/common/smbsrv/smb_vops.h	Fri Jun 19 12:13:15 2009 -0600
@@ -115,6 +115,7 @@
     cred_t *);
 int smb_vop_create(vnode_t *, char *, smb_attr_t *, vnode_t **, int, cred_t *,
     vsecattr_t *);
+int smb_vop_link(vnode_t *, vnode_t *, char *, int, cred_t *);
 int smb_vop_remove(vnode_t *, char *, int, cred_t *);
 int smb_vop_rename(vnode_t *, char *, vnode_t *, char *, int, cred_t *);
 int smb_vop_mkdir(vnode_t *, char *, smb_attr_t *, vnode_t **, int, cred_t *,