#include <smbsrv/smb_kproto.h>
#define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
void
smb1_oplock_ack_break(smb_request_t *sr, uchar_t oplock_level)
{
smb_ofile_t *ofile;
smb_node_t *node;
uint32_t NewLevel;
ofile = sr->fid_ofile;
node = ofile->f_node;
if (oplock_level == 0)
NewLevel = OPLOCK_LEVEL_NONE;
else
NewLevel = OPLOCK_LEVEL_TWO;
smb_llist_enter(&node->n_ofile_list, RW_READER);
mutex_enter(&node->n_oplock.ol_mutex);
ofile->f_oplock.og_breaking = B_FALSE;
cv_broadcast(&ofile->f_oplock.og_ack_cv);
(void) smb_oplock_ack_break(sr, ofile, &NewLevel);
ofile->f_oplock.og_state = NewLevel;
mutex_exit(&node->n_oplock.ol_mutex);
smb_llist_exit(&node->n_ofile_list);
}
static void
smb1_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
{
smb_ofile_t *ofile = sr->fid_ofile;
uint16_t fid;
uint8_t lock_type;
uint8_t oplock_level;
switch (NewLevel) {
default:
ASSERT(0);
case OPLOCK_LEVEL_NONE:
oplock_level = 0;
break;
case OPLOCK_LEVEL_TWO:
oplock_level = 1;
break;
}
sr->smb_com = SMB_COM_LOCKING_ANDX;
sr->smb_tid = ofile->f_tree->t_tid;
sr->smb_pid = 0xFFFF;
sr->smb_uid = 0;
sr->smb_mid = 0xFFFF;
fid = ofile->f_fid;
lock_type = LOCKING_ANDX_OPLOCK_RELEASE;
(void) smb_mbc_encodef(
&sr->reply, "Mb19.wwwwbb3.wbb10.",
sr->smb_com,
sr->smb_tid,
sr->smb_pid,
sr->smb_uid,
sr->smb_mid,
8,
0xFF,
fid,
lock_type,
oplock_level);
}
void
smb1_oplock_send_break(smb_request_t *sr)
{
smb_ofile_t *ofile = sr->fid_ofile;
smb_node_t *node = ofile->f_node;
uint32_t NewLevel = sr->arg.olbrk.NewLevel;
boolean_t AckReq = sr->arg.olbrk.AckRequired;
uint32_t status;
int rc;
if (NewLevel == OPLOCK_LEVEL_TWO &&
ofile->f_oplock.og_dialect < NT_LM_0_12)
NewLevel = OPLOCK_LEVEL_NONE;
sr->reply.max_bytes = MLEN;
smb1_oplock_break_notification(sr, NewLevel);
if (sr->session == ofile->f_session)
rc = smb_session_send(sr->session, 0, &sr->reply);
else
rc = ENOTCONN;
if (rc != 0) {
smb_ofile_close(ofile, 0);
return;
}
if (!AckReq)
return;
status = smb_oplock_wait_ack(sr, NewLevel);
if (status == 0)
return;
DTRACE_PROBE2(wait__ack__failed, smb_request_t *, sr,
uint32_t, status);
NewLevel = OPLOCK_LEVEL_NONE;
smb_llist_enter(&node->n_ofile_list, RW_READER);
mutex_enter(&node->n_oplock.ol_mutex);
ofile->f_oplock.og_breaking = B_FALSE;
cv_broadcast(&ofile->f_oplock.og_ack_cv);
status = smb_oplock_ack_break(sr, ofile, &NewLevel);
ofile->f_oplock.og_state = NewLevel;
mutex_exit(&node->n_oplock.ol_mutex);
smb_llist_exit(&node->n_ofile_list);
#ifdef DEBUG
if (status != 0) {
cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
sr->session->ip_addr_str, status);
}
#endif
}
void
smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
{
smb_arg_open_t *op = &sr->arg.open;
smb_ofile_t *ofile = sr->fid_ofile;
uint32_t status;
if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
if (!smb_session_levelII_oplocks(sr->session))
level2ok = B_FALSE;
switch (op->op_oplock_level) {
case SMB_OPLOCK_BATCH:
op->op_oplock_state = OPLOCK_LEVEL_BATCH;
break;
case SMB_OPLOCK_EXCLUSIVE:
op->op_oplock_state = OPLOCK_LEVEL_ONE;
break;
case SMB_OPLOCK_LEVEL_II:
op->op_oplock_state = OPLOCK_LEVEL_TWO;
break;
case SMB_OPLOCK_NONE:
default:
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
op->op_oplock_state = OPLOCK_LEVEL_TWO;
}
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 (status == NT_STATUS_OPLOCK_NOT_GRANTED && level2ok) {
op->op_oplock_state = OPLOCK_LEVEL_TWO;
status = smb_oplock_request(sr, ofile,
&op->op_oplock_state);
}
switch (status) {
case NT_STATUS_SUCCESS:
case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS:
ofile->f_oplock.og_dialect = (level2ok) ?
NT_LM_0_12 : LANMAN2_1;
ofile->f_oplock.og_state = op->op_oplock_state;
ofile->f_oplock.og_breakto = op->op_oplock_state;
ofile->f_oplock.og_breaking = B_FALSE;
break;
case NT_STATUS_OPLOCK_NOT_GRANTED:
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
default:
cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
sr->session->ip_addr_str, status);
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
op->op_oplock_level = SMB_OPLOCK_BATCH;
} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
} else {
op->op_oplock_level = SMB_OPLOCK_NONE;
}
if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
(void) smb_oplock_wait_break(sr, ofile->f_node, 0);
}
}