struct smb2_oplock_break *rsp;
struct ksmbd_file *fp;
struct oplock_info *opinfo = NULL;
- __le32 err = 0;
- int ret = 0;
+ __le32 status = STATUS_SUCCESS;
+ int ret;
u64 volatile_id, persistent_id;
char req_oplevel = 0, rsp_oplevel = 0;
- unsigned int oplock_change_type;
WORK_BUFFERS(work, req, rsp);
return;
}
- if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) {
- rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
+ if (opinfo->op_state != OPLOCK_ACK_WAIT) {
+ ksmbd_debug(SMB, "unexpected oplock state 0x%x\n",
+ opinfo->op_state);
+ status = STATUS_INVALID_DEVICE_STATE;
goto err_out;
}
- if (opinfo->op_state == OPLOCK_STATE_NONE) {
- ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state);
- rsp->hdr.Status = STATUS_UNSUCCESSFUL;
+ if (req_oplevel == SMB2_OPLOCK_LEVEL_LEASE) {
+ opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
+ status = STATUS_INVALID_PARAMETER;
goto err_out;
}
- if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE ||
- opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) &&
- (req_oplevel != SMB2_OPLOCK_LEVEL_II &&
- req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) {
- err = STATUS_INVALID_OPLOCK_PROTOCOL;
- oplock_change_type = OPLOCK_WRITE_TO_NONE;
- } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II &&
- req_oplevel != SMB2_OPLOCK_LEVEL_NONE) {
- err = STATUS_INVALID_OPLOCK_PROTOCOL;
- oplock_change_type = OPLOCK_READ_TO_NONE;
- } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II ||
- req_oplevel == SMB2_OPLOCK_LEVEL_NONE) {
- err = STATUS_INVALID_DEVICE_STATE;
- if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE ||
- opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) &&
- req_oplevel == SMB2_OPLOCK_LEVEL_II) {
- oplock_change_type = OPLOCK_WRITE_TO_READ;
- } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE ||
- opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) &&
- req_oplevel == SMB2_OPLOCK_LEVEL_NONE) {
- oplock_change_type = OPLOCK_WRITE_TO_NONE;
- } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II &&
- req_oplevel == SMB2_OPLOCK_LEVEL_NONE) {
- oplock_change_type = OPLOCK_READ_TO_NONE;
- } else {
- oplock_change_type = 0;
- }
- } else {
- oplock_change_type = 0;
+ if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) {
+ status = STATUS_INVALID_OPLOCK_PROTOCOL;
+ goto err_out;
}
- switch (oplock_change_type) {
- case OPLOCK_WRITE_TO_READ:
- ret = opinfo_write_to_read(opinfo);
- rsp_oplevel = SMB2_OPLOCK_LEVEL_II;
- break;
- case OPLOCK_WRITE_TO_NONE:
- ret = opinfo_write_to_none(opinfo);
- rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE;
- break;
- case OPLOCK_READ_TO_NONE:
- ret = opinfo_read_to_none(opinfo);
- rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE;
- break;
- default:
- pr_err("unknown oplock change 0x%x -> 0x%x\n",
- opinfo->level, rsp_oplevel);
+ if (opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE &&
+ req_oplevel != SMB2_OPLOCK_LEVEL_II &&
+ req_oplevel != SMB2_OPLOCK_LEVEL_NONE) {
+ opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
+ status = STATUS_INVALID_OPLOCK_PROTOCOL;
+ goto err_out;
}
- if (ret < 0) {
- rsp->hdr.Status = err;
+ if (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH &&
+ req_oplevel != SMB2_OPLOCK_LEVEL_II &&
+ req_oplevel != SMB2_OPLOCK_LEVEL_NONE &&
+ req_oplevel != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
+ opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
+ status = STATUS_INVALID_OPLOCK_PROTOCOL;
+ goto err_out;
+ }
+
+ if (opinfo->level == SMB2_OPLOCK_LEVEL_II &&
+ req_oplevel != SMB2_OPLOCK_LEVEL_NONE) {
+ opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
+ status = STATUS_INVALID_OPLOCK_PROTOCOL;
goto err_out;
}
+ if (req_oplevel == SMB2_OPLOCK_LEVEL_EXCLUSIVE)
+ rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE;
+ else
+ rsp_oplevel = req_oplevel;
+
+ opinfo->level = rsp_oplevel;
+
rsp->StructureSize = cpu_to_le16(24);
rsp->OplockLevel = rsp_oplevel;
rsp->Reserved = 0;
rsp->VolatileFid = volatile_id;
rsp->PersistentFid = persistent_id;
ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break));
- if (ret) {
+ if (ret)
+ ksmbd_debug(SMB, "failed to pin oplock break response: %d\n",
+ ret);
+ goto out;
+
err_out:
- smb2_set_err_rsp(work);
- }
+ rsp->hdr.Status = status;
+ smb2_set_err_rsp(work);
+out:
opinfo->op_state = OPLOCK_STATE_NONE;
wake_up_interruptible_all(&opinfo->oplock_q);
opinfo_put(opinfo);