}
static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
- struct ksmbd_work *in_work)
+ struct ksmbd_work *in_work, bool share_break)
{
int err = 0;
bool sent_interim = false;
* none.
*/
lease->new_state = SMB2_LEASE_NONE_LE;
+ } else if (share_break &&
+ lease->state & SMB2_LEASE_HANDLE_CACHING_LE) {
+ lease->new_state =
+ lease->state & ~SMB2_LEASE_HANDLE_CACHING_LE;
} else {
if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) {
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
if (wait_ack)
wait_lease_breaking(brk_opinfo);
- if (wait_ack && !err &&
+ /*
+ * A break caused by a share-mode conflict only drops the
+ * conflicting caching bit and the triggering open still fails
+ * with a sharing violation, so it must stay a single break.
+ * Do not cascade down to req_op_level through the again loop.
+ */
+ if (wait_ack && !err && !share_break &&
lease_break_needed(brk_opinfo, req_op_level, open_trunc))
goto again;
continue;
}
- oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
+ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false);
opinfo_put(opinfo);
}
}
continue;
}
- oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
+ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false);
opinfo_put(opinfo);
}
}
prev_fid = prev_opinfo->fid;
}
- err = oplock_break(prev_opinfo, break_level, work);
+ err = oplock_break(prev_opinfo, break_level, work,
+ share_ret < 0 && prev_opinfo->is_lease);
if (prev_durable_detached || (prev_durable_open && err == -ENOENT))
ksmbd_invalidate_durable_fd(prev_fid);
opinfo_put(prev_opinfo);
}
brk_opinfo->open_trunc = is_trunc;
- oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work);
+ oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work, false);
sent_break = true;
opinfo_put(brk_opinfo);
oplock_break(brk_op,
brk_op->is_lease && !is_trunc ?
SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE,
- send_interim && !sent_interim ? work : NULL);
+ send_interim && !sent_interim ? work : NULL,
+ false);
}
sent_interim = true;
next: