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); + } }