Skip to content

Commit 415855d

Browse files
committed
ksmbd: fix lease break and ack state handling
Do not skip valid lease states containing WRITE_CACHING when breaking level-II/read leases for writes and truncates. Handle lease break acknowledgments according to the SMB2 rule that the acknowledged state must be a subset of the server's break target. Apply the acknowledged state directly and keep the break pending on failed ACKs. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
1 parent d65adf4 commit 415855d

2 files changed

Lines changed: 34 additions & 96 deletions

File tree

oplock.c

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,14 +1661,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
16611661
}
16621662
}
16631663
#else
1664-
if (brk_op->is_lease &&
1665-
(brk_op->o_lease->state &
1666-
(~(SMB2_LEASE_READ_CACHING_LE |
1667-
SMB2_LEASE_HANDLE_CACHING_LE)))) {
1668-
ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n",
1669-
brk_op->o_lease->state);
1670-
goto next;
1671-
} else if (brk_op->level != SMB2_OPLOCK_LEVEL_II) {
1664+
if (!brk_op->is_lease &&
1665+
brk_op->level != SMB2_OPLOCK_LEVEL_II) {
16721666
ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n",
16731667
brk_op->level);
16741668
goto next;
@@ -1721,15 +1715,13 @@ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp)
17211715
*/
17221716
__u8 smb2_map_lease_to_oplock(__le32 lease_state)
17231717
{
1724-
if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE |
1725-
SMB2_LEASE_READ_CACHING_LE |
1726-
SMB2_LEASE_WRITE_CACHING_LE)) {
1718+
if ((lease_state & SMB2_LEASE_WRITE_CACHING_LE) &&
1719+
(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) {
17271720
return SMB2_OPLOCK_LEVEL_BATCH;
1728-
} else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE &&
1729-
lease_state & SMB2_LEASE_WRITE_CACHING_LE) {
1730-
if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE))
1731-
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
1732-
} else if (lease_state & SMB2_LEASE_READ_CACHING_LE) {
1721+
} else if (lease_state & SMB2_LEASE_WRITE_CACHING_LE) {
1722+
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
1723+
} else if (lease_state & (SMB2_LEASE_READ_CACHING_LE |
1724+
SMB2_LEASE_HANDLE_CACHING_LE)) {
17331725
return SMB2_OPLOCK_LEVEL_II;
17341726
}
17351727
return 0;

smb2pdu.c

Lines changed: 26 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -9381,16 +9381,17 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work)
93819381
ksmbd_fd_put(work, fp);
93829382
}
93839383

