#include <smbsrv/smb2_kproto.h>
#include <smbsrv/smb_fsops.h>
#define DH_PERSISTENT SMB2_DHANDLE_FLAG_PERSISTENT
#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
#define CCTX_EA_BUFFER 1
#define CCTX_SD_BUFFER 2
#define CCTX_DH_REQUEST 4
#define CCTX_DH_RECONNECT 8
#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;
uint_t cc_out_flags;
smb2_create_ctx_elem_t cc_in_ext_attr;
smb2_create_ctx_elem_t cc_in_sec_desc;
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;
smb2_create_ctx_elem_t cc_in_max_access;
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;
if (sr->fid_ofile != NULL) {
smb_ofile_release(sr->fid_ofile);
sr->fid_ofile = NULL;
}
rc = smb_mbc_decodef(
&sr->smb_data, "wbblqqlllllwwll",
&StructSize,
&SecurityFlags,
&op->op_oplock_level,
&ImpersonationLevel,
&SmbCreateFlags,
&Reserved4,
&op->desired_access,
&op->dattr,
&op->share_access,
&op->create_disposition,
&op->create_options,
&NameOffset,
&NameLength,
&CreateCtxOffset,
&CreateCtxLength);
if (rc != 0 || StructSize != 57)
return (SDRC_ERROR);
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);
if (NameLength >= SMB_MAXPATHLEN) {
status = NT_STATUS_OBJECT_PATH_INVALID;
goto errout;
}
if (NameLength == 0) {
op->fqi.fq_path.pn_path = "";
} else {
rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
NameLength, &op->fqi.fq_path.pn_path);
if (rc) {
status = NT_STATUS_OBJECT_PATH_INVALID;
goto errout;
}
if (op->fqi.fq_path.pn_path[0] == '\\') {
status = NT_STATUS_INVALID_PARAMETER;
goto errout;
}
}
op->fqi.fq_dnode = sr->tid_tree->t_snode;
if (CreateCtxLength != 0) {
if ((CreateCtxOffset & 7) != 0 ||
(CreateCtxOffset + sr->smb2_cmd_hdr) <
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;
}
DTRACE_SMB2_START(op__Create, smb_request_t *, sr);
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 (sr->session->dialect < SMB_VERS_3_0) {
cctx.cc_in_flags &=
~(CCTX_DH_REQUEST_V2|CCTX_DH_RECONNECT_V2);
}
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;
}
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;
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);
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;
of = sr->fid_ofile;
smb2_oplock_reconnect(sr);
goto reconnect_done;
}
switch (op->op_oplock_level) {
case SMB2_OPLOCK_LEVEL_NONE:
case SMB2_OPLOCK_LEVEL_II:
case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
case SMB2_OPLOCK_LEVEL_BATCH:
cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
break;
case SMB2_OPLOCK_LEVEL_LEASE:
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;
}
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:
op->lease_state = SMB2_LEASE_NONE;
break;
}
break;
default:
status = NT_STATUS_INVALID_PARAMETER;
goto cmd_done;
}
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;
}
switch (ImpersonationLevel) {
case SMB2_IMPERSONATION_ANONYMOUS:
case SMB2_IMPERSONATION_IDENTIFICATION:
case SMB2_IMPERSONATION_IMPERSONATION:
case SMB2_IMPERSONATION_DELEGATE:
break;
default:
status = NT_STATUS_BAD_IMPERSONATION_LEVEL;
goto cmd_done;
}
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;
}
status = smb_common_open(sr);
if (status != NT_STATUS_SUCCESS)
goto cmd_done;
of = sr->fid_ofile;
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);
}
if (of->f_ftype != SMB_FTYPE_DISK ||
!smb_node_is_file(of->f_node)) {
op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
}
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);
}
if ((cctx.cc_in_flags &
(CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0 &&
smb_dh_create_allowed(sr, of)) {
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;
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;
}
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;
}
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;
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 ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) != 0 &&
op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE)
cctx.cc_out_flags |= CCTX_REQUEST_LEASE;
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;
}
cmd_done:
sr->smb2_status = status;
DTRACE_SMB2_DONE(op__Create, smb_request_t *, sr);
if (status != NT_STATUS_SUCCESS)
goto errout;
if (cctx.cc_out_flags) {
sr->raw_data.max_bytes = smb2_max_trans;
status = smb2_encode_create_ctx(sr, &cctx);
if (status)
goto errout;
}
attr = &op->fqi.fq_fattr;
rc = smb_mbc_encodef(
&sr->reply,
"wb.lTTTTqqllqqll",
89,
op->op_oplock_level,
op->action_taken,
&attr->sa_crtime,
&attr->sa_vattr.va_atime,
&attr->sa_vattr.va_mtime,
&attr->sa_vattr.va_ctime,
attr->sa_allocsz,
attr->sa_vattr.va_size,
attr->sa_dosattr,
0,
smb2fid.persistent,
smb2fid.temporal,
0,
0);
if (rc != 0) {
status = NT_STATUS_UNSUCCESSFUL;
goto errout;
}
CreateCtxOffset = sr->reply.chain_offset - sr->smb2_reply_hdr;
CreateCtxLength = MBC_LENGTH(&sr->raw_data);
if (CreateCtxLength != 0) {
sr->reply.chain_offset -= 8;
rc = smb_mbc_encodef(
&sr->reply,
"ll#C",
CreateCtxOffset,
CreateCtxLength,
CreateCtxLength,
&sr->raw_data);
if (rc != 0) {
status = NT_STATUS_UNSUCCESSFUL;
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);
}
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;
uint32_t status;
int32_t next_off;
uint32_t data_len;
uint16_t data_off;
uint16_t name_off;
uint16_t name_len;
int top_offset;
int rc;
status = NT_STATUS_INVALID_PARAMETER;
for (;;) {
cce = NULL;
top_offset = in_mbc->chain_offset;
rc = smb_mbc_decodef(
in_mbc,
"lww..wl",
&next_off,
&name_off,
&name_len,
&data_off,
&data_len);
if (rc)
break;
if ((top_offset + name_off) < in_mbc->chain_offset)
break;
rc = MBC_SHADOW_CHAIN(&name_mbc, in_mbc,
top_offset + name_off, name_len);
if (rc)
break;
rc = smb_mbc_decodef(&name_mbc, "4c", &cc_name);
if (rc)
break;
cc_name.i = ntohl(cc_name.i);
switch (cc_name.i) {
case SMB2_CREATE_EA_BUFFER:
cc->cc_in_flags |= CCTX_EA_BUFFER;
cce = &cc->cc_in_ext_attr;
break;
case SMB2_CREATE_SD_BUFFER:
cc->cc_in_flags |= CCTX_SD_BUFFER;
cce = &cc->cc_in_sec_desc;
break;
case SMB2_CREATE_DURABLE_HANDLE_REQUEST:
cc->cc_in_flags |= CCTX_DH_REQUEST;
cce = &cc->cc_in_dh_request;
break;
case SMB2_CREATE_DURABLE_HANDLE_RECONNECT:
cc->cc_in_flags |= CCTX_DH_RECONNECT;
cce = &cc->cc_in_dh_reconnect;
break;
case SMB2_CREATE_ALLOCATION_SIZE:
cc->cc_in_flags |= CCTX_ALLOCATION_SIZE;
cce = &cc->cc_in_alloc_size;
break;
case SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ:
cc->cc_in_flags |= CCTX_QUERY_MAX_ACCESS;
cce = &cc->cc_in_max_access;
break;
case SMB2_CREATE_TIMEWARP_TOKEN:
cc->cc_in_flags |= CCTX_TIMEWARP_TOKEN;
cce = &cc->cc_in_time_warp;
break;
case SMB2_CREATE_QUERY_ON_DISK_ID:
cc->cc_in_flags |= CCTX_QUERY_ON_DISK_ID;
break;
case SMB2_CREATE_REQUEST_LEASE:
cc->cc_in_flags |= CCTX_REQUEST_LEASE;
cce = &cc->cc_in_req_lease;
break;
case SMB2_CREATE_CTX_AAPL:
cc->cc_in_flags |= CCTX_AAPL_EXT;
cce = &cc->cc_in_aapl;
break;
case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2:
cc->cc_in_flags |= CCTX_DH_REQUEST_V2;
cce = &cc->cc_in_dh_request_v2;
break;
case SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2:
cc->cc_in_flags |= CCTX_DH_RECONNECT_V2;
cce = &cc->cc_in_dh_reconnect_v2;
break;
case 0x45bca66a:
case 0xB982D0B7:
case 0x9ccbcf9e:
cce = NULL;
break;
default:
#ifdef DEBUG
cmn_err(CE_NOTE, "unknown create context ID 0x%x",
cc_name.i);
#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;
switch (cc_name.i) {
uint64_t nttime;
case SMB2_CREATE_SD_BUFFER:
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:
rc = smb_mbc_decodef(&cce->cce_mbc, "q", &op->dsize);
if (rc != 0)
goto errout;
break;
case SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ:
rc = smb_mbc_decodef(&cce->cce_mbc, "q", &nttime);
if (rc != 0)
goto errout;
break;
case SMB2_CREATE_TIMEWARP_TOKEN:
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;
case SMB2_CREATE_REQUEST_LEASE:
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,
&op->lease_state,
&op->lease_flags,
&nttime);
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:
rc = smb_mbc_decodef(&cce->cce_mbc, "qq#cl",
&op->dh_fileid.persistent,
&op->dh_fileid.temporal,
UUID_LEN,
op->create_guid,
&op->dh_v2_flags);
if (rc != 0)
goto errout;
break;
case SMB2_CREATE_DURABLE_HANDLE_RECONNECT:
rc = smb_mbc_decodef(&cce->cce_mbc, "qq",
&op->dh_fileid.persistent,
&op->dh_fileid.temporal);
if (rc != 0)
goto errout;
bzero(op->create_guid, UUID_LEN);
op->dh_v2_flags = 0;
break;
case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2:
rc = smb_mbc_decodef(&cce->cce_mbc,
"ll8.#c",
&op->dh_timeout,
&op->dh_v2_flags,
UUID_LEN,
op->create_guid);
if (rc != 0)
goto errout;
break;
case SMB2_CREATE_DURABLE_HANDLE_REQUEST:
rc = smb_mbc_decodef(&cce->cce_mbc,
"16.");
if (rc != 0)
goto errout;
op->dh_timeout = 0;
op->dh_v2_flags = 0;
break;
}
next_cc:
if (next_off == 0) {
status = 0;
break;
}
if ((next_off & 7) != 0)
break;
if ((top_offset + next_off) < in_mbc->chain_offset)
break;
if ((top_offset + next_off) > in_mbc->max_bytes)
break;
in_mbc->chain_offset = top_offset + next_off;
}
errout:
return (status);
}
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);
(void) smb_mbc_poke(mbc, last_top, "l",
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,
op->op_fsid.val[0],
op->op_fsid.val[1]);
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;
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,
op->lease_state,
op->lease_flags,
0LL);
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);
}
static int
smb2_encode_create_ctx_elem(mbuf_chain_t *out_mbc,
smb2_create_ctx_elem_t *cce, uint32_t id)
{
union {
uint32_t i;
char ch[4];
} cc_name;
int rc;
cc_name.i = htonl(id);
rc = smb_mbc_encodef(
out_mbc, "lwwwwl",
0,
16,
4,
0,
24,
cce->cce_len);
if (rc)
return (rc);
rc = smb_mbc_encodef(
out_mbc, "4c4.#C",
cc_name.ch,
cce->cce_len,
&cce->cce_mbc);
(void) smb_mbc_put_align(out_mbc, 8);
return (rc);
}
static void
smb2_free_create_ctx(smb2_create_ctx_t *cc)
{
smb2_create_ctx_elem_t *cce;
if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
cce = &cc->cc_out_max_access;
MBC_FLUSH(&cce->cce_mbc);
}
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);
}
}