Print this page
NEX-19375 SMB2 durable handle create response missing timeout
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-5672 SMB2_create dtrace probe
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-3553 SMB2/3 durable handles
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5586 SMB2 ofiles need real Persistent IDs
NEX-5313 SMB2 oplock break notification should use TID=0
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-4836 WBC: export and destroy may hang due to faulty wrc migration termination
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-3733 Want SMB2 Apple extensions (allow disable)
NEX-3733 Want SMB2 Apple extensions
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4239 smbtorture create failures re. allocation size
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-1635 Codenomicon: SMB2 TC: 157974 Panic in smb2_create/smb_decode_sd
SMB-115 Support SMB path names with length > 1024
SMB-100 Internal error if filename is too long
Approved by: Gordon Ross <gwr@nexenta.com>
SMB-136 Snapshots not visible in Windows previous versions
SMB-138 memory leak in smb2_create
SMB-122 smbd core dumps in smbd_dc_update / smb_log
SMB-117 Win7 fails to open security properties
SMB-96 Codenomicon: SMB2 TC: 141500 - Panic in smb2_decode_create_ctx
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)

@@ -8,11 +8,11 @@
  * source.  A copy of the CDDL is also available via the Internet at
  * http://www.illumos.org/license/CDDL.
  */
 
 /*
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * Dispatch function for SMB2_CREATE
  * [MS-SMB2] 2.2.13

@@ -19,11 +19,30 @@
  */
 
 #include <smbsrv/smb2_kproto.h>
 #include <smbsrv/smb_fsops.h>
 
+#define DH_PERSISTENT   SMB2_DHANDLE_FLAG_PERSISTENT
+
 /*
+ * Compile-time check that the SMB2_LEASE_... definitions
+ * match the (internal) equivalents from ntifs.h
+ */
+#if SMB2_LEASE_NONE != OPLOCK_LEVEL_NONE
+#error "SMB2_LEASE_NONE"
+#endif
+#if SMB2_LEASE_READ_CACHING != OPLOCK_LEVEL_CACHE_READ
+#error "SMB2_LEASE_READ_CACHING"
+#endif
+#if SMB2_LEASE_HANDLE_CACHING != OPLOCK_LEVEL_CACHE_HANDLE
+#error "SMB2_LEASE_HANDLE_CACHING"
+#endif
+#if SMB2_LEASE_WRITE_CACHING != OPLOCK_LEVEL_CACHE_WRITE
+#error "SMB2_LEASE_WRITE_CACHING"
+#endif
+
+/*
  * Some flags used locally to keep track of which Create Context
  * names have been provided and/or requested.
  */
 #define CCTX_EA_BUFFER                  1
 #define CCTX_SD_BUFFER                  2

@@ -32,18 +51,21 @@
 #define CCTX_ALLOCATION_SIZE            0x10
 #define CCTX_QUERY_MAX_ACCESS           0x20
 #define CCTX_TIMEWARP_TOKEN             0x40
 #define CCTX_QUERY_ON_DISK_ID           0x80
 #define CCTX_REQUEST_LEASE              0x100
+#define CCTX_AAPL_EXT                   0x200
+#define CCTX_DH_REQUEST_V2              0x400
+#define CCTX_DH_RECONNECT_V2            0x800
 
-
 typedef struct smb2_create_ctx_elem {
         uint32_t cce_len;
         mbuf_chain_t cce_mbc;
 } smb2_create_ctx_elem_t;
 
 typedef struct smb2_create_ctx {
+        mbuf_chain_t cc_in_mbc;
         uint_t  cc_in_flags;    /* CCTX_... */
         uint_t  cc_out_flags;   /* CCTX_... */
         /* Elements we may see in the request. */
         smb2_create_ctx_elem_t cc_in_ext_attr;
         smb2_create_ctx_elem_t cc_in_sec_desc;

@@ -50,68 +72,82 @@
         smb2_create_ctx_elem_t cc_in_dh_request;
         smb2_create_ctx_elem_t cc_in_dh_reconnect;
         smb2_create_ctx_elem_t cc_in_alloc_size;
         smb2_create_ctx_elem_t cc_in_time_warp;
         smb2_create_ctx_elem_t cc_in_req_lease;
+        smb2_create_ctx_elem_t cc_in_aapl;
+        smb2_create_ctx_elem_t cc_in_dh_request_v2;
+        smb2_create_ctx_elem_t cc_in_dh_reconnect_v2;
         /* Elements we my place in the response */
         smb2_create_ctx_elem_t cc_out_max_access;
         smb2_create_ctx_elem_t cc_out_file_id;
+        smb2_create_ctx_elem_t cc_out_aapl;
+        smb2_create_ctx_elem_t cc_out_req_lease;
+        smb2_create_ctx_elem_t cc_out_dh_request;
+        smb2_create_ctx_elem_t cc_out_dh_request_v2;
 } smb2_create_ctx_t;
 
 static uint32_t smb2_decode_create_ctx(
-        mbuf_chain_t *, smb2_create_ctx_t *);
+        smb_request_t *, smb2_create_ctx_t *);
 static uint32_t smb2_encode_create_ctx(
-        mbuf_chain_t *, smb2_create_ctx_t *);
+        smb_request_t *, smb2_create_ctx_t *);
 static int smb2_encode_create_ctx_elem(
         mbuf_chain_t *, smb2_create_ctx_elem_t *, uint32_t);
 static void smb2_free_create_ctx(smb2_create_ctx_t *);
 
+int smb2_enable_dh = 1;
+
 smb_sdrc_t
 smb2_create(smb_request_t *sr)
 {
         smb_attr_t *attr;
         smb2_create_ctx_elem_t *cce;
         smb2_create_ctx_t cctx;
-        mbuf_chain_t cc_mbc;
         smb_arg_open_t *op = &sr->arg.open;
         smb_ofile_t *of = NULL;
         uint16_t StructSize;
         uint8_t SecurityFlags;
-        uint8_t OplockLevel;
         uint32_t ImpersonationLevel;
         uint64_t SmbCreateFlags;
         uint64_t Reserved4;
         uint16_t NameOffset;
         uint16_t NameLength;
         uint32_t CreateCtxOffset;
         uint32_t CreateCtxLength;
-        smb2fid_t smb2fid;
+        smb2fid_t smb2fid = { 0, 0 };
         uint32_t status;
+        int dh_flags;
         int skip;
         int rc = 0;
 
         bzero(&cctx, sizeof (cctx));
-        bzero(&cc_mbc, sizeof (cc_mbc));
+        op->create_ctx = &cctx; /* for debugging */
 
         /*
          * Paranoia.  This will set sr->fid_ofile, so
          * if we already have one, release it now.
          */
         if (sr->fid_ofile != NULL) {
-                smb_ofile_request_complete(sr->fid_ofile);
                 smb_ofile_release(sr->fid_ofile);
                 sr->fid_ofile = NULL;
         }
 
         /*
-         * SMB2 Create request
+         * Decode the SMB2 Create request
+         *
+         * Most decode errors return SDRC_ERROR, but
+         * for some we give a more specific error.
+         *
+         * In the "decode section" (starts here) any
+         * errors should either return SDRC_ERROR, or
+         * if any cleanup is needed, goto errout.
          */
         rc = smb_mbc_decodef(
             &sr->smb_data, "wbblqqlllllwwll",
             &StructSize,                /* w */
             &SecurityFlags,             /* b */
-            &OplockLevel,               /* b */
+            &op->op_oplock_level,       /* b */
             &ImpersonationLevel,        /* l */
             &SmbCreateFlags,            /* q */
             &Reserved4,                 /* q */
             &op->desired_access,        /* l */
             &op->dattr,                 /* l */

@@ -129,19 +165,21 @@
          * We're normally positioned at the path name now,
          * but there could be some padding before it.
          */
         skip = (NameOffset + sr->smb2_cmd_hdr) -
             sr->smb_data.chain_offset;
-        if (skip < 0) {
-                status = NT_STATUS_OBJECT_PATH_INVALID;
-                goto errout;
-        }
+        if (skip < 0)
+                return (SDRC_ERROR);
         if (skip > 0)
                 (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
 
         /*
          * Get the path name
+         *
+         * Name too long is not technically a decode error,
+         * but it's very rare, so we'll just skip the
+         * dtrace probes for this error case.
          */
         if (NameLength >= SMB_MAXPATHLEN) {
                 status = NT_STATUS_OBJECT_PATH_INVALID;
                 goto errout;
         }

@@ -155,54 +193,11 @@
                         goto errout;
                 }
         }
         op->fqi.fq_dnode = sr->tid_tree->t_snode;
 
-        switch (OplockLevel) {
-        case SMB2_OPLOCK_LEVEL_NONE:
-                op->op_oplock_level = SMB_OPLOCK_NONE;
-                break;
-        case SMB2_OPLOCK_LEVEL_II:
-                op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
-                break;
-        case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
-                op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
-                break;
-        case SMB2_OPLOCK_LEVEL_BATCH:
-                op->op_oplock_level = SMB_OPLOCK_BATCH;
-                break;
-        case SMB2_OPLOCK_LEVEL_LEASE:
-                status = NT_STATUS_INVALID_PARAMETER;
-                goto errout;
-        }
-        op->op_oplock_levelII = B_TRUE;
-
         /*
-         * ImpersonationLevel (spec. says ignore)
-         * SmbCreateFlags (spec. says ignore)
-         */
-
-        if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
-            !(op->desired_access & DELETE)) {
-                status = NT_STATUS_INVALID_PARAMETER;
-                goto errout;
-        }
-        if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) {
-                status = NT_STATUS_INVALID_PARAMETER;
-                goto errout;
-        }
-
-        if (op->dattr & FILE_FLAG_WRITE_THROUGH)
-                op->create_options |= FILE_WRITE_THROUGH;
-        if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE)
-                op->create_options |= FILE_DELETE_ON_CLOSE;
-        if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS)
-                op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
-        if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT)
-                sr->user_cr = smb_user_getprivcred(sr->uid_user);
-
-        /*
          * If there is a "Create Context" payload, decode it.
          * This may carry things like a security descriptor,
          * extended attributes, etc. to be used in create.
          *
          * The create ctx buffer must start after the headers

@@ -214,171 +209,469 @@
                     sr->smb_data.chain_offset) {
                         status = NT_STATUS_INVALID_PARAMETER;
                         goto errout;
                 }
 
-                rc = MBC_SHADOW_CHAIN(&cc_mbc, &sr->smb_data,
+                rc = MBC_SHADOW_CHAIN(&cctx.cc_in_mbc, &sr->smb_data,
                     sr->smb2_cmd_hdr + CreateCtxOffset, CreateCtxLength);
                 if (rc) {
                         status = NT_STATUS_INVALID_PARAMETER;
                         goto errout;
                 }
-                status = smb2_decode_create_ctx(&cc_mbc, &cctx);
+                status = smb2_decode_create_ctx(sr, &cctx);
                 if (status)
                         goto errout;
+        }
 
-                if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
-                        status = NT_STATUS_EAS_NOT_SUPPORTED;
-                        goto errout;
+        /*
+         * Everything is decoded into some internal form, so
+         * in this probe one can look at sr->arg.open etc.
+         *
+         * This marks the end of the "decode" section and the
+         * beginning of the "body" section.  Any errors in
+         * this section should use: goto cmd_done (which is
+         * just before the dtrace "done" probe).
+         */
+        DTRACE_SMB2_START(op__Create, smb_request_t *, sr); /* arg.open */
+
+        /*
+         * Process the incoming create contexts (already decoded),
+         * that need action before the open, starting with the
+         * Durable Handle ones, which may override others.
+         */
+
+        /*
+         * Only disk trees get durable handles.
+         */
+        if (smb2_enable_dh == 0 ||
+            (sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
+                cctx.cc_in_flags &=
+                    ~(CCTX_DH_REQUEST | CCTX_DH_REQUEST_V2 |
+                    CCTX_DH_RECONNECT | CCTX_DH_RECONNECT_V2);
                 }
 
-                if (cctx.cc_in_flags & CCTX_SD_BUFFER) {
-                        smb_sd_t sd;
-                        cce = &cctx.cc_in_sec_desc;
-                        status = smb_decode_sd(
-                            &cce->cce_mbc, &sd);
-                        if (status)
-                                goto errout;
-                        op->sd = kmem_alloc(sizeof (sd), KM_SLEEP);
-                        *op->sd = sd;
+        /*
+         * DH v2 is only valid in SMB3.0 and later.
+         * If seen in earlier dialects, ignore.
+         */
+        if (sr->session->dialect < SMB_VERS_3_0) {
+                cctx.cc_in_flags &=
+                    ~(CCTX_DH_REQUEST_V2|CCTX_DH_RECONNECT_V2);
                 }
 
-                if (cctx.cc_in_flags & CCTX_ALLOCATION_SIZE) {
-                        cce = &cctx.cc_in_alloc_size;
-                        rc = smb_mbc_decodef(&cce->cce_mbc, "q", &op->dsize);
-                        if (rc) {
+        /*
+         * It is an error to specify more than one Durable Handle
+         * operation in a single create, except when only the v1
+         * REQUEST and RECONNECT operations are specified. In that
+         * case, the v1 REQUEST is ignored.
+         */
+        dh_flags = cctx.cc_in_flags &
+            (CCTX_DH_REQUEST | CCTX_DH_REQUEST_V2 |
+            CCTX_DH_RECONNECT | CCTX_DH_RECONNECT_V2);
+        if ((dh_flags & (dh_flags - 1)) != 0 &&
+            dh_flags != (CCTX_DH_REQUEST|CCTX_DH_RECONNECT)) {
                                 status = NT_STATUS_INVALID_PARAMETER;
-                                goto errout;
+                goto cmd_done;
                         }
-                }
 
                 /*
-                 * Support for opening "Previous Versions".
-                 * [MS-SMB2] 2.2.13.2.7  Data is an NT time.
+         * Reconnect is special in MANY ways, including the
+         * somewhat surprising (specified) behavior that
+         * most other creat parameters are ignored, and
+         * many create context types are ignored too.
                  */
-                if (cctx.cc_in_flags & CCTX_TIMEWARP_TOKEN) {
-                        uint64_t timewarp;
-                        cce = &cctx.cc_in_time_warp;
-                        status = smb_mbc_decodef(&cce->cce_mbc,
-                            "q", &timewarp);
-                        if (status)
-                                goto errout;
-                        smb_time_nt_to_unix(timewarp, &op->timewarp);
-                        op->create_timewarp = B_TRUE;
+        op->dh_vers = SMB2_NOT_DURABLE;
+        if ((cctx.cc_in_flags &
+            (CCTX_DH_RECONNECT|CCTX_DH_RECONNECT_V2)) != 0) {
+
+                if ((cctx.cc_in_flags & CCTX_DH_RECONNECT_V2) != 0)
+                        op->dh_vers = SMB2_DURABLE_V2;
+                else
+                        op->dh_vers = SMB2_DURABLE_V1;
+
+                /* Ignore these create contexts. */
+                cctx.cc_in_flags &=
+                    ~(CCTX_DH_REQUEST |
+                    CCTX_DH_REQUEST_V2 |
+                    CCTX_EA_BUFFER |
+                    CCTX_SD_BUFFER |
+                    CCTX_ALLOCATION_SIZE |
+                    CCTX_TIMEWARP_TOKEN |
+                    CCTX_QUERY_ON_DISK_ID);
+
+                /*
+                 * Reconnect check needs to know if a lease was requested.
+                 * The requested oplock level is ignored in reconnect, so
+                 * using op_oplock_level to convey this info.
+                 */
+                if (cctx.cc_in_flags & CCTX_REQUEST_LEASE)
+                        op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+                else
+                        op->op_oplock_level = 0;
+
+                status = smb2_dh_reconnect(sr);
+                if (status != NT_STATUS_SUCCESS)
+                        goto cmd_done;
+
+                /*
+                 * Skip most open execution during reconnect,
+                 * but need (reclaimed) oplock state in *op.
+                 */
+                of = sr->fid_ofile;
+
+                op->op_oplock_state = of->f_oplock.og_state;
+                if (of->f_lease != NULL) {
+                        smb_lease_t *ls = of->f_lease;
+
+                        op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+                        op->lease_state = ls->ls_state &
+                            OPLOCK_LEVEL_CACHE_MASK;
+                        op->lease_flags = (ls->ls_breaking != 0) ?
+                            SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
+                        op->lease_epoch = ls->ls_epoch;
+                        op->lease_version = ls->ls_version;
+                } else {
+                        switch (op->op_oplock_state & OPLOCK_LEVEL_TYPE_MASK) {
+                        default:
+                        case OPLOCK_LEVEL_NONE:
+                                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+                                break;
+                        case OPLOCK_LEVEL_TWO:
+                                op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
+                                break;
+                        case OPLOCK_LEVEL_ONE:
+                                op->op_oplock_level =
+                                    SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+                                break;
+                        case OPLOCK_LEVEL_BATCH:
+                                op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+                                break;
                 }
         }
 
+                goto reconnect_done;
+        }
+
         /*
-         * The real open call.   Note: this gets attributes into
-         * op->fqi.fq_fattr (SMB_AT_ALL).  We need those below.
+         * Real create (of a new handle, not reconnect)
          */
-        status = smb_common_open(sr);
-        if (status != NT_STATUS_SUCCESS)
-                goto errout;
-        attr = &op->fqi.fq_fattr;
 
         /*
-         * Convert the negotiate Oplock level back into
-         * SMB2 encoding form.
+         * Validate the requested oplock level.
+         * Conversion to internal form is in smb2_oplock_acquire()
          */
         switch (op->op_oplock_level) {
-        default:
-        case SMB_OPLOCK_NONE:
-                OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
+        case SMB2_OPLOCK_LEVEL_NONE:            /* OPLOCK_LEVEL_NONE */
+        case SMB2_OPLOCK_LEVEL_II:              /* OPLOCK_LEVEL_TWO */
+        case SMB2_OPLOCK_LEVEL_EXCLUSIVE:       /* OPLOCK_LEVEL_ONE */
+        case SMB2_OPLOCK_LEVEL_BATCH:           /* OPLOCK_LEVEL_BATCH */
+                /*
+                 * Ignore lease create context (if any)
+                 */
+                cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
                 break;
-        case SMB_OPLOCK_LEVEL_II:
-                OplockLevel = SMB2_OPLOCK_LEVEL_II;
+
+        case SMB2_OPLOCK_LEVEL_LEASE:           /* OPLOCK_LEVEL_GRANULAR */
+                /*
+                 * Require a lease create context.
+                 */
+                if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) == 0) {
+                        cmn_err(CE_NOTE, "smb2:create, oplock=ff and no lease");
+                        status = NT_STATUS_INVALID_PARAMETER;
+                        goto cmd_done;
+                }
+
+                /*
+                 * Validate lease request state
+                 * Only a few valid combinations.
+                 */
+                switch (op->lease_state) {
+                case SMB2_LEASE_NONE:
+                case SMB2_LEASE_READ_CACHING:
+                case SMB2_LEASE_READ_CACHING | SMB2_LEASE_HANDLE_CACHING:
+                case SMB2_LEASE_READ_CACHING | SMB2_LEASE_WRITE_CACHING:
+                case SMB2_LEASE_READ_CACHING | SMB2_LEASE_WRITE_CACHING |
+                    SMB2_LEASE_HANDLE_CACHING:
                 break;
-        case SMB_OPLOCK_EXCLUSIVE:
-                OplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+
+                default:
+                        /*
+                         * Invalid lease state flags
+                         * Just force to "none".
+                         */
+                        op->lease_state = SMB2_LEASE_NONE;
                 break;
-        case SMB_OPLOCK_BATCH:
-                OplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
+                }
                 break;
+
+        default:
+                /* Unknown SMB2 oplock level. */
+                status = NT_STATUS_INVALID_PARAMETER;
+                goto cmd_done;
         }
 
         /*
+         * Only disk trees get oplocks or leases.
+         */
+        if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
+                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+                cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
+        }
+
+        if ((sr->tid_tree->t_flags & SMB_TREE_CA) == 0)
+                op->dh_v2_flags &= ~DH_PERSISTENT;
+
+        if ((cctx.cc_in_flags &
+            (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0) {
+                if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0)
+                        op->dh_vers = SMB2_DURABLE_V2;
+                else
+                        op->dh_vers = SMB2_DURABLE_V1;
+        }
+
+        if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
+                status = NT_STATUS_EAS_NOT_SUPPORTED;
+                goto cmd_done;
+        }
+
+        /*
+         * ImpersonationLevel (spec. says validate + ignore)
+         * SmbCreateFlags (spec. says ignore)
+         */
+
+        if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
+            !(op->desired_access & DELETE)) {
+                status = NT_STATUS_INVALID_PARAMETER;
+                goto cmd_done;
+        }
+
+        if (op->dattr & FILE_FLAG_WRITE_THROUGH)
+                op->create_options |= FILE_WRITE_THROUGH;
+        if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE)
+                op->create_options |= FILE_DELETE_ON_CLOSE;
+        if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS)
+                op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+        if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT)
+                sr->user_cr = smb_user_getprivcred(sr->uid_user);
+        if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) {
+                status = NT_STATUS_INVALID_PARAMETER;
+                goto cmd_done;
+        }
+
+        /*
+         * The real open call.   Note: this gets attributes into
+         * op->fqi.fq_fattr (SMB_AT_ALL).  We need those below.
+         * When of != NULL, goto errout closes it.
+         */
+        status = smb_common_open(sr);
+        if (status != NT_STATUS_SUCCESS)
+                goto cmd_done;
+        of = sr->fid_ofile;
+
+        /*
+         * Set the "persistent" part of the file ID
+         * (only for DISK shares).  Need this even for
+         * non-durable handles in case we get the ioctl
+         * to set "resiliency" on this handle.
+         */
+        if (of->f_ftype == SMB_FTYPE_DISK) {
+                if ((op->dh_v2_flags & DH_PERSISTENT) != 0)
+                        smb_ofile_set_persistid_ph(of);
+                else
+                        smb_ofile_set_persistid_dh(of);
+        }
+
+        /*
+         * [MS-SMB2] 3.3.5.9.8
+         * Handling the SMB2_CREATE_REQUEST_LEASE Create Context
+         */
+        if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) != 0) {
+                status = smb2_lease_create(sr, sr->session->clnt_uuid);
+                if (status != NT_STATUS_SUCCESS) {
+                        if (op->action_taken == SMB_OACT_CREATED) {
+                                smb_ofile_set_delete_on_close(sr, of);
+                        }
+                        goto cmd_done;
+                }
+        }
+        if (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
+                smb2_lease_acquire(sr);
+        } else if (op->op_oplock_level != SMB2_OPLOCK_LEVEL_NONE) {
+                smb2_oplock_acquire(sr);
+        }
+
+        /*
+         * Make this a durable open, but only if:
+         * (durable handle requested and...)
+         *
+         * 1. op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH
+         * 2. A lease is requested with handle caching
+         *    - for v1, the lease must not be on a directory
+         * 3. For v2, flags has "persistent" && tree->is_CA
+         *    (when tree not CA, turned off persist above)
+         *
+         * Otherwise, DH requests are ignored, so we set
+         * dh_vers = not durable
+         */
+        if ((cctx.cc_in_flags &
+            (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0 &&
+            smb_node_is_file(of->f_node) &&
+            ((op->dh_v2_flags & DH_PERSISTENT) != 0 ||
+            (op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) ||
+            (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE &&
+            (op->lease_state & OPLOCK_LEVEL_CACHE_HANDLE) != 0))) {
+                /*
+                 * OK, make this handle "durable"
+                 */
+                if (op->dh_vers == SMB2_DURABLE_V2) {
+                        (void) memcpy(of->dh_create_guid,
+                            op->create_guid, UUID_LEN);
+
+                        if ((op->dh_v2_flags & DH_PERSISTENT) != 0) {
+                                if (smb2_dh_make_persistent(sr, of) == 0) {
+                                        of->dh_persist = B_TRUE;
+                                } else {
+                                        op->dh_v2_flags = 0;
+                                }
+                        }
+                }
+                if (op->dh_vers != SMB2_NOT_DURABLE) {
+                        uint32_t msto;
+
+                        of->dh_vers = op->dh_vers;
+                        of->dh_expire_time = 0;
+
+                        /*
+                         * Client may provide timeout=0 to request
+                         * the default timeout (in mSec.)
+                         */
+                        msto = op->dh_timeout;
+                        if (msto == 0) {
+                                msto = (of->dh_persist) ?
+                                    smb2_persist_timeout :
+                                    smb2_dh_def_timeout;
+                        }
+                        if (msto > smb2_dh_max_timeout)
+                                msto = smb2_dh_max_timeout;
+                        op->dh_timeout = msto;
+                        of->dh_timeout_offset = MSEC2NSEC(msto);
+                }
+        } else {
+                op->dh_vers = SMB2_NOT_DURABLE;
+                op->dh_v2_flags = 0;
+        }
+
+        /*
          * NB: after the above smb_common_open() success,
          * we have a handle allocated (sr->fid_ofile).
          * If we don't return success, we must close it.
          *
          * Using sr->smb_fid as the file handle for now,
          * though it could later be something larger,
          * (16 bytes) similar to an NFSv4 open handle.
          */
-        of = sr->fid_ofile;
-        smb2fid.persistent = 0;
+reconnect_done:
+        smb2fid.persistent = of->f_persistid;
         smb2fid.temporal = sr->smb_fid;
 
         switch (sr->tid_tree->t_res_type & STYPE_MASK) {
         case STYPE_DISKTREE:
         case STYPE_PRINTQ:
                 if (op->create_options & FILE_DELETE_ON_CLOSE)
-                        smb_ofile_set_delete_on_close(of);
+                        smb_ofile_set_delete_on_close(sr, of);
                 break;
         }
 
         /*
-         * Build the Create Context to return; first the
-         * per-element parts, then the aggregated buffer.
-         *
-         * No response for these:
-         *      CCTX_EA_BUFFER
-         *      CCTX_SD_BUFFER
-         *      CCTX_ALLOCATION_SIZE
-         *      CCTX_TIMEWARP_TOKEN
-         *
-         * We don't handle these yet.
-         *      CCTX_DH_REQUEST
-         *      CCTX_DH_RECONNECT
-         *      CCTX_REQUEST_LEASE
+         * Process any outgoing create contexts that need work
+         * after the open succeeds.  Encode happens later.
          */
         if (cctx.cc_in_flags & CCTX_QUERY_MAX_ACCESS) {
-                cce = &cctx.cc_out_max_access;
-                uint32_t MaxAccess = 0;
+                op->maximum_access = 0;
                 if (of->f_node != NULL) {
-                        smb_fsop_eaccess(sr, of->f_cr, of->f_node, &MaxAccess);
+                        smb_fsop_eaccess(sr, of->f_cr, of->f_node,
+                            &op->maximum_access);
                 }
-                MaxAccess |= of->f_granted_access;
-                cce->cce_len = 8;
-                cce->cce_mbc.max_bytes = 8;
-                (void) smb_mbc_encodef(&cce->cce_mbc,
-                    "ll", 0, MaxAccess);
+                op->maximum_access |= of->f_granted_access;
                 cctx.cc_out_flags |= CCTX_QUERY_MAX_ACCESS;
         }
+
         if ((cctx.cc_in_flags & CCTX_QUERY_ON_DISK_ID) != 0 &&
             of->f_node != NULL) {
-                cce = &cctx.cc_out_file_id;
-                fsid_t fsid;
+                op->op_fsid = SMB_NODE_FSID(of->f_node);
+                cctx.cc_out_flags |= CCTX_QUERY_ON_DISK_ID;
+        }
 
-                fsid = SMB_NODE_FSID(of->f_node);
+        if ((cctx.cc_in_flags & CCTX_AAPL_EXT) != 0) {
+                cce = &cctx.cc_out_aapl;
+                /*
+                 * smb2_aapl_crctx has a variable response depending on
+                 * what the incoming context looks like, so it does all
+                 * the work of building cc_out_aapl, including setting
+                 * cce_len, cce_mbc.max_bytes, and smb_mbc_encode.
+                 * If we see errors getting this, simply omit it from
+                 * the collection of returned create contexts.
+                 */
+                status = smb2_aapl_crctx(sr,
+                    &cctx.cc_in_aapl.cce_mbc, &cce->cce_mbc);
+                if (status == 0) {
+                        cce->cce_len = cce->cce_mbc.chain_offset;
+                        cctx.cc_out_flags |= CCTX_AAPL_EXT;
+                }
+                status = 0;
+        }
 
-                cce->cce_len = 32;
-                cce->cce_mbc.max_bytes = 32;
-                (void) smb_mbc_encodef(
-                    &cce->cce_mbc, "qll.15.",
-                    op->fileid,         /* q */
-                    fsid.val[0],        /* l */
-                    fsid.val[1]);       /* l */
-                /* reserved (16 bytes)  .15. */
-                cctx.cc_out_flags |= CCTX_QUERY_ON_DISK_ID;
+        /*
+         * If a lease was requested, and we got one...
+         */
+        if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) != 0 &&
+            op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE)
+                cctx.cc_out_flags |= CCTX_REQUEST_LEASE;
+
+        /*
+         * If a durable handle was requested and we got one...
+         */
+        if ((cctx.cc_in_flags & CCTX_DH_REQUEST) != 0 &&
+            of->dh_vers == SMB2_DURABLE_V1) {
+                cctx.cc_out_flags |= CCTX_DH_REQUEST;
         }
+        if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0 &&
+            of->dh_vers == SMB2_DURABLE_V2) {
+                cctx.cc_out_flags |= CCTX_DH_REQUEST_V2;
+        }
+
+        /*
+         * This marks the end of the "body" section and the
+         * beginning of the "encode" section.  Any errors
+         * encoding the response should use: goto errout
+         */
+cmd_done:
+        /* Want status visible in the done probe. */
+        sr->smb2_status = status;
+        DTRACE_SMB2_DONE(op__Create, smb_request_t *, sr);
+        if (status != NT_STATUS_SUCCESS)
+                goto errout;
+
+        /*
+         * Encode all the create contexts to return.
+         */
         if (cctx.cc_out_flags) {
                 sr->raw_data.max_bytes = smb2_max_trans;
-                status = smb2_encode_create_ctx(&sr->raw_data, &cctx);
+                status = smb2_encode_create_ctx(sr, &cctx);
                 if (status)
                         goto errout;
         }
 
         /*
-         * SMB2 Create reply
+         * Encode the SMB2 Create reply
          */
+        attr = &op->fqi.fq_fattr;
         rc = smb_mbc_encodef(
             &sr->reply,
             "wb.lTTTTqqllqqll",
             89, /* StructSize */        /* w */
-            OplockLevel,                /* b */
+            op->op_oplock_level,        /* b */
             op->action_taken,           /* l */
             &attr->sa_crtime,           /* T */
             &attr->sa_vattr.va_atime,   /* T */
             &attr->sa_vattr.va_mtime,   /* T */
             &attr->sa_vattr.va_ctime,   /* T */

@@ -414,29 +707,37 @@
                         goto errout;
                 }
         } else {
                 (void) smb_mbc_encodef(&sr->reply, ".");
         }