9384-
static int check_lease_state(struct lease *lease, __le32 req_state)
9384+
static bool smb2_lease_state_valid(__le32 state)
93859385
{
9386-
if ((lease->new_state ==
9387-
(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) &&
9388-
!(req_state & SMB2_LEASE_WRITE_CACHING_LE)) {
9389-
lease->new_state = req_state;
9390-
return 0;
9391-
}
9386+
return !(state & ~(SMB2_LEASE_READ_CACHING_LE |
9387+
SMB2_LEASE_HANDLE_CACHING_LE |
9388+
SMB2_LEASE_WRITE_CACHING_LE));
9389+
}
93929390

9393-
if (lease->new_state == req_state)
9391+
static int check_lease_state(struct lease *lease, __le32 req_state)
9392+
{
9393+
if (smb2_lease_state_valid(req_state) &&
9394+
!(req_state & ~lease->new_state))
93949395
return 0;
93959396

93969397
return 1;
@@ -9408,9 +9409,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
94089409
struct smb2_lease_ack *req;
94099410
struct smb2_lease_ack *rsp;
94109411
struct oplock_info *opinfo;
9411-
__le32 err = 0;
94129412
int ret = 0;
9413-
unsigned int lease_change_type;
94149413
__le32 lease_state;
94159414
struct lease *lease;
94169415

@@ -9434,80 +9433,23 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
94349433
goto err_out;
94359434
}
94369435

9437-
if (check_lease_state(lease, req->LeaseState)) {
9438-
rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED;
9439-
ksmbd_debug(OPLOCK,
9440-
"req lease state: 0x%x, expected state: 0x%x\n",
9441-
req->LeaseState, lease->new_state);
9442-
goto err_out;
9443-
}
9444-
94459436
if (!atomic_read(&opinfo->breaking_cnt)) {
94469437
rsp->hdr.Status = STATUS_UNSUCCESSFUL;
94479438
goto err_out;
94489439
}
94499440

9450-
/* check for bad lease state */
9451-
if (req->LeaseState &
9452-
(~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) {
9453-
err = STATUS_INVALID_OPLOCK_PROTOCOL;
9454-
if (lease->state & SMB2_LEASE_WRITE_CACHING_LE)
9455-
lease_change_type = OPLOCK_WRITE_TO_NONE;
9456-
else
9457-
lease_change_type = OPLOCK_READ_TO_NONE;
9458-
ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n",
9459-
le32_to_cpu(lease->state),
9460-
le32_to_cpu(req->LeaseState));
9461-
} else if (lease->state == SMB2_LEASE_READ_CACHING_LE &&
9462-
req->LeaseState != SMB2_LEASE_NONE_LE) {
9463-
err = STATUS_INVALID_OPLOCK_PROTOCOL;
9464-
lease_change_type = OPLOCK_READ_TO_NONE;
9465-
ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n",
9466-
le32_to_cpu(lease->state),
9467-
le32_to_cpu(req->LeaseState));
9468-
} else {
9469-
/* valid lease state changes */
9470-
err = STATUS_INVALID_DEVICE_STATE;
9471-
if (req->LeaseState == SMB2_LEASE_NONE_LE) {
9472-
if (lease->state & SMB2_LEASE_WRITE_CACHING_LE)
9473-
lease_change_type = OPLOCK_WRITE_TO_NONE;
9474-
else
9475-
lease_change_type = OPLOCK_READ_TO_NONE;
9476-
} else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) {
9477-
if (lease->state & SMB2_LEASE_WRITE_CACHING_LE)
9478-
lease_change_type = OPLOCK_WRITE_TO_READ;
9479-
else
9480-
lease_change_type = OPLOCK_READ_HANDLE_TO_READ;
9481-
} else {
9482-
lease_change_type = 0;
9483-
}
9484-
}
9485-
9486-
switch (lease_change_type) {
9487-
case OPLOCK_WRITE_TO_READ:
9488-
ret = opinfo_write_to_read(opinfo);
9489-
break;
9490-
case OPLOCK_READ_HANDLE_TO_READ:
9491-
ret = opinfo_read_handle_to_read(opinfo);
9492-
break;
9493-
case OPLOCK_WRITE_TO_NONE:
9494-
ret = opinfo_write_to_none(opinfo);
9495-
break;
9496-
case OPLOCK_READ_TO_NONE:
9497-
ret = opinfo_read_to_none(opinfo);
9498-
break;
9499-
default:
9500-
ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n",
9501-
le32_to_cpu(lease->state),
9502-
le32_to_cpu(req->LeaseState));
9503-
}
9504-
9505-
if (ret < 0) {
9506-
rsp->hdr.Status = err;
9441+
if (check_lease_state(lease, req->LeaseState)) {
9442+
rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED;
9443+
ksmbd_debug(OPLOCK,
9444+
"req lease state: 0x%x, expected state: 0x%x\n",
9445+
req->LeaseState, lease->new_state);
95079446
goto err_out;
95089447
}
95099448

9510-
lease_state = lease->state;
9449+
lease_state = req->LeaseState;
9450+
lease->state = lease_state;
9451+
lease->new_state = SMB2_LEASE_NONE_LE;
9452+
opinfo->level = smb2_map_lease_to_oplock(lease_state);
95119453

95129454
rsp->StructureSize = cpu_to_le16(36);
95139455
rsp->Reserved = 0;
@@ -9516,16 +9458,20 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
95169458
rsp->LeaseState = lease_state;
95179459
rsp->LeaseDuration = 0;
95189460
ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack));
9519-
if (ret) {
9520-
err_out:
9521-
smb2_set_err_rsp(work);
9522-
}
9461+
if (ret)
9462+
goto err_out;
95239463

95249464
opinfo->op_state = OPLOCK_STATE_NONE;
95259465
wake_up_interruptible_all(&opinfo->oplock_q);
95269466
atomic_dec(&opinfo->breaking_cnt);
95279467
wake_up_interruptible_all(&opinfo->oplock_brk);
95289468
opinfo_put(opinfo);
9469+
return;
9470+
9471+
err_out:
9472+
smb2_set_err_rsp(work);
9473+
opinfo_put(opinfo);
9474+
return;
95299475
}
95309476

95319477
/**

0 commit comments

Comments
 (0)