summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/smbsrv/smb2_lease.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs/smbsrv/smb2_lease.c')
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_lease.c68
1 files changed, 41 insertions, 27 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lease.c b/usr/src/uts/common/fs/smbsrv/smb2_lease.c
index 81bf93d8fe..c440fa16c8 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_lease.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_lease.c
@@ -301,7 +301,6 @@ smb2_lease_break_ack(smb_request_t *sr)
smb_node_t *node;
smb_ofile_t *ofile;
uint32_t LeaseState;
- uint32_t BreakTo;
uint32_t status;
int rc = 0;
@@ -363,7 +362,7 @@ smb2_lease_break_ack(smb_request_t *sr)
/* Success, so have sr->fid_ofile */
ofile = sr->fid_ofile;
- if (lease->ls_breaking == 0) {
+ if (lease->ls_breaking == B_FALSE) {
/*
* This ACK is either unsolicited or too late,
* eg. we timed out the ACK and did it locally.
@@ -376,9 +375,7 @@ smb2_lease_break_ack(smb_request_t *sr)
* If the new LeaseState has any bits in excess of
* the lease state we sent in the break, error...
*/
- BreakTo = (lease->ls_breaking >> BREAK_SHIFT) &
- OPLOCK_LEVEL_CACHE_MASK;
- if ((LeaseState & ~BreakTo) != 0) {
+ if ((LeaseState & ~(lease->ls_breakto)) != 0) {
status = NT_STATUS_REQUEST_NOT_ACCEPTED;
goto errout;
}
@@ -388,16 +385,18 @@ smb2_lease_break_ack(smb_request_t *sr)
*
* Clear breaking flags before we ack,
* because ack might set those.
+ * Signal both CVs, out of paranoia.
*/
- ofile->f_oplock.og_breaking = 0;
- lease->ls_breaking = 0;
+ ofile->f_oplock.og_breaking = B_FALSE;
+ cv_broadcast(&ofile->f_oplock.og_ack_cv);
+ lease->ls_breaking = B_FALSE;
cv_broadcast(&lease->ls_ack_cv);
LeaseState |= OPLOCK_LEVEL_GRANULAR;
status = smb_oplock_ack_break(sr, ofile, &LeaseState);
ofile->f_oplock.og_state = LeaseState;
- lease->ls_state = LeaseState & CACHE_RWH;
+ lease->ls_state = LeaseState;
/* ls_epoch does not change here */
if (ofile->dh_persist)
@@ -676,7 +675,7 @@ smb2_lease_send_break(smb_request_t *sr)
* We're expecting an ACK. Wait in this thread
* so we can log clients that don't respond.
*/
- status = smb_oplock_wait_ack(sr);
+ status = smb_oplock_wait_ack(sr, NewLevel);
if (status == 0)
return;
@@ -718,14 +717,14 @@ smb2_lease_send_break(smb_request_t *sr)
/*
* Now continue like the non-lease code
*/
- ofile->f_oplock.og_breaking = 0;
- lease->ls_breaking = 0;
+ ofile->f_oplock.og_breaking = B_FALSE;
+ lease->ls_breaking = B_FALSE;
cv_broadcast(&lease->ls_ack_cv);
status = smb_oplock_ack_break(sr, ofile, &NewLevel);
ofile->f_oplock.og_state = NewLevel;
- lease->ls_state = NewLevel & CACHE_RWH;
+ lease->ls_state = NewLevel;
/* ls_epoch does not change here */
if (ofile->dh_persist)
@@ -748,7 +747,7 @@ unlock_out:
* Convert SMB2 lease request info in to internal form,
* call common oplock code, convert result to SMB2.
*
- * If necessary, "go async" here.
+ * If necessary, "go async" here (at the end).
*/
void
smb2_lease_acquire(smb_request_t *sr)
@@ -807,15 +806,18 @@ smb2_lease_acquire(smb_request_t *sr)
mutex_enter(&node->n_oplock.ol_mutex);
/*
- * Disallow downgrade
+ * MS-SMB2 3.3.5.9.8 and 3.3.5.9.11 Lease (V2) create contexts
*
- * Note that open with a lease is not allowed to turn off
- * any cache rights. If the client tries to "downgrade",
- * any bits, just return the existing lease cache bits.
+ * If the caching state requested in LeaseState of the (create ctx)
+ * is not a superset of Lease.LeaseState or if Lease.Breaking is TRUE,
+ * the server MUST NOT promote Lease.LeaseState. If the lease state
+ * requested is a superset of Lease.LeaseState and Lease.Breaking is
+ * FALSE, the server MUST request promotion of the lease state from
+ * the underlying object store to the new caching state.
*/
have = lease->ls_state & CACHE_RWH;
want = op->op_oplock_state & CACHE_RWH;
- if ((have & ~want) != 0) {
+ if ((have & ~want) != 0 || lease->ls_breaking) {
op->op_oplock_state = have |
OPLOCK_LEVEL_GRANULAR;
goto done;
@@ -840,9 +842,7 @@ smb2_lease_acquire(smb_request_t *sr)
* Try exclusive (request is RW or RWH)
*/
if ((op->op_oplock_state & WRITE_CACHING) != 0) {
- want = op->op_oplock_state & CACHE_RWH;
- if (have == want)
- goto done;
+ /* Alread checked (want & ~have) */
status = smb_oplock_request_LH(sr, ofile,
&op->op_oplock_state);
@@ -881,7 +881,7 @@ smb2_lease_acquire(smb_request_t *sr)
*/
if ((op->op_oplock_state & HANDLE_CACHING) != 0) {
want = op->op_oplock_state & CACHE_RWH;
- if (have == want)
+ if ((want & ~have) == 0)
goto done;
status = smb_oplock_request_LH(sr, ofile,
@@ -907,7 +907,7 @@ smb2_lease_acquire(smb_request_t *sr)
*/
if ((op->op_oplock_state & READ_CACHING) != 0) {
want = op->op_oplock_state & CACHE_RWH;
- if (have == want)
+ if ((want & ~have) == 0)
goto done;
status = smb_oplock_request_LH(sr, ofile,
@@ -945,11 +945,13 @@ done:
* this has to be using granular oplocks.
*/
if (NewGrant) {
- ofile->f_oplock.og_state = op->op_oplock_state;
- ofile->f_oplock.og_breaking = 0;
+ 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;
lease->ls_oplock_ofile = ofile;
- lease->ls_state = op->op_oplock_state;
+ lease->ls_state = ofile->f_oplock.og_state;
+ lease->ls_breakto = ofile->f_oplock.og_breakto;
lease->ls_breaking = B_FALSE;
lease->ls_epoch++;
@@ -988,6 +990,11 @@ done:
* This ofile has a lease and is about to close.
* Called by smb_ofile_close when there's a lease.
*
+ * Note that a client may close an ofile in response to an
+ * oplock break or lease break intead of doing an Ack break,
+ * so this must wake anything that might be waiting on an ack
+ * when the last close of a lease happens.
+ *
* With leases, just one ofile on a lease owns the oplock.
* If an ofile with a lease is closed and it's the one that
* owns the oplock, try to move the oplock to another ofile
@@ -1009,6 +1016,12 @@ smb2_lease_ofile_close(smb_ofile_t *ofile)
ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
+#ifdef DEBUG
+ FOREACH_NODE_OFILE(node, o) {
+ DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
+ }
+#endif
+
/*
* If this ofile was not the oplock owner for this lease,
* we can leave things as they are.
@@ -1077,7 +1090,8 @@ smb2_lease_ofile_close(smb_ofile_t *ofile)
* Wakeup ACK waiters too.
*/
lease->ls_state = 0;
- lease->ls_breaking = 0;
+ lease->ls_breakto = 0;
+ lease->ls_breaking = B_FALSE;
cv_broadcast(&lease->ls_ack_cv);
lease->ls_oplock_ofile = NULL;