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,18 ****
* 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.
*/
/*
* Dispatch function for SMB2_CREATE
* [MS-SMB2] 2.2.13
--- 8,18 ----
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
! * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Dispatch function for SMB2_CREATE
* [MS-SMB2] 2.2.13
*** 19,29 ****
--- 19,48 ----
*/
#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,49 ****
#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
-
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 {
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;
--- 51,71 ----
#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,117 ****
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;
/* 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_t;
static uint32_t smb2_decode_create_ctx(
! mbuf_chain_t *, smb2_create_ctx_t *);
static uint32_t smb2_encode_create_ctx(
! mbuf_chain_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 *);
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;
uint32_t status;
int skip;
int rc = 0;
bzero(&cctx, sizeof (cctx));
! bzero(&cc_mbc, sizeof (cc_mbc));
/*
* 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
*/
rc = smb_mbc_decodef(
&sr->smb_data, "wbblqqlllllwwll",
&StructSize, /* w */
&SecurityFlags, /* b */
! &OplockLevel, /* b */
&ImpersonationLevel, /* l */
&SmbCreateFlags, /* q */
&Reserved4, /* q */
&op->desired_access, /* l */
&op->dattr, /* l */
--- 72,153 ----
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(
! smb_request_t *, smb2_create_ctx_t *);
static uint32_t smb2_encode_create_ctx(
! 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;
smb_arg_open_t *op = &sr->arg.open;
smb_ofile_t *of = NULL;
uint16_t StructSize;
uint8_t SecurityFlags;
uint32_t ImpersonationLevel;
uint64_t SmbCreateFlags;
uint64_t Reserved4;
uint16_t NameOffset;
uint16_t NameLength;
uint32_t CreateCtxOffset;
uint32_t CreateCtxLength;
! smb2fid_t smb2fid = { 0, 0 };
uint32_t status;
+ int dh_flags;
int skip;
int rc = 0;
bzero(&cctx, sizeof (cctx));
! 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_release(sr->fid_ofile);
sr->fid_ofile = NULL;
}
/*
! * 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 */
! &op->op_oplock_level, /* b */
&ImpersonationLevel, /* l */
&SmbCreateFlags, /* q */
&Reserved4, /* q */
&op->desired_access, /* l */
&op->dattr, /* l */
*** 129,147 ****
* 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)
(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
/*
* Get the path name
*/
if (NameLength >= SMB_MAXPATHLEN) {
status = NT_STATUS_OBJECT_PATH_INVALID;
goto errout;
}
--- 165,185 ----
* 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)
! 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,208 ****
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
--- 193,203 ----
*** 214,384 ****
sr->smb_data.chain_offset) {
status = NT_STATUS_INVALID_PARAMETER;
goto errout;
}
! rc = MBC_SHADOW_CHAIN(&cc_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);
if (status)
goto errout;
! if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
! status = NT_STATUS_EAS_NOT_SUPPORTED;
! goto errout;
}
! 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;
}
! 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) {
status = NT_STATUS_INVALID_PARAMETER;
! goto errout;
}
- }
/*
! * Support for opening "Previous Versions".
! * [MS-SMB2] 2.2.13.2.7 Data is an NT time.
*/
! 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;
}
}
/*
! * The real open call. Note: this gets attributes into
! * op->fqi.fq_fattr (SMB_AT_ALL). We need those below.
*/
- 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.
*/
switch (op->op_oplock_level) {
! default:
! case SMB_OPLOCK_NONE:
! OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
break;
! case SMB_OPLOCK_LEVEL_II:
! OplockLevel = SMB2_OPLOCK_LEVEL_II;
break;
! case SMB_OPLOCK_EXCLUSIVE:
! OplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
break;
! case SMB_OPLOCK_BATCH:
! OplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
break;
}
/*
* 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;
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);
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
*/
if (cctx.cc_in_flags & CCTX_QUERY_MAX_ACCESS) {
! cce = &cctx.cc_out_max_access;
! uint32_t MaxAccess = 0;
if (of->f_node != NULL) {
! smb_fsop_eaccess(sr, of->f_cr, of->f_node, &MaxAccess);
}
! 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);
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;
! fsid = SMB_NODE_FSID(of->f_node);
! 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 (cctx.cc_out_flags) {
sr->raw_data.max_bytes = smb2_max_trans;
! status = smb2_encode_create_ctx(&sr->raw_data, &cctx);
if (status)
goto errout;
}
/*
! * SMB2 Create reply
*/
rc = smb_mbc_encodef(
&sr->reply,
"wb.lTTTTqqllqqll",
89, /* StructSize */ /* w */
! OplockLevel, /* 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 */
--- 209,677 ----
sr->smb_data.chain_offset) {
status = NT_STATUS_INVALID_PARAMETER;
goto errout;
}
! 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(sr, &cctx);
if (status)
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);
}
! /*
! * 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);
}
! /*
! * 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 cmd_done;
}
/*
! * 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.
*/
! 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;
+ }
+
/*
! * Real create (of a new handle, not reconnect)
*/
/*
! * Validate the requested oplock level.
! * Conversion to internal form is in smb2_oplock_acquire()
*/
switch (op->op_oplock_level) {
! 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 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;
!
! default:
! /*
! * Invalid lease state flags
! * Just force to "none".
! */
! op->lease_state = SMB2_LEASE_NONE;
break;
! }
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.
*/
! 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(sr, of);
break;
}
/*
! * Process any outgoing create contexts that need work
! * after the open succeeds. Encode happens later.
*/
if (cctx.cc_in_flags & CCTX_QUERY_MAX_ACCESS) {
! op->maximum_access = 0;
if (of->f_node != NULL) {
! smb_fsop_eaccess(sr, of->f_cr, of->f_node,
! &op->maximum_access);
}
! 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) {
! op->op_fsid = SMB_NODE_FSID(of->f_node);
! cctx.cc_out_flags |= CCTX_QUERY_ON_DISK_ID;
! }
! 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;
! }
! /*
! * 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, &cctx);
if (status)
goto errout;
}
/*
! * Encode the SMB2 Create reply
*/
+ attr = &op->fqi.fq_fattr;
rc = smb_mbc_encodef(
&sr->reply,
"wb.lTTTTqqllqqll",
89, /* StructSize */ /* w */
! 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,442 ****
goto errout;
}
} else {
(void) smb_mbc_encodef(&sr->reply, ".");
}
- return (SDRC_SUCCESS);
! errout:
if (of != NULL)
smb_ofile_close(of, 0);
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.
*/
static uint32_t
! smb2_decode_create_ctx(mbuf_chain_t *in_mbc, smb2_create_ctx_t *cc)
{
smb2_create_ctx_elem_t *cce;
mbuf_chain_t name_mbc;
union {
uint32_t i;
char ch[4];
} cc_name;
--- 707,743 ----
goto errout;
}
} else {
(void) smb_mbc_encodef(&sr->reply, ".");
}
! 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);
!
return (SDRC_SUCCESS);
}
/*
* Decode an SMB2 Create Context buffer into our internal form.
! * Avoid policy decisions about what's supported here, just decode.
*/
static uint32_t
! 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,456 ****
--- 748,762 ----
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,527 ****
--- 824,849 ----
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,554 ****
#endif
cce = NULL;
break;
}
! if (cce != NULL && data_len != 0) {
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;
}
if (next_off == 0) {
/* Normal loop termination */
status = 0;
break;
}
--- 855,986 ----
#endif
cce = NULL;
break;
}
! 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,585 ****
if ((top_offset + next_off) > in_mbc->max_bytes)
break;
in_mbc->chain_offset = top_offset + next_off;
}
return (status);
}
/*
* Encode an SMB2 Create Context buffer from our internal form.
*/
- /* ARGSUSED */
static uint32_t
! smb2_encode_create_ctx(mbuf_chain_t *mbc, smb2_create_ctx_t *cc)
{
smb2_create_ctx_elem_t *cce;
int last_top = -1;
int rc;
if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
cce = &cc->cc_out_max_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);
--- 992,1036 ----
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.
*/
static uint32_t
! 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,605 ****
--- 1038,1137 ----
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,633 ****
--- 1156,1166 ----
* 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,644 ****
0, /* Next offset l */
16, /* NameOffset w */
4, /* NameLength w */
0, /* Reserved w */
24, /* DataOffset w */
! cce->cce_len); /* l */
if (rc)
return (rc);
/*
* Now the "name" and payload.
--- 1167,1177 ----
0, /* Next offset l */
16, /* NameOffset w */
4, /* NameLength w */
0, /* Reserved w */
24, /* DataOffset w */
! cce->cce_len); /* DataLen l */
if (rc)
return (rc);
/*
* Now the "name" and payload.
*** 647,656 ****
--- 1180,1191 ----
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,668 ****
--- 1198,1219 ----
}
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);
+ }
}