Print this page
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-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-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-5560 smb2 should use 64-bit server-global uids
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-1892 30 sec. delay responding to smb2_create
NEX-1734 SMB2 oplock break request missing a flag
SMB-122 smbd core dumps in smbd_dc_update / smb_log
SMB-117 Win7 fails to open security properties
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,152 **** * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * Dispatch function for SMB2_OPLOCK_BREAK */ #include <smbsrv/smb2_kproto.h> /* * SMB2 Oplock Break Acknowledgement ! * [MS-SMB2 2.2.24] */ smb_sdrc_t smb2_oplock_break_ack(smb_request_t *sr) { ! smb_node_t *node; smb2fid_t smb2fid; uint32_t status; ! uint16_t StructSize; ! uint8_t OplockLevel; ! uint8_t brk; int rc = 0; /* ! * Decode the SMB2 Oplock Break Ack. */ rc = smb_mbc_decodef( ! &sr->smb_data, "wb5.qq", ! &StructSize, /* w */ ! &OplockLevel, /* b */ /* reserved 5. */ &smb2fid.persistent, /* q */ &smb2fid.temporal); /* q */ ! if (rc || StructSize != 24) return (SDRC_ERROR); status = smb2sr_lookup_fid(sr, &smb2fid); ! if (status) goto errout; - if ((node = sr->fid_ofile->f_node) == NULL) { - /* Not a regular file */ - status = NT_STATUS_INVALID_PARAMETER; - goto errout; - } /* ! * Process the oplock break ack. We only expect levels ! * at or below the hightest break levels we send, which is ! * currently SMB2_OPLOCK_LEVEL_II. */ ! switch (OplockLevel) { case SMB2_OPLOCK_LEVEL_NONE: /* 0x00 */ ! brk = SMB_OPLOCK_BREAK_TO_NONE; break; - case SMB2_OPLOCK_LEVEL_II: /* 0x01 */ ! brk = SMB_OPLOCK_BREAK_TO_LEVEL_II; break; - - /* We don't break to these levels (yet). */ case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */ case SMB2_OPLOCK_LEVEL_BATCH: /* 0x09 */ case SMB2_OPLOCK_LEVEL_LEASE: /* 0xFF */ ! default: /* gcc -Wuninitialized */ ! status = NT_STATUS_INVALID_PARAMETER; goto errout; } ! smb_oplock_ack(node, sr->fid_ofile, brk); /* ! * Generate SMB2 Oplock Break response ! * [MS-SMB2] 2.2.25 */ - StructSize = 24; (void) smb_mbc_encodef( &sr->reply, "wb5.qq", ! StructSize, /* w */ ! OplockLevel, /* b */ /* reserved 5. */ smb2fid.persistent, /* q */ smb2fid.temporal); /* q */ - return (SDRC_SUCCESS); - errout: - smb2sr_put_error(sr, status); return (SDRC_SUCCESS); } /* * Compose an SMB2 Oplock Break Notification packet, including * the SMB2 header and everything, in sr->reply. * The caller will send it and free the request. */ void ! smb2_oplock_break_notification(smb_request_t *sr, uint8_t brk) { smb_ofile_t *ofile = sr->fid_ofile; smb2fid_t smb2fid; uint16_t StructSize; uint8_t OplockLevel; ! switch (brk) { default: ASSERT(0); /* FALLTHROUGH */ ! case SMB_OPLOCK_BREAK_TO_NONE: OplockLevel = SMB2_OPLOCK_LEVEL_NONE; break; ! case SMB_OPLOCK_BREAK_TO_LEVEL_II: OplockLevel = SMB2_OPLOCK_LEVEL_II; break; } /* * SMB2 Header */ sr->smb2_cmd_code = SMB2_OPLOCK_BREAK; sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; ! sr->smb_tid = ofile->f_tree->t_tid; sr->smb_pid = 0; ! sr->smb_uid = 0; sr->smb2_messageid = UINT64_MAX; (void) smb2_encode_header(sr, B_FALSE); /* * SMB2 Oplock Break, variable part */ StructSize = 24; ! smb2fid.persistent = 0; smb2fid.temporal = ofile->f_fid; (void) smb_mbc_encodef( &sr->reply, "wb5.qq", StructSize, /* w */ OplockLevel, /* b */ /* reserved 5. */ smb2fid.persistent, /* q */ smb2fid.temporal); /* q */ } --- 8,344 ---- * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Dispatch function for SMB2_OPLOCK_BREAK */ #include <smbsrv/smb2_kproto.h> + #define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE) + + /* StructSize for the two "break" message formats. */ + #define SSZ_OPLOCK 24 + #define SSZ_LEASE 36 + /* * SMB2 Oplock Break Acknowledgement ! * [MS-SMB2] 3.3.5.22.1 Processing an Oplock Acknowledgment ! * Called via smb2_disp_table[] */ smb_sdrc_t smb2_oplock_break_ack(smb_request_t *sr) { ! smb_ofile_t *ofile; smb2fid_t smb2fid; uint32_t status; ! uint32_t NewLevel; ! uint8_t smbOplockLevel; int rc = 0; + uint16_t StructSize; /* ! * Decode the SMB2 Oplock Break Ack (24 bytes) or ! * Lease Break Ack (36 bytes), starting with just ! * the StructSize, which tells us what this is. */ + rc = smb_mbc_decodef(&sr->smb_data, "w", &StructSize); + if (rc != 0) + return (SDRC_ERROR); + + if (StructSize == SSZ_LEASE) { + /* See smb2_lease.c */ + return (smb2_lease_break_ack(sr)); + } + if (StructSize != SSZ_OPLOCK) + return (SDRC_ERROR); + + /* + * Decode an SMB2 Oplock Break Ack. + * [MS-SMB2] 2.2.24.1 + * Note: Struct size decoded above. + */ rc = smb_mbc_decodef( ! &sr->smb_data, "b5.qq", ! &smbOplockLevel, /* b */ /* reserved 5. */ &smb2fid.persistent, /* q */ &smb2fid.temporal); /* q */ ! if (rc != 0) return (SDRC_ERROR); + /* Find the ofile */ status = smb2sr_lookup_fid(sr, &smb2fid); ! /* Success or NT_STATUS_FILE_CLOSED */ ! ! DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr); ! if (status != 0) goto errout; /* ! * Process an (old-style) oplock break ack. */ ! switch (smbOplockLevel) { case SMB2_OPLOCK_LEVEL_NONE: /* 0x00 */ ! NewLevel = OPLOCK_LEVEL_NONE; break; case SMB2_OPLOCK_LEVEL_II: /* 0x01 */ ! NewLevel = OPLOCK_LEVEL_TWO; break; case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */ + NewLevel = OPLOCK_LEVEL_ONE; + break; case SMB2_OPLOCK_LEVEL_BATCH: /* 0x09 */ + NewLevel = OPLOCK_LEVEL_BATCH; + break; case SMB2_OPLOCK_LEVEL_LEASE: /* 0xFF */ ! default: ! NewLevel = OPLOCK_LEVEL_NONE; ! break; ! } ! ! ofile = sr->fid_ofile; ! ofile->f_oplock.og_breaking = 0; ! status = smb_oplock_ack_break(sr, ofile, &NewLevel); ! if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { ! status = smb2sr_go_async(sr); ! if (status != 0) goto errout; + (void) smb_oplock_wait_break(ofile->f_node, 0); + status = 0; } + if (status != 0) { + NewLevel = OPLOCK_LEVEL_NONE; + goto errout; + } ! ofile->f_oplock.og_state = NewLevel; ! switch (NewLevel & OPLOCK_LEVEL_TYPE_MASK) { ! case OPLOCK_LEVEL_NONE: ! smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE; ! break; ! case OPLOCK_LEVEL_TWO: ! smbOplockLevel = SMB2_OPLOCK_LEVEL_II; ! break; ! case OPLOCK_LEVEL_ONE: ! smbOplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE; ! break; ! case OPLOCK_LEVEL_BATCH: ! smbOplockLevel = SMB2_OPLOCK_LEVEL_BATCH; ! break; ! case OPLOCK_LEVEL_GRANULAR: ! default: ! smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE; ! break; ! } + errout: + sr->smb2_status = status; + DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr); + if (status) { + smb2sr_put_error(sr, status); + return (SDRC_SUCCESS); + } + /* ! * Encode an SMB2 Oplock Break Ack response ! * [MS-SMB2] 2.2.25.1 */ (void) smb_mbc_encodef( &sr->reply, "wb5.qq", ! SSZ_OPLOCK, /* w */ ! smbOplockLevel, /* b */ /* reserved 5. */ smb2fid.persistent, /* q */ smb2fid.temporal); /* q */ return (SDRC_SUCCESS); } /* * Compose an SMB2 Oplock Break Notification packet, including * the SMB2 header and everything, in sr->reply. * The caller will send it and free the request. */ void ! smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel) { smb_ofile_t *ofile = sr->fid_ofile; smb2fid_t smb2fid; uint16_t StructSize; uint8_t OplockLevel; ! /* ! * Convert internal level to SMB2 ! */ ! switch (NewLevel) { default: ASSERT(0); /* FALLTHROUGH */ ! case OPLOCK_LEVEL_NONE: OplockLevel = SMB2_OPLOCK_LEVEL_NONE; break; ! case OPLOCK_LEVEL_TWO: OplockLevel = SMB2_OPLOCK_LEVEL_II; break; } /* * SMB2 Header */ sr->smb2_cmd_code = SMB2_OPLOCK_BREAK; sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; ! sr->smb_tid = 0; sr->smb_pid = 0; ! sr->smb2_ssnid = 0; sr->smb2_messageid = UINT64_MAX; (void) smb2_encode_header(sr, B_FALSE); /* * SMB2 Oplock Break, variable part */ StructSize = 24; ! smb2fid.persistent = ofile->f_persistid; smb2fid.temporal = ofile->f_fid; (void) smb_mbc_encodef( &sr->reply, "wb5.qq", StructSize, /* w */ OplockLevel, /* b */ /* reserved 5. */ smb2fid.persistent, /* q */ smb2fid.temporal); /* q */ + } + + /* + * Client has an open handle and requests an oplock. + * Convert SMB2 oplock request info in to internal form, + * call common oplock code, convert result to SMB2. + * + * If necessary, "go async" here. + */ + void + smb2_oplock_acquire(smb_request_t *sr) + { + smb_arg_open_t *op = &sr->arg.open; + smb_ofile_t *ofile = sr->fid_ofile; + uint32_t status; + + /* Only disk trees get oplocks. */ + ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE); + + /* Only plain files... */ + if (!smb_node_is_file(ofile->f_node)) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + return; + } + + if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + return; + } + + /* + * SMB2: Convert to internal form. + */ + switch (op->op_oplock_level) { + case SMB2_OPLOCK_LEVEL_BATCH: + op->op_oplock_state = OPLOCK_LEVEL_BATCH; + break; + case SMB2_OPLOCK_LEVEL_EXCLUSIVE: + op->op_oplock_state = OPLOCK_LEVEL_ONE; + break; + case SMB2_OPLOCK_LEVEL_II: + op->op_oplock_state = OPLOCK_LEVEL_TWO; + break; + case SMB2_OPLOCK_LEVEL_LEASE: + ASSERT(0); /* Handled elsewhere */ + /* FALLTHROUGH */ + case SMB2_OPLOCK_LEVEL_NONE: + default: + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + return; + } + + /* + * Tree options may force shared oplocks, + * in which case we reduce the request. + */ + if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) { + op->op_oplock_state = OPLOCK_LEVEL_TWO; + } + + /* + * Try exclusive first, if requested + */ + if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) { + status = smb_oplock_request(sr, ofile, + &op->op_oplock_state); + } else { + status = NT_STATUS_OPLOCK_NOT_GRANTED; + } + + /* + * If exclusive failed (or the tree forced shared oplocks) + * try for a shared oplock (Level II) + */ + if (status == NT_STATUS_OPLOCK_NOT_GRANTED) { + op->op_oplock_state = OPLOCK_LEVEL_TWO; + status = smb_oplock_request(sr, ofile, + &op->op_oplock_state); + } + + /* + * Either of the above may have returned the + * status code that says we should wait. + */ + if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { + (void) smb2sr_go_async(sr); + (void) smb_oplock_wait_break(ofile->f_node, 0); + status = 0; + } + + /* + * Keep track of what we got (in ofile->f_oplock.og_state) + * so we'll know what we had when sending a break later. + * The og_dialect here is the oplock dialect, not the + * SMB dialect. No lease here, so SMB 2.0. + */ + ofile->f_oplock.og_dialect = SMB_VERS_2_002; + switch (status) { + case NT_STATUS_SUCCESS: + ofile->f_oplock.og_state = op->op_oplock_state; + break; + case NT_STATUS_OPLOCK_NOT_GRANTED: + ofile->f_oplock.og_state = 0; + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + return; + default: + /* Caller did not check args sufficiently? */ + cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x", + sr->session->ip_addr_str, status); + ofile->f_oplock.og_state = 0; + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + return; + } + + /* + * Have STATUS_SUCCESS + * Convert internal oplock state to SMB2 + */ + if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) { + ASSERT(0); + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + } else if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_II; + } else { + op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; + } }