summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2016-04-19 01:30:04 -0400
committerGordon Ross <gwr@nexenta.com>2019-06-07 23:01:40 -0400
commit6f58980a389cc62f07e5f2673629d9e9a209c2de (patch)
treecb89c34f98b3dec1e68a5898fbf973d10bbf692e
parent811599a462e8920d70cf548f4002182d3c222d13 (diff)
downloadillumos-joyent-6f58980a389cc62f07e5f2673629d9e9a209c2de.tar.gz
11012 SMB resilient handle lock replay
Reviewed by: Matt Barden <matt.barden@nexenta.com> Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com> Approved by: Garrett D'Amore <garrett@damore.org>
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_lock.c120
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_ofile.c1
-rw-r--r--usr/src/uts/common/smbsrv/smb_ktypes.h9
3 files changed, 128 insertions, 2 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lock.c b/usr/src/uts/common/fs/smbsrv/smb2_lock.c
index 91f57dade9..dc888b9e8c 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_lock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_lock.c
@@ -19,6 +19,12 @@
#include <smbsrv/smb2_kproto.h>
+/*
+ * [MS-SMB2] 2.2.26 LockSequenceIndex, LockSequenceNumber.
+ */
+#define SMB2_LSN_SHIFT 4
+#define SMB2_LSN_MASK 0xf
+
typedef struct SMB2_LOCK_ELEMENT {
uint64_t Offset;
uint64_t Length;
@@ -30,6 +36,9 @@ static uint32_t smb2_unlock(smb_request_t *);
static uint32_t smb2_locks(smb_request_t *);
static smb_sdrc_t smb2_lock_async(smb_request_t *);
+static boolean_t smb2_lock_chk_lockseq(smb_ofile_t *, uint32_t);
+static void smb2_lock_set_lockseq(smb_ofile_t *, uint32_t);
+
/*
* This is a somewhat arbitrary sanity limit on the length of the
* SMB2_LOCK_ELEMENT array. It usually has length one or two.
@@ -79,6 +88,26 @@ smb2_lock(smb_request_t *sr)
}
/*
+ * Check the LockSequence to determine whether a previous
+ * lock request succeeded, but the client disconnected
+ * (retaining a durable or resilient handle). If so, this
+ * is a lock "replay". We'll find the lock sequence here
+ * and return success without processing the lock again.
+ */
+ if (sr->session->dialect < SMB_VERS_2_1)
+ LockSequence = 0;
+ if ((sr->session->dialect == SMB_VERS_2_1) &&
+ sr->fid_ofile->dh_vers != SMB2_RESILIENT)
+ LockSequence = 0;
+ /* dialect 3.0 or later can always use LockSequence */
+
+ if (LockSequence != 0 &&
+ smb2_lock_chk_lockseq(sr->fid_ofile, LockSequence)) {
+ status = NT_STATUS_SUCCESS;
+ goto done;
+ }
+
+ /*
* Parse the array of SMB2_LOCK_ELEMENT structs.
* This array is free'd in smb_srm_fini.
*/
@@ -127,6 +156,7 @@ errout:
/*
* Encode SMB2 Lock reply (sync)
*/
+done:
(void) smb_mbc_encodef(
&sr->reply, "w..",
4); /* StructSize w */
@@ -160,7 +190,9 @@ smb2_unlock(smb_request_t *sr)
if (status != 0)
break;
}
- (void) LockSequence; /* todo */
+ if (status == 0 && LockSequence != 0) {
+ smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
+ }
return (status);
}
@@ -174,6 +206,7 @@ smb2_locks(smb_request_t *sr)
lock_elem_t *lk;
lock_elem_t *lvec = sr->arg.lock.lvec;
uint32_t LockCount = sr->arg.lock.lcnt;
+ uint32_t LockSequence = sr->arg.lock.lseq;
uint32_t i;
uint32_t ltype;
uint32_t pid = 0; /* SMB2 ignores lock PIDs */
@@ -236,6 +269,8 @@ end_loop:
lk->Offset, lk->Length, pid);
}
}
+ if (status == 0 && LockSequence != 0)
+ smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
return (status);
}
@@ -249,6 +284,7 @@ smb2_lock_async(smb_request_t *sr)
{
lock_elem_t *lk = sr->arg.lock.lvec;
uint32_t LockCount = sr->arg.lock.lcnt;
+ uint32_t LockSequence = sr->arg.lock.lseq;
uint32_t status;
uint32_t ltype;
uint32_t pid = 0; /* SMB2 ignores lock PIDs */
@@ -284,6 +320,9 @@ errout:
return (SDRC_SUCCESS);
}
+ if (LockSequence != 0)
+ smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
+
/*
* SMB2 Lock reply (async)
*/
@@ -293,3 +332,82 @@ errout:
/* reserved .. */
return (SDRC_SUCCESS);
}
+
+/*
+ * Check whether we've stored a given LockSequence
+ *
+ * [MS-SMB2] 3.3.5.14
+ *
+ * The server verifies the LockSequence by performing the following steps:
+ *
+ * 1. The server MUST use LockSequenceIndex as an index into the
+ * Open.LockSequenceArray in order to locate the sequence number entry.
+ * If the index exceeds the maximum extent of the Open.LockSequenceArray,
+ * or LockSequenceIndex is 0, or if the sequence number entry is empty,
+ * the server MUST skip step 2 and continue lock/unlock processing.
+ *
+ * 2. The server MUST compare LockSequenceNumber to the SequenceNumber of
+ * the entry located in step 1. If the sequence numbers are equal, the
+ * server MUST complete the lock/unlock request with success. Otherwise,
+ * the server MUST reset the entry value to empty and continue lock/unlock
+ * processing.
+ */
+boolean_t
+smb2_lock_chk_lockseq(smb_ofile_t *ofile, uint32_t lockseq)
+{
+ uint32_t lsi;
+ uint8_t lsn;
+ boolean_t rv;
+
+ /*
+ * LockSequenceNumber is the low four bits.
+ * LockSequenceIndex is the remaining 28 bits.
+ * valid range is 1..64, which we convert to an
+ * array index in the range 0..63
+ */
+ lsn = lockseq & SMB2_LSN_MASK;
+ lsi = (lockseq >> SMB2_LSN_SHIFT);
+ if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX)
+ return (B_FALSE);
+ --lsi;
+
+ mutex_enter(&ofile->f_mutex);
+
+ if (ofile->f_lock_seq[lsi] == lsn) {
+ rv = B_TRUE;
+ } else {
+ ofile->f_lock_seq[lsi] = (uint8_t)-1; /* "Empty" */
+ rv = B_FALSE;
+ }
+
+ mutex_exit(&ofile->f_mutex);
+
+ return (rv);
+}
+
+static void
+smb2_lock_set_lockseq(smb_ofile_t *ofile, uint32_t lockseq)
+{
+ uint32_t lsi;
+ uint8_t lsn;
+
+ /*
+ * LockSequenceNumber is the low four bits.
+ * LockSequenceIndex is the remaining 28 bits.
+ * valid range is 1..64, which we convert to an
+ * array index in the range 0..63
+ */
+ lsn = lockseq & SMB2_LSN_MASK;
+ lsi = (lockseq >> SMB2_LSN_SHIFT);
+ if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX) {
+ cmn_err(CE_NOTE, "smb2_lock_set_lockseq, index=%u", lsi);
+ return;
+ }
+ --lsi;
+
+ mutex_enter(&ofile->f_mutex);
+
+ ofile->f_lock_seq[lsi] = lsn;
+
+ mutex_exit(&ofile->f_mutex);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
index e7b07732d1..9521d6f277 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
@@ -342,6 +342,7 @@ smb_ofile_open(
crhold(of->f_cr);
of->f_server = tree->t_server;
of->f_session = tree->t_session;
+ (void) memset(of->f_lock_seq, -1, SMB_OFILE_LSEQ_MAX);
/*
* grab a ref for of->f_user and of->f_tree
diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h
index 2bf62f6242..edafb04ddc 100644
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h
@@ -1325,6 +1325,12 @@ typedef struct smb_opipe {
#define SMB_OFILE_VALID(p) \
ASSERT((p != NULL) && ((p)->f_magic == SMB_OFILE_MAGIC))
+/*
+ * This is the size of the per-handle "Lock Sequence" array.
+ * See LockSequenceIndex in [MS-SMB2] 2.2.26, and smb2_lock.c
+ */
+#define SMB_OFILE_LSEQ_MAX 64
+
/* {arg_open,ofile}->dh_vers values */
typedef enum {
SMB2_NOT_DURABLE = 0,
@@ -1387,7 +1393,6 @@ typedef struct smb_ofile {
pid_t f_pid;
smb_attr_t f_pending_attr;
boolean_t f_written;
- char f_quota_resume[SMB_SID_STRSZ];
smb_oplock_grant_t f_oplock_grant;
smb_notify_t f_notify;
@@ -1396,6 +1401,8 @@ typedef struct smb_ofile {
hrtime_t dh_expire_time; /* time the handle expires */
boolean_t dh_persist;
uint8_t dh_create_guid[16];
+ char f_quota_resume[SMB_SID_STRSZ];
+ uint8_t f_lock_seq[SMB_OFILE_LSEQ_MAX];
} smb_ofile_t;
typedef struct smb_fileinfo {