-        return (SDRC_SUCCESS);
 
-errout:
+        if (status != 0) {
+        errout:
         if (of != NULL)
                 smb_ofile_close(of, 0);
+                smb2sr_put_error(sr, status);
+        }
+        if (op->sd != NULL) {
+                smb_sd_term(op->sd);
+                kmem_free(op->sd, sizeof (*op->sd));
+        }
         if (cctx.cc_out_flags)
                 smb2_free_create_ctx(&cctx);
-        smb2sr_put_error(sr, status);
+
         return (SDRC_SUCCESS);
 }
 
 /*
  * Decode an SMB2 Create Context buffer into our internal form.
- * No policy decisions about what's supported here, just decode.
+ * Avoid policy decisions about what's supported here, just decode.
  */
 static uint32_t
-smb2_decode_create_ctx(mbuf_chain_t *in_mbc, smb2_create_ctx_t *cc)
+smb2_decode_create_ctx(smb_request_t *sr, smb2_create_ctx_t *cc)
 {
+        smb_arg_open_t *op = &sr->arg.open;
         smb2_create_ctx_elem_t *cce;
+        mbuf_chain_t *in_mbc = &cc->cc_in_mbc;
         mbuf_chain_t name_mbc;
         union {
                 uint32_t i;
                 char ch[4];
         } cc_name;

@@ -447,10 +748,15 @@
         uint16_t name_off;
         uint16_t name_len;
         int top_offset;
         int rc;
 
+        /*
+         * Any break from the loop below before we've decoded
+         * the entire create context means it was malformatted,
+         * so we should return INVALID_PARAMETER.
+         */
         status = NT_STATUS_INVALID_PARAMETER;
         for (;;) {
                 cce = NULL;
                 top_offset = in_mbc->chain_offset;
                 rc = smb_mbc_decodef(

@@ -518,10 +824,26 @@
                         break;
                 case SMB2_CREATE_REQUEST_LEASE:         /* ("RqLs") */
                         cc->cc_in_flags |= CCTX_REQUEST_LEASE;
                         cce = &cc->cc_in_req_lease;
                         break;
+                case SMB2_CREATE_CTX_AAPL:              /* ("AAPL") */
+                        cc->cc_in_flags |= CCTX_AAPL_EXT;
+                        cce = &cc->cc_in_aapl;
+                        break;
+                case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2: /* ("DH2Q") */
+                        cc->cc_in_flags |= CCTX_DH_REQUEST_V2;
+                        cce = &cc->cc_in_dh_request_v2;
+                        break;
+                case SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2: /* ("DH2C") */
+                        cc->cc_in_flags |= CCTX_DH_RECONNECT_V2;
+                        cce = &cc->cc_in_dh_reconnect_v2;
+                        break;
+                case 0x9ccbcf9e: /* SVHDX_OPEN_DEVICE_CONTEXT */
+                        /* 9ccbcf9e 04c1e643 980e158d a1f6ec83 */
+                        /* silently ignore */
+                        break;
                 default:
                         /*
                          * Unknown create context values are normal, and
                          * should be ignored.  However, in debug mode,
                          * let's log them so we know which ones we're

@@ -533,22 +855,132 @@
 #endif
                         cce = NULL;
                         break;
                 }
 
-                if (cce != NULL && data_len != 0) {
+                if (cce == NULL || data_len == 0)
+                        goto next_cc;
+
                         if ((data_off & 7) != 0)
                                 break;
                         if ((top_offset + data_off) < in_mbc->chain_offset)
                                 break;
                         rc = MBC_SHADOW_CHAIN(&cce->cce_mbc, in_mbc,
                             top_offset + data_off, data_len);
                         if (rc)
                                 break;
                         cce->cce_len = data_len;
+
+                /*
+                 * Additonal decoding for some create contexts.
+                 */
+                switch (cc_name.i) {
+                        uint64_t nttime;
+
+                case SMB2_CREATE_SD_BUFFER:             /* ("SecD") */
+                        op->sd = kmem_alloc(sizeof (smb_sd_t), KM_SLEEP);
+                        if (smb_decode_sd(&cce->cce_mbc, op->sd) != 0)
+                                goto errout;
+                        break;
+
+                case SMB2_CREATE_ALLOCATION_SIZE:       /* ("AISi") */
+                        rc = smb_mbc_decodef(&cce->cce_mbc, "q", &op->dsize);
+                        if (rc != 0)
+                                goto errout;
+                        break;
+
+                case SMB2_CREATE_TIMEWARP_TOKEN:        /* ("TWrp") */
+                        /*
+                         * Support for opening "Previous Versions".
+                         * [MS-SMB2] 2.2.13.2.7  Data is an NT time.
+                         */
+                        rc = smb_mbc_decodef(&cce->cce_mbc,
+                            "q", &nttime);
+                        if (rc != 0)
+                                goto errout;
+                        smb_time_nt_to_unix(nttime, &op->timewarp);
+                        op->create_timewarp = B_TRUE;
+                        break;
+
+                /*
+                 * Note: This handles both V1 and V2 leases,
+                 * which differ only by their length.
+                 */
+                case SMB2_CREATE_REQUEST_LEASE:         /* ("RqLs") */
+                        if (data_len == 52) {
+                                op->lease_version = 2;
+                        } else if (data_len == 32) {
+                                op->lease_version = 1;
+                        } else {
+                                cmn_err(CE_NOTE, "Cctx RqLs bad len=0x%x",
+                                    data_len);
                 }
+                        rc = smb_mbc_decodef(&cce->cce_mbc, "#cllq",
+                            UUID_LEN,                   /* # */
+                            op->lease_key,              /* c */
+                            &op->lease_state,           /* l */
+                            &op->lease_flags,           /* l */
+                            &nttime);   /* (ignored)       q */
+                        if (rc != 0)
+                                goto errout;
+                        if (op->lease_version == 2) {
+                                rc = smb_mbc_decodef(&cce->cce_mbc,
+                                    "#cw..",
+                                    UUID_LEN,
+                                    op->parent_lease_key,
+                                    &op->lease_epoch);
+                                if (rc != 0)
+                                        goto errout;
+                        } else {
+                                bzero(op->parent_lease_key, UUID_LEN);
+                        }
+                        break;
 
+                case SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2: /* ("DH2C") */
+                        rc = smb_mbc_decodef(&cce->cce_mbc, "qq#cl",
+                            &op->dh_fileid.persistent,  /* q */
+                            &op->dh_fileid.temporal,    /* q */
+                            UUID_LEN,                   /* # */
+                            op->create_guid,            /* c */
+                            &op->dh_v2_flags);          /* l */
+                        if (rc != 0)
+                                goto errout;
+                        break;
+
+                case SMB2_CREATE_DURABLE_HANDLE_RECONNECT: /* ("DHnC") */
+                        rc = smb_mbc_decodef(&cce->cce_mbc, "qq",
+                            &op->dh_fileid.persistent, /* q */
+                            &op->dh_fileid.temporal); /* q */
+                        if (rc != 0)
+                                goto errout;
+                        bzero(op->create_guid, UUID_LEN);
+                        op->dh_v2_flags = 0;
+                        break;
+
+                case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2: /* ("DH2Q") */
+                        rc = smb_mbc_decodef(&cce->cce_mbc,
+                            "ll8.#c",
+                            &op->dh_timeout,    /* l */
+                            &op->dh_v2_flags,   /* l */
+                            /* reserved */      /* 8. */
+                            UUID_LEN, /* # */
+                            op->create_guid); /* c */
+                        if (rc != 0)
+                                goto errout;
+                        break;
+
+                case SMB2_CREATE_DURABLE_HANDLE_REQUEST: /* ("DHnQ") */
+                        rc = smb_mbc_decodef(&cce->cce_mbc,
+                            "16."); /* reserved */
+                        if (rc != 0)
+                                goto errout;
+                        op->dh_timeout = 0;     /* default */
+                        op->dh_v2_flags = 0;
+                        break;
+                }
+
+        next_cc:
                 if (next_off == 0) {
                         /* Normal loop termination */
                         status = 0;
                         break;
                 }

@@ -560,26 +992,45 @@
                 if ((top_offset + next_off) > in_mbc->max_bytes)
                         break;
                 in_mbc->chain_offset = top_offset + next_off;
         }
 
+errout:
         return (status);
 }
 
 /*
  * Encode an SMB2 Create Context buffer from our internal form.
+ *
+ * Build the Create Context to return; first the
+ * per-element parts, then the aggregated buffer.
+ *
+ * No response for these:
+ *      CCTX_EA_BUFFER
+ *      CCTX_SD_BUFFER
+ *      CCTX_ALLOCATION_SIZE
+ *      CCTX_TIMEWARP_TOKEN
+ *
+ * Remember to add code sections to smb2_free_create_ctx()
+ * for each section here that encodes a context element.
  */
-/* ARGSUSED */
 static uint32_t
-smb2_encode_create_ctx(mbuf_chain_t *mbc, smb2_create_ctx_t *cc)
+smb2_encode_create_ctx(smb_request_t *sr, smb2_create_ctx_t *cc)
 {
+        smb_arg_open_t *op = &sr->arg.open;
         smb2_create_ctx_elem_t *cce;
+        mbuf_chain_t *mbc = &sr->raw_data;
         int last_top = -1;
         int rc;
 
         if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
                 cce = &cc->cc_out_max_access;
+
+                cce->cce_mbc.max_bytes = cce->cce_len = 8;
+                (void) smb_mbc_encodef(&cce->cce_mbc,
+                    "ll", 0, op->maximum_access);
+
                 last_top = mbc->chain_offset;
                 rc = smb2_encode_create_ctx_elem(mbc, cce,
                     SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ);
                 if (rc)
                         return (NT_STATUS_INTERNAL_ERROR);

@@ -587,19 +1038,100 @@
                     mbc->chain_offset - last_top);
         }
 
         if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
                 cce = &cc->cc_out_file_id;
+
+                cce->cce_mbc.max_bytes = cce->cce_len = 32;
+                (void) smb_mbc_encodef(
+                    &cce->cce_mbc, "qll.15.",
+                    op->fileid,                 /* q */
+                    op->op_fsid.val[0],         /* l */
+                    op->op_fsid.val[1]);        /* l */
+                    /* reserved (16 bytes)      .15. */
+
                 last_top = mbc->chain_offset;
                 rc = smb2_encode_create_ctx_elem(mbc, cce,
                     SMB2_CREATE_QUERY_ON_DISK_ID);
                 if (rc)
                         return (NT_STATUS_INTERNAL_ERROR);
                 (void) smb_mbc_poke(mbc, last_top, "l",
                     mbc->chain_offset - last_top);
         }
 
+        if (cc->cc_out_flags & CCTX_AAPL_EXT) {
+                cce = &cc->cc_out_aapl;
+                /* cc_out_aapl already encoded */
+
+                last_top = mbc->chain_offset;
+                rc = smb2_encode_create_ctx_elem(mbc, cce,
+                    SMB2_CREATE_CTX_AAPL);
+                if (rc)
+                        return (NT_STATUS_INTERNAL_ERROR);
+                (void) smb_mbc_poke(mbc, last_top, "l",
+                    mbc->chain_offset - last_top);
+        }
+
+        if (cc->cc_out_flags & CCTX_REQUEST_LEASE) {
+                cce = &cc->cc_out_req_lease;
+
+                cce->cce_mbc.max_bytes = cce->cce_len = 32;
+                (void) smb_mbc_encodef(&cce->cce_mbc, "#cllq",
+                    UUID_LEN,                   /* # */
+                    op->lease_key,              /* c */
+                    op->lease_state,            /* l */
+                    op->lease_flags,            /* l */
+                    0LL);                       /* q */
+                if (op->lease_version == 2) {
+                        cce->cce_mbc.max_bytes = cce->cce_len = 52;
+                        (void) smb_mbc_encodef(&cce->cce_mbc,
+                            "#cw..",
+                            UUID_LEN,
+                            op->parent_lease_key,
+                            op->lease_epoch);
+                }
+
+                last_top = mbc->chain_offset;
+                rc = smb2_encode_create_ctx_elem(mbc, cce,
+                    SMB2_CREATE_REQUEST_LEASE);
+                if (rc)
+                        return (NT_STATUS_INTERNAL_ERROR);
+                (void) smb_mbc_poke(mbc, last_top, "l",
+                    mbc->chain_offset - last_top);
+        }
+
+        if (cc->cc_out_flags & CCTX_DH_REQUEST) {
+                cce = &cc->cc_out_dh_request;
+
+                cce->cce_mbc.max_bytes = cce->cce_len = 8;
+                (void) smb_mbc_encodef(&cce->cce_mbc, "q", 0LL);
+
+                last_top = mbc->chain_offset;
+                rc = smb2_encode_create_ctx_elem(mbc, cce,
+                    SMB2_CREATE_DURABLE_HANDLE_REQUEST);
+                if (rc)
+                        return (NT_STATUS_INTERNAL_ERROR);
+                (void) smb_mbc_poke(mbc, last_top, "l",
+                    mbc->chain_offset - last_top);
+        }
+
+        if (cc->cc_out_flags & CCTX_DH_REQUEST_V2) {
+                cce = &cc->cc_out_dh_request_v2;
+
+                cce->cce_mbc.max_bytes = cce->cce_len = 8;
+                (void) smb_mbc_encodef(&cce->cce_mbc, "ll",
+                    op->dh_timeout, op->dh_v2_flags);
+
+                last_top = mbc->chain_offset;
+                rc = smb2_encode_create_ctx_elem(mbc, cce,
+                    SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2);
+                if (rc)
+                        return (NT_STATUS_INTERNAL_ERROR);
+                (void) smb_mbc_poke(mbc, last_top, "l",
+                    mbc->chain_offset - last_top);
+        }
+
         if (last_top >= 0)
                 (void) smb_mbc_poke(mbc, last_top, "l", 0);
 
         return (0);
 }

@@ -624,10 +1156,11 @@
          * name is a fixed length, so this easy.
          * The final layout looks like this:
          *      a: this header (16 bytes)
          *      b: the name (4 bytes, 4 pad)
          *      c: the payload (variable)
+         *      d: padding (to align 8)
          *
          * Note that "Next elem." is filled in later.
          */
         rc = smb_mbc_encodef(
             out_mbc, "lwwwwl",

@@ -634,11 +1167,11 @@
             0,          /* Next offset  l */
             16,         /* NameOffset   w */
             4,          /* NameLength   w */
             0,          /* Reserved     w */
             24,         /* DataOffset   w */
-            cce->cce_len);      /*      l */
+            cce->cce_len); /* DataLen   l */
         if (rc)
                 return (rc);
 
         /*
          * Now the "name" and payload.

@@ -647,10 +1180,12 @@
             out_mbc, "4c4.#C",
             cc_name.ch,         /* 4c4. */
             cce->cce_len,       /* # */
             &cce->cce_mbc);     /* C */
 
+        (void) smb_mbc_put_align(out_mbc, 8);
+
         return (rc);
 }
 
 static void
 smb2_free_create_ctx(smb2_create_ctx_t *cc)

@@ -663,6 +1198,22 @@
         }
         if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
                 cce = &cc->cc_out_file_id;
                 MBC_FLUSH(&cce->cce_mbc);
         }
+        if (cc->cc_out_flags & CCTX_AAPL_EXT) {
+                cce = &cc->cc_out_aapl;
+                MBC_FLUSH(&cce->cce_mbc);
+        }
+        if (cc->cc_out_flags & CCTX_REQUEST_LEASE) {
+                cce = &cc->cc_out_req_lease;
+                MBC_FLUSH(&cce->cce_mbc);
+        }
+        if (cc->cc_out_flags & CCTX_DH_REQUEST) {
+                cce = &cc->cc_out_dh_request;
+                MBC_FLUSH(&cce->cce_mbc);
+        }
+        if (cc->cc_out_flags & CCTX_DH_REQUEST_V2) {
+                cce = &cc->cc_out_dh_request_v2;
+                MBC_FLUSH(&cce->cce_mbc);
+        }
 }