18c10a865Sas200622 /* 28c10a865Sas200622 * CDDL HEADER START 38c10a865Sas200622 * 48c10a865Sas200622 * The contents of this file are subject to the terms of the 58c10a865Sas200622 * Common Development and Distribution License (the "License"). 68c10a865Sas200622 * You may not use this file except in compliance with the License. 78c10a865Sas200622 * 88c10a865Sas200622 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98c10a865Sas200622 * or http://www.opensolaris.org/os/licensing. 108c10a865Sas200622 * See the License for the specific language governing permissions 118c10a865Sas200622 * and limitations under the License. 128c10a865Sas200622 * 138c10a865Sas200622 * When distributing Covered Code, include this CDDL HEADER in each 148c10a865Sas200622 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158c10a865Sas200622 * If applicable, add the following below this CDDL HEADER, with the 168c10a865Sas200622 * fields enclosed by brackets "[]" replaced with your own identifying 178c10a865Sas200622 * information: Portions Copyright [yyyy] [name of copyright owner] 188c10a865Sas200622 * 198c10a865Sas200622 * CDDL HEADER END 208c10a865Sas200622 */ 218c10a865Sas200622 /* 22148c5f43SAlan Wright * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23525641e8SGordon Ross * Copyright 2020 Tintri by DDN, Inc. All rights reserved. 2472b35b05SGordon Ross * Copyright 2022 RackTop Systems, Inc. 258c10a865Sas200622 */ 26cb174861Sjoyce mcintosh 278c10a865Sas200622 /* 2894047d49SGordon Ross * smb1 oplock support 298c10a865Sas200622 */ 308c10a865Sas200622 31bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h> 328c10a865Sas200622 3394047d49SGordon Ross #define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE) 34cb174861Sjoyce mcintosh 35cb174861Sjoyce mcintosh /* 3672b35b05SGordon Ross * This is called by the SMB1 "Locking_andX" handler, 3772b35b05SGordon Ross * for SMB1 oplock break acknowledgement. 3872b35b05SGordon Ross * This is an "Ack" from the client. 3972b35b05SGordon Ross */ 4072b35b05SGordon Ross void 4172b35b05SGordon Ross smb1_oplock_ack_break(smb_request_t *sr, uchar_t oplock_level) 4272b35b05SGordon Ross { 4372b35b05SGordon Ross smb_ofile_t *ofile; 4472b35b05SGordon Ross smb_node_t *node; 4572b35b05SGordon Ross uint32_t NewLevel; 4672b35b05SGordon Ross 4772b35b05SGordon Ross ofile = sr->fid_ofile; 4872b35b05SGordon Ross node = ofile->f_node; 4972b35b05SGordon Ross 5072b35b05SGordon Ross if (oplock_level == 0) 5172b35b05SGordon Ross NewLevel = OPLOCK_LEVEL_NONE; 5272b35b05SGordon Ross else 5372b35b05SGordon Ross NewLevel = OPLOCK_LEVEL_TWO; 5472b35b05SGordon Ross 5572b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 5672b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex); 5772b35b05SGordon Ross 58*7f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE; 5972b35b05SGordon Ross cv_broadcast(&ofile->f_oplock.og_ack_cv); 6072b35b05SGordon Ross 6172b35b05SGordon Ross (void) smb_oplock_ack_break(sr, ofile, &NewLevel); 6272b35b05SGordon Ross 6372b35b05SGordon Ross ofile->f_oplock.og_state = NewLevel; 6472b35b05SGordon Ross 6572b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex); 6672b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 6772b35b05SGordon Ross } 6872b35b05SGordon Ross 6972b35b05SGordon Ross /* 7072b35b05SGordon Ross * Compose an SMB1 Oplock Break Notification packet, including 7172b35b05SGordon Ross * the SMB1 header and everything, in sr->reply. 7272b35b05SGordon Ross * The caller will send it and free the request. 7372b35b05SGordon Ross */ 7472b35b05SGordon Ross static void 7572b35b05SGordon Ross smb1_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel) 7672b35b05SGordon Ross { 7772b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 7872b35b05SGordon Ross uint16_t fid; 7972b35b05SGordon Ross uint8_t lock_type; 8072b35b05SGordon Ross uint8_t oplock_level; 8172b35b05SGordon Ross 8272b35b05SGordon Ross /* 8372b35b05SGordon Ross * Convert internal level to SMB1 8472b35b05SGordon Ross */ 8572b35b05SGordon Ross switch (NewLevel) { 8672b35b05SGordon Ross default: 8772b35b05SGordon Ross ASSERT(0); 8872b35b05SGordon Ross /* FALLTHROUGH */ 8972b35b05SGordon Ross case OPLOCK_LEVEL_NONE: 9072b35b05SGordon Ross oplock_level = 0; 9172b35b05SGordon Ross break; 9272b35b05SGordon Ross 9372b35b05SGordon Ross case OPLOCK_LEVEL_TWO: 9472b35b05SGordon Ross oplock_level = 1; 9572b35b05SGordon Ross break; 9672b35b05SGordon Ross } 9772b35b05SGordon Ross 9872b35b05SGordon Ross sr->smb_com = SMB_COM_LOCKING_ANDX; 9972b35b05SGordon Ross sr->smb_tid = ofile->f_tree->t_tid; 10072b35b05SGordon Ross sr->smb_pid = 0xFFFF; 10172b35b05SGordon Ross sr->smb_uid = 0; 10272b35b05SGordon Ross sr->smb_mid = 0xFFFF; 10372b35b05SGordon Ross fid = ofile->f_fid; 10472b35b05SGordon Ross lock_type = LOCKING_ANDX_OPLOCK_RELEASE; 10572b35b05SGordon Ross 10672b35b05SGordon Ross (void) smb_mbc_encodef( 10772b35b05SGordon Ross &sr->reply, "Mb19.wwwwbb3.wbb10.", 10872b35b05SGordon Ross /* "\xffSMB" M */ 10972b35b05SGordon Ross sr->smb_com, /* b */ 11072b35b05SGordon Ross /* status, flags, signature 19. */ 11172b35b05SGordon Ross sr->smb_tid, /* w */ 11272b35b05SGordon Ross sr->smb_pid, /* w */ 11372b35b05SGordon Ross sr->smb_uid, /* w */ 11472b35b05SGordon Ross sr->smb_mid, /* w */ 11572b35b05SGordon Ross 8, /* word count b */ 11672b35b05SGordon Ross 0xFF, /* AndX cmd b */ 11772b35b05SGordon Ross /* AndX reserved, offset 3. */ 11872b35b05SGordon Ross fid, 11972b35b05SGordon Ross lock_type, 12072b35b05SGordon Ross oplock_level); 12172b35b05SGordon Ross } 12272b35b05SGordon Ross 12372b35b05SGordon Ross /* 12472b35b05SGordon Ross * Send an oplock break over the wire, or if we can't, 12572b35b05SGordon Ross * then process the oplock break locally. 12672b35b05SGordon Ross * 12772b35b05SGordon Ross * [MS-CIFS] 3.3.4.2 Object Store Indicates an OpLock Break 12872b35b05SGordon Ross * 12972b35b05SGordon Ross * This is mostly similar to smb2_oplock_send_break() 13072b35b05SGordon Ross * See top comment there about the design. 13172b35b05SGordon Ross * Called from smb_oplock_async_break. 13272b35b05SGordon Ross * 13372b35b05SGordon Ross * This handles only SMB1, which has no durable handles, 13472b35b05SGordon Ross * and never has GRANULAR oplocks. 13572b35b05SGordon Ross */ 13672b35b05SGordon Ross void 13772b35b05SGordon Ross smb1_oplock_send_break(smb_request_t *sr) 13872b35b05SGordon Ross { 13972b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 14072b35b05SGordon Ross smb_node_t *node = ofile->f_node; 14172b35b05SGordon Ross uint32_t NewLevel = sr->arg.olbrk.NewLevel; 14272b35b05SGordon Ross boolean_t AckReq = sr->arg.olbrk.AckRequired; 14372b35b05SGordon Ross uint32_t status; 14472b35b05SGordon Ross int rc; 14572b35b05SGordon Ross 14672b35b05SGordon Ross /* 14772b35b05SGordon Ross * SMB1 clients should only get Level II oplocks if they 14872b35b05SGordon Ross * set the capability indicating they know about them. 14972b35b05SGordon Ross */ 15072b35b05SGordon Ross if (NewLevel == OPLOCK_LEVEL_TWO && 15172b35b05SGordon Ross ofile->f_oplock.og_dialect < NT_LM_0_12) 15272b35b05SGordon Ross NewLevel = OPLOCK_LEVEL_NONE; 15372b35b05SGordon Ross 15472b35b05SGordon Ross /* 15572b35b05SGordon Ross * Build the break message in sr->reply. 15672b35b05SGordon Ross * It's free'd in smb_request_free(). 15772b35b05SGordon Ross * Always SMB1 here. 15872b35b05SGordon Ross */ 15972b35b05SGordon Ross sr->reply.max_bytes = MLEN; 16072b35b05SGordon Ross smb1_oplock_break_notification(sr, NewLevel); 16172b35b05SGordon Ross 16272b35b05SGordon Ross /* 16372b35b05SGordon Ross * Try to send the break message to the client. 16472b35b05SGordon Ross * If connected, this IF body will be true. 16572b35b05SGordon Ross */ 16672b35b05SGordon Ross if (sr->session == ofile->f_session) 16772b35b05SGordon Ross rc = smb_session_send(sr->session, 0, &sr->reply); 16872b35b05SGordon Ross else 16972b35b05SGordon Ross rc = ENOTCONN; 17072b35b05SGordon Ross 17172b35b05SGordon Ross if (rc != 0) { 17272b35b05SGordon Ross /* 17372b35b05SGordon Ross * We were unable to send the oplock break request, 17472b35b05SGordon Ross * presumably because the connection is gone. 17572b35b05SGordon Ross * Just close the handle. 17672b35b05SGordon Ross */ 17772b35b05SGordon Ross smb_ofile_close(ofile, 0); 17872b35b05SGordon Ross return; 17972b35b05SGordon Ross } 18072b35b05SGordon Ross 18172b35b05SGordon Ross /* 18272b35b05SGordon Ross * OK, we were able to send the break message. 18372b35b05SGordon Ross * If no ack. required, we're done. 18472b35b05SGordon Ross */ 18572b35b05SGordon Ross if (!AckReq) 18672b35b05SGordon Ross return; 18772b35b05SGordon Ross 18872b35b05SGordon Ross /* 18972b35b05SGordon Ross * We're expecting an ACK. Wait in this thread 19072b35b05SGordon Ross * so we can log clients that don't respond. 19172b35b05SGordon Ross */ 192*7f6a299eSGordon Ross status = smb_oplock_wait_ack(sr, NewLevel); 19372b35b05SGordon Ross if (status == 0) 19472b35b05SGordon Ross return; 19572b35b05SGordon Ross 19672b35b05SGordon Ross cmn_err(CE_NOTE, "clnt %s oplock break timeout", 19772b35b05SGordon Ross sr->session->ip_addr_str); 19872b35b05SGordon Ross DTRACE_PROBE1(ack_timeout, smb_request_t *, sr); 19972b35b05SGordon Ross 20072b35b05SGordon Ross /* 20172b35b05SGordon Ross * Did not get an ACK, so do the ACK locally. 20272b35b05SGordon Ross * Note: always break to none here, regardless 20372b35b05SGordon Ross * of what the passed in cache level was. 20472b35b05SGordon Ross */ 20572b35b05SGordon Ross NewLevel = OPLOCK_LEVEL_NONE; 20672b35b05SGordon Ross 20772b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 20872b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex); 20972b35b05SGordon Ross 210*7f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE; 21172b35b05SGordon Ross cv_broadcast(&ofile->f_oplock.og_ack_cv); 21272b35b05SGordon Ross 21372b35b05SGordon Ross status = smb_oplock_ack_break(sr, ofile, &NewLevel); 21472b35b05SGordon Ross 21572b35b05SGordon Ross ofile->f_oplock.og_state = NewLevel; 21672b35b05SGordon Ross 21772b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex); 21872b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 21972b35b05SGordon Ross 22072b35b05SGordon Ross #ifdef DEBUG 22172b35b05SGordon Ross if (status != 0) { 22272b35b05SGordon Ross cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x", 22372b35b05SGordon Ross sr->session->ip_addr_str, status); 22472b35b05SGordon Ross } 22572b35b05SGordon Ross #endif 22672b35b05SGordon Ross } 22772b35b05SGordon Ross 22872b35b05SGordon Ross /* 22994047d49SGordon Ross * Client has an open handle and requests an oplock. 23094047d49SGordon Ross * Convert SMB1 oplock request info in to internal form, 23194047d49SGordon Ross * call common oplock code, convert result to SMB1. 2328c10a865Sas200622 */ 2332c2961f8Sjose borrego void 23494047d49SGordon Ross smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok) 2358c10a865Sas200622 { 23694047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open; 237811599a4SMatt Barden smb_ofile_t *ofile = sr->fid_ofile; 23894047d49SGordon Ross uint32_t status; 239a90cf9f2SGordon Ross 24094047d49SGordon Ross /* Only disk trees get oplocks. */ 24194047d49SGordon Ross if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) { 24294047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_NONE; 243cb174861Sjoyce mcintosh return; 2448c10a865Sas200622 } 2458c10a865Sas200622 24694047d49SGordon Ross if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) { 24794047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_NONE; 24894047d49SGordon Ross return; 2492c2961f8Sjose borrego } 25094047d49SGordon Ross 25194047d49SGordon Ross if (!smb_session_levelII_oplocks(sr->session)) 25294047d49SGordon Ross level2ok = B_FALSE; 25394047d49SGordon Ross 25494047d49SGordon Ross /* Common code checks file type. */ 25594047d49SGordon Ross 25694047d49SGordon Ross /* 25794047d49SGordon Ross * SMB1: Convert to internal form. 25894047d49SGordon Ross */ 25994047d49SGordon Ross switch (op->op_oplock_level) { 26094047d49SGordon Ross case SMB_OPLOCK_BATCH: 26194047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_BATCH; 2622c2961f8Sjose borrego break; 26394047d49SGordon Ross case SMB_OPLOCK_EXCLUSIVE: 26494047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_ONE; 26594047d49SGordon Ross break; 26694047d49SGordon Ross case SMB_OPLOCK_LEVEL_II: 26794047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_TWO; 26894047d49SGordon Ross break; 26994047d49SGordon Ross case SMB_OPLOCK_NONE: 2702c2961f8Sjose borrego default: 27194047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_NONE; 27294047d49SGordon Ross return; 2738c10a865Sas200622 } 2748c10a865Sas200622 2752c2961f8Sjose borrego /* 27694047d49SGordon Ross * Tree options may force shared oplocks 2772c2961f8Sjose borrego */ 27894047d49SGordon Ross if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) { 27994047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_TWO; 2808c10a865Sas200622 } 2818c10a865Sas200622 2822c2961f8Sjose borrego /* 28394047d49SGordon Ross * Try exclusive first, if requested 2842c2961f8Sjose borrego */ 28594047d49SGordon Ross if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) { 28694047d49SGordon Ross status = smb_oplock_request(sr, ofile, 28794047d49SGordon Ross &op->op_oplock_state); 28894047d49SGordon Ross } else { 28994047d49SGordon Ross status = NT_STATUS_OPLOCK_NOT_GRANTED; 2908c10a865Sas200622 } 291cb174861Sjoyce mcintosh 292cb174861Sjoyce mcintosh /* 29372b35b05SGordon Ross * If exclusive failed (or the tree forced shared oplocks) 29494047d49SGordon Ross * and if the caller supports Level II, try shared. 295cb174861Sjoyce mcintosh */ 29694047d49SGordon Ross if (status == NT_STATUS_OPLOCK_NOT_GRANTED && level2ok) { 29794047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_TWO; 29894047d49SGordon Ross status = smb_oplock_request(sr, ofile, 29994047d49SGordon Ross &op->op_oplock_state); 300cb174861Sjoyce mcintosh } 301cb174861Sjoyce mcintosh 302cb174861Sjoyce mcintosh /* 30372b35b05SGordon Ross * Keep track of what we got (ofile->f_oplock.og_state etc) 30494047d49SGordon Ross * so we'll know what we had when sending a break later. 30594047d49SGordon Ross * The og_dialect here is the oplock dialect, which may be 30694047d49SGordon Ross * different than SMB dialect. Pre-NT clients did not 30794047d49SGordon Ross * support "Level II" oplocks. If we're talking to a 30894047d49SGordon Ross * client that didn't set the CAP_LEVEL_II_OPLOCKS in 30994047d49SGordon Ross * its capabilities, let og_dialect = LANMAN2_1. 310cb174861Sjoyce mcintosh */ 31194047d49SGordon Ross switch (status) { 31294047d49SGordon Ross case NT_STATUS_SUCCESS: 31372b35b05SGordon Ross case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS: 31472b35b05SGordon Ross ofile->f_oplock.og_dialect = (level2ok) ? 31572b35b05SGordon Ross NT_LM_0_12 : LANMAN2_1; 31694047d49SGordon Ross ofile->f_oplock.og_state = op->op_oplock_state; 317*7f6a299eSGordon Ross ofile->f_oplock.og_breakto = op->op_oplock_state; 318*7f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE; 31994047d49SGordon Ross break; 32094047d49SGordon Ross case NT_STATUS_OPLOCK_NOT_GRANTED: 32194047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_NONE; 32294047d49SGordon Ross return; 32394047d49SGordon Ross default: 32494047d49SGordon Ross /* Caller did not check args sufficiently? */ 32594047d49SGordon Ross cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x", 32694047d49SGordon Ross sr->session->ip_addr_str, status); 32794047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_NONE; 32894047d49SGordon Ross return; 329cb174861Sjoyce mcintosh } 330cb174861Sjoyce mcintosh 331cb174861Sjoyce mcintosh /* 33272b35b05SGordon Ross * Only succes cases get here. 33394047d49SGordon Ross * Convert internal oplock state to SMB1 334cb174861Sjoyce mcintosh */ 33594047d49SGordon Ross if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) { 33694047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_BATCH; 33794047d49SGordon Ross } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) { 33894047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE; 33994047d49SGordon Ross } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) { 34094047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_LEVEL_II; 34194047d49SGordon Ross } else { 34294047d49SGordon Ross op->op_oplock_level = SMB_OPLOCK_NONE; 343cb174861Sjoyce mcintosh } 34472b35b05SGordon Ross 34572b35b05SGordon Ross /* 34672b35b05SGordon Ross * An smb_oplock_reqest call may have returned the 34772b35b05SGordon Ross * status code that says we should wait. 34872b35b05SGordon Ross */ 34972b35b05SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 35072b35b05SGordon Ross (void) smb_oplock_wait_break(sr, ofile->f_node, 0); 35172b35b05SGordon Ross } 352cb174861Sjoyce mcintosh } 353