Latest leases patchset - getting there !
Stefan (metze) Metzmacher
metze at samba.org
Tue Nov 11 09:35:21 MST 2014
Hi Jeremy,
here're 3 patch files.
ok12.diff.txt - this is ready to be pushed to master
leases12.diff.txt - contains the last patchset with the undo patch
squashed.
ontop12.diff.txt - contains fixes for "kernel oplocks = yes" and "level2
oplocks = no"
I'll squash them in the next round...
So please push ok12.diff.txt and make sure the result of all patches
still works for you.
metze
Am 07.11.2014 um 21:21 schrieb Jeremy Allison:
> On Fri, Nov 07, 2014 at 10:03:50AM -0800, Jeremy Allison wrote:
>> On Fri, Nov 07, 2014 at 06:08:38PM +0100, Stefan (metze) Metzmacher wrote:
>>> Hi Jeremy,
>>>
>>> here're 2 patch files.
>>>
>>> ok.diff.txt contains the changes from my
>>> https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master3-leases-ok
>>> branch, they're ready for master and should not cause much trouble when
>>> you rebase the rest.
>>
>> OK - pushed !
>>
>>> tmp.diff.txt contains the grant_fsp_oplock_type() and brl_num_read changes
>>> Just tell me if they look ok for you, don't push them yet.
>>
>> Yes, they look ok - I looked over the logic carefully,
>> plus it still passes make test :-).
>>
>>> undo.diff.txt revert some of tmp.diff.txt so that the rest will
>>> rebase correctly.
>>
>> Thanks !
>>
>>> If you're fine with tmp.diff.txt I'll prepare a complete rebased patchset.
>>
>> Sure - send it over to me once you're finished.
>>
>> I'll also rebase this afternoon on top of ok+tmp+undo
>> just to make sure all the leases code still works :-).
>
> Ok, here is the rebased leases patchset on top
> of ok+tmp+undo (for anyone who wants to follow
> along :-).
>
> Doing a dirdiff in source3 between the original
> leases patchset and this one only shows the
> DEBUG message difference in source3/locking/brlock.c.
>
> Cheers,
>
> Jeremy.
>
-------------- next part --------------
From 576055815b09508c7bf59c5b59b3bdf776af4f28 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 23:34:14 +0200
Subject: [PATCH 1/5] s3:smbd: break oplocks to none with FILE_OVERWRITE
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
source3/smbd/open.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index ccea1e9..26244f6 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1431,6 +1431,7 @@ static bool delay_for_oplock(files_struct *fsp,
switch (create_disposition) {
case FILE_SUPERSEDE:
+ case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
break_to = NO_OPLOCK;
break;
--
1.9.1
From 6800229d6ec6ba29b3365786d66d352a454ccfd4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:27:09 -0700
Subject: [PATCH 2/5] s3:smbd: move all oplock granting code to
grant_fsp_oplock_type()
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/open.c | 73 ++++++++++++++++++++++++++++++++---------------------
1 file changed, 44 insertions(+), 29 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 26244f6..945850c 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1490,38 +1490,37 @@ static bool file_has_brlocks(files_struct *fsp)
return (brl_num_locks(br_lck) > 0);
}
-static void grant_fsp_oplock_type(files_struct *fsp,
- struct share_mode_lock *lck,
- int oplock_request)
+static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
+ struct files_struct *fsp,
+ struct share_mode_lock *lck,
+ int oplock_request)
{
bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
lp_level2_oplocks(SNUM(fsp->conn));
bool got_level2_oplock, got_a_none_oplock;
uint32_t i;
+ bool ok;
+ NTSTATUS status;
/* Start by granting what the client asked for,
but ensure no SAMBA_PRIVATE bits can be set. */
fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
+ if (fsp->oplock_type == NO_OPLOCK) {
+ goto type_selected;
+ }
+
if (oplock_request & INTERNAL_OPEN_ONLY) {
/* No oplocks on internal open. */
fsp->oplock_type = NO_OPLOCK;
- DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
- fsp->oplock_type, fsp_str_dbg(fsp)));
- return;
+ goto type_selected;
}
if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
fsp_str_dbg(fsp)));
fsp->oplock_type = NO_OPLOCK;
- }
-
- if (is_stat_open(fsp->access_mask)) {
- /* Leave the value already set. */
- DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
- fsp->oplock_type, fsp_str_dbg(fsp)));
- return;
+ goto type_selected;
}
got_level2_oplock = false;
@@ -1555,6 +1554,23 @@ static void grant_fsp_oplock_type(files_struct *fsp,
*/
if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
fsp->oplock_type = NO_OPLOCK;
+ goto type_selected;
+ }
+
+ type_selected:
+ status = set_file_oplock(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Could not get the kernel oplock
+ */
+ fsp->oplock_type = NO_OPLOCK;
+ }
+
+ ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
+ req ? req->mid : 0,
+ fsp->oplock_type);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
}
if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
@@ -1572,6 +1588,8 @@ static void grant_fsp_oplock_type(files_struct *fsp,
DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
fsp->oplock_type, fsp_str_dbg(fsp)));
+
+ return NT_STATUS_OK;
}
static bool request_timed_out(struct timeval request_time,
@@ -2739,8 +2757,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
}
}
- grant_fsp_oplock_type(fsp, lck, oplock_request);
-
/*
* We have the share entry *locked*.....
*/
@@ -2800,9 +2816,18 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
}
if (file_existed) {
- /* stat opens on existing files don't get oplocks. */
+ /*
+ * stat opens on existing files don't get oplocks.
+ *
+ * Note that we check for stat open on the *open_access_mask*,
+ * i.e. the access mask we actually used to do the open,
+ * not the one the client asked for (which is in
+ * fsp->access_mask). This is due to the fact that
+ * FILE_OVERWRITE and FILE_OVERWRITE_IF add in O_TRUNC,
+ * which adds FILE_WRITE_DATA to open_access_mask.
+ */
if (is_stat_open(open_access_mask)) {
- fsp->oplock_type = NO_OPLOCK;
+ oplock_request = NO_OPLOCK;
}
}
@@ -2824,21 +2849,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* Setup the oplock info in both the shared memory and
* file structs.
*/
-
- status = set_file_oplock(fsp);
+ status = grant_fsp_oplock_type(req, fsp, lck, oplock_request);
if (!NT_STATUS_IS_OK(status)) {
- /*
- * Could not get the kernel oplock
- */
- fsp->oplock_type = NO_OPLOCK;
- }
-
- if (!set_share_mode(lck, fsp, get_current_uid(conn),
- req ? req->mid : 0,
- fsp->oplock_type)) {
TALLOC_FREE(lck);
fd_close(fsp);
- return NT_STATUS_NO_MEMORY;
+ return status;
}
/* Handle strange delete on close create semantics. */
--
1.9.1
From e43b5f28a7f7aae3fe8e0d9eb0d9bddcb471c62c Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 22 Oct 2014 17:53:01 -0700
Subject: [PATCH 3/5] s3:smbd: Don't set fsp->oplock_type before we've granted
any oplocks.
It's not needed, and may lead to unexpected side effects.
grant_fsp_oplock_type() is the only place to touch this.
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
source3/smbd/open.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 945850c..28ab434 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -2423,9 +2423,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* the open is done. */
fsp->posix_open = posix_open;
- /* Ensure no SAMBA_PRIVATE bits can be set. */
- fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
-
if (timeval_is_zero(&request_time)) {
request_time = fsp->open_time;
}
--
1.9.1
From b0dc1c83249542069966dec92d3cd87a3412d330 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:27:09 -0700
Subject: [PATCH 4/5] s3:locking: convert brl_have_read field to brl_num_read.
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
source3/locking/brlock.c | 123 +++++++++++++++++++++--------------------------
source3/locking/proto.h | 6 +--
source3/smbd/open.c | 15 ++----
source3/smbd/oplock.c | 115 +++++++++++++++++++++++++++-----------------
source3/smbd/proto.h | 2 +
5 files changed, 135 insertions(+), 126 deletions(-)
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
index 0bed9f9..6c73c72 100644
--- a/source3/locking/brlock.c
+++ b/source3/locking/brlock.c
@@ -47,7 +47,7 @@ struct byte_range_lock {
struct files_struct *fsp;
unsigned int num_locks;
bool modified;
- bool have_read_oplocks;
+ uint32_t num_read_oplocks;
struct lock_struct *lock_data;
struct db_record *record;
};
@@ -82,18 +82,18 @@ struct files_struct *brl_fsp(struct byte_range_lock *brl)
return brl->fsp;
}
-bool brl_have_read_oplocks(const struct byte_range_lock *brl)
+uint32_t brl_num_read_oplocks(const struct byte_range_lock *brl)
{
- return brl->have_read_oplocks;
+ return brl->num_read_oplocks;
}
-void brl_set_have_read_oplocks(struct byte_range_lock *brl,
- bool have_read_oplocks)
+void brl_set_num_read_oplocks(struct byte_range_lock *brl,
+ uint32_t num_read_oplocks)
{
- DEBUG(10, ("Setting have_read_oplocks to %s\n",
- have_read_oplocks ? "true" : "false"));
+ DEBUG(10, ("Setting num_read_oplocks to %"PRIu32"\n",
+ num_read_oplocks));
SMB_ASSERT(brl->record != NULL); /* otherwise we're readonly */
- brl->have_read_oplocks = have_read_oplocks;
+ brl->num_read_oplocks = num_read_oplocks;
brl->modified = true;
}
@@ -1850,7 +1850,6 @@ int brl_forall(void (*fn)(struct file_id id, struct server_id pid,
static void byte_range_lock_flush(struct byte_range_lock *br_lck)
{
- size_t data_len;
unsigned i;
struct lock_struct *locks = br_lck->lock_data;
@@ -1874,15 +1873,7 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck)
}
}
- data_len = br_lck->num_locks * sizeof(struct lock_struct);
-
- if (br_lck->have_read_oplocks) {
- data_len += 1;
- }
-
- DEBUG(10, ("data_len=%d\n", (int)data_len));
-
- if (data_len == 0) {
+ if ((br_lck->num_locks == 0) && (br_lck->num_read_oplocks == 0)) {
/* No locks - delete this entry. */
NTSTATUS status = dbwrap_record_delete(br_lck->record);
if (!NT_STATUS_IS_OK(status)) {
@@ -1891,19 +1882,20 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck)
smb_panic("Could not delete byte range lock entry");
}
} else {
+ size_t lock_len, data_len;
TDB_DATA data;
NTSTATUS status;
+ lock_len = br_lck->num_locks * sizeof(struct lock_struct);
+ data_len = lock_len + sizeof(br_lck->num_read_oplocks);
+
data.dsize = data_len;
data.dptr = talloc_array(talloc_tos(), uint8_t, data_len);
SMB_ASSERT(data.dptr != NULL);
- memcpy(data.dptr, br_lck->lock_data,
- br_lck->num_locks * sizeof(struct lock_struct));
-
- if (br_lck->have_read_oplocks) {
- data.dptr[data_len-1] = 1;
- }
+ memcpy(data.dptr, br_lck->lock_data, lock_len);
+ memcpy(data.dptr + lock_len, &br_lck->num_read_oplocks,
+ sizeof(br_lck->num_read_oplocks));
status = dbwrap_record_store(br_lck->record, data, TDB_REPLACE);
TALLOC_FREE(data.dptr);
@@ -1926,6 +1918,32 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck)
return 0;
}
+static bool brl_parse_data(struct byte_range_lock *br_lck, TDB_DATA data)
+{
+ size_t data_len;
+
+ if (data.dsize == 0) {
+ return true;
+ }
+ if (data.dsize % sizeof(struct lock_struct) !=
+ sizeof(br_lck->num_read_oplocks)) {
+ DEBUG(1, ("Invalid data size: %u\n", (unsigned)data.dsize));
+ return false;
+ }
+
+ br_lck->num_locks = data.dsize / sizeof(struct lock_struct);
+ data_len = br_lck->num_locks * sizeof(struct lock_struct);
+
+ br_lck->lock_data = talloc_memdup(br_lck, data.dptr, data_len);
+ if (br_lck->lock_data == NULL) {
+ DEBUG(1, ("talloc_memdup failed\n"));
+ return false;
+ }
+ memcpy(&br_lck->num_read_oplocks, data.dptr + data_len,
+ sizeof(br_lck->num_read_oplocks));
+ return true;
+}
+
/*******************************************************************
Fetch a set of byte range lock data from the database.
Leave the record locked.
@@ -1935,16 +1953,14 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck)
struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp)
{
TDB_DATA key, data;
- struct byte_range_lock *br_lck = talloc(mem_ctx, struct byte_range_lock);
+ struct byte_range_lock *br_lck;
+ br_lck = talloc_zero(mem_ctx, struct byte_range_lock);
if (br_lck == NULL) {
return NULL;
}
br_lck->fsp = fsp;
- br_lck->num_locks = 0;
- br_lck->have_read_oplocks = false;
- br_lck->modified = False;
key.dptr = (uint8 *)&fsp->file_id;
key.dsize = sizeof(struct file_id);
@@ -1959,30 +1975,12 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp)
data = dbwrap_record_get_value(br_lck->record);
- br_lck->lock_data = NULL;
-
- talloc_set_destructor(br_lck, byte_range_lock_destructor);
-
- br_lck->num_locks = data.dsize / sizeof(struct lock_struct);
-
- if (br_lck->num_locks != 0) {
- br_lck->lock_data = talloc_array(
- br_lck, struct lock_struct, br_lck->num_locks);
- if (br_lck->lock_data == NULL) {
- DEBUG(0, ("malloc failed\n"));
- TALLOC_FREE(br_lck);
- return NULL;
- }
-
- memcpy(br_lck->lock_data, data.dptr,
- talloc_get_size(br_lck->lock_data));
+ if (!brl_parse_data(br_lck, data)) {
+ TALLOC_FREE(br_lck);
+ return NULL;
}
- DEBUG(10, ("data.dsize=%d\n", (int)data.dsize));
-
- if ((data.dsize % sizeof(struct lock_struct)) == 1) {
- br_lck->have_read_oplocks = (data.dptr[data.dsize-1] == 1);
- }
+ talloc_set_destructor(br_lck, byte_range_lock_destructor);
if (DEBUGLEVEL >= 10) {
unsigned int i;
@@ -2008,28 +2006,19 @@ static void brl_get_locks_readonly_parser(TDB_DATA key, TDB_DATA data,
{
struct brl_get_locks_readonly_state *state =
(struct brl_get_locks_readonly_state *)private_data;
- struct byte_range_lock *br_lock;
+ struct byte_range_lock *br_lck;
- br_lock = talloc_pooled_object(
+ br_lck = talloc_pooled_object(
state->mem_ctx, struct byte_range_lock, 1, data.dsize);
- if (br_lock == NULL) {
+ if (br_lck == NULL) {
*state->br_lock = NULL;
return;
}
- br_lock->lock_data = (struct lock_struct *)talloc_memdup(
- br_lock, data.dptr, data.dsize);
- br_lock->num_locks = data.dsize / sizeof(struct lock_struct);
-
- if ((data.dsize % sizeof(struct lock_struct)) == 1) {
- br_lock->have_read_oplocks = (data.dptr[data.dsize-1] == 1);
- } else {
- br_lock->have_read_oplocks = false;
+ if (!brl_parse_data(br_lck, data)) {
+ *state->br_lock = NULL;
+ return;
}
-
- DEBUG(10, ("Got %d bytes, have_read_oplocks: %s\n", (int)data.dsize,
- br_lock->have_read_oplocks ? "true" : "false"));
-
- *state->br_lock = br_lock;
+ *state->br_lock = br_lck;
}
struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
@@ -2072,7 +2061,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
return NULL;
}
- br_lock->have_read_oplocks = false;
+ br_lock->num_read_oplocks = 0;
br_lock->num_locks = 0;
br_lock->lock_data = NULL;
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 44f3ba1..8eccff8 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -30,9 +30,9 @@ void brl_shutdown(void);
unsigned int brl_num_locks(const struct byte_range_lock *brl);
struct files_struct *brl_fsp(struct byte_range_lock *brl);
-bool brl_have_read_oplocks(const struct byte_range_lock *brl);
-void brl_set_have_read_oplocks(struct byte_range_lock *brl,
- bool have_read_oplocks);
+uint32_t brl_num_read_oplocks(const struct byte_range_lock *brl);
+void brl_set_num_read_oplocks(struct byte_range_lock *brl,
+ uint32_t num_read_oplocks);
NTSTATUS brl_lock_windows_default(struct byte_range_lock *br_lck,
struct lock_struct *plock,
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 28ab434..1952823 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1573,17 +1573,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
return NT_STATUS_NO_MEMORY;
}
- if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
- /*
- * We're the first level2 oplock. Indicate that in brlock.tdb.
- */
- struct byte_range_lock *brl;
-
- brl = brl_get_locks(talloc_tos(), fsp);
- if (brl != NULL) {
- brl_set_have_read_oplocks(brl, true);
- TALLOC_FREE(brl);
- }
+ ok = update_num_read_oplocks(fsp, lck);
+ if (!ok) {
+ del_share_mode(lck, fsp);
+ return NT_STATUS_INTERNAL_ERROR;
}
DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 7935092..aa99f68 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -148,6 +148,53 @@ static void downgrade_file_oplock(files_struct *fsp)
TALLOC_FREE(fsp->oplock_timeout);
}
+bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
+{
+ struct share_mode_data *d = lck->data;
+ struct byte_range_lock *br_lck;
+ uint32_t num_read_oplocks = 0;
+ uint32_t i;
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ /*
+ * If we're the only one, we don't need a brlock entry
+ */
+ SMB_ASSERT(d->num_share_modes == 1);
+ SMB_ASSERT(EXCLUSIVE_OPLOCK_TYPE(d->share_modes[0].op_type));
+ return true;
+ }
+
+ for (i=0; i<d->num_share_modes; i++) {
+ struct share_mode_entry *e = &d->share_modes[i];
+
+ if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
+ num_read_oplocks += 1;
+ continue;
+ }
+
+ if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
+ num_read_oplocks += 1;
+ continue;
+ }
+ }
+
+ br_lck = brl_get_locks_readonly(fsp);
+ if (br_lck == NULL) {
+ return false;
+ }
+ if (brl_num_read_oplocks(br_lck) == num_read_oplocks) {
+ return true;
+ }
+
+ br_lck = brl_get_locks(talloc_tos(), fsp);
+ if (br_lck == NULL) {
+ return false;
+ }
+ brl_set_num_read_oplocks(br_lck, num_read_oplocks);
+ TALLOC_FREE(br_lck);
+ return true;
+}
+
/****************************************************************************
Remove a file oplock. Copes with level II and exclusive.
Locks then unlocks the share mode lock. Client can decide to go directly
@@ -170,44 +217,6 @@ bool remove_oplock(files_struct *fsp)
return False;
}
- if (fsp->oplock_type == LEVEL_II_OPLOCK) {
-
- /*
- * If we're the only LEVEL_II holder, we have to remove the
- * have_read_oplocks from the brlock entry
- */
-
- struct share_mode_data *data = lck->data;
- uint32_t i, num_level2;
-
- num_level2 = 0;
- for (i=0; i<data->num_share_modes; i++) {
- if (data->share_modes[i].op_type == LEVEL_II_OPLOCK) {
- num_level2 += 1;
- }
- if (num_level2 > 1) {
- /*
- * No need to count them all...
- */
- break;
- }
- }
-
- if (num_level2 == 1) {
- /*
- * That's only us. We are dropping that level2 oplock,
- * so remove the brlock flag.
- */
- struct byte_range_lock *brl;
-
- brl = brl_get_locks(talloc_tos(), fsp);
- if (brl) {
- brl_set_have_read_oplocks(brl, false);
- TALLOC_FREE(brl);
- }
- }
- }
-
ret = remove_share_oplock(lck, fsp);
if (!ret) {
DEBUG(0,("remove_oplock: failed to remove share oplock for "
@@ -216,6 +225,15 @@ bool remove_oplock(files_struct *fsp)
file_id_string_tos(&fsp->file_id)));
}
release_file_oplock(fsp);
+
+ ret = update_num_read_oplocks(fsp, lck);
+ if (!ret) {
+ DEBUG(0, ("%s: update_num_read_oplocks failed for "
+ "file %s, %s, %s\n",
+ __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
+ file_id_string_tos(&fsp->file_id)));
+ }
+
TALLOC_FREE(lck);
return ret;
}
@@ -227,7 +245,6 @@ bool downgrade_oplock(files_struct *fsp)
{
bool ret;
struct share_mode_lock *lck;
- struct byte_range_lock *brl;
DEBUG(10, ("downgrade_oplock called for %s\n",
fsp_str_dbg(fsp)));
@@ -245,13 +262,14 @@ bool downgrade_oplock(files_struct *fsp)
fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
file_id_string_tos(&fsp->file_id)));
}
-
downgrade_file_oplock(fsp);
- brl = brl_get_locks(talloc_tos(), fsp);
- if (brl != NULL) {
- brl_set_have_read_oplocks(brl, true);
- TALLOC_FREE(brl);
+ ret = update_num_read_oplocks(fsp, lck);
+ if (!ret) {
+ DEBUG(0, ("%s: update_num_read_oplocks failed for "
+ "file %s, %s, %s\n",
+ __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
+ file_id_string_tos(&fsp->file_id)));
}
TALLOC_FREE(lck);
@@ -596,6 +614,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
struct tevent_immediate *im;
struct break_to_none_state *state;
struct byte_range_lock *brl;
+ uint32_t num_read_oplocks = 0;
/*
* If this file is level II oplocked then we need
@@ -613,7 +632,13 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
}
brl = brl_get_locks_readonly(fsp);
- if ((brl != NULL) && !brl_have_read_oplocks(brl)) {
+ if (brl != NULL) {
+ num_read_oplocks = brl_num_read_oplocks(brl);
+ }
+
+ DEBUG(10, ("num_read_oplocks = %"PRIu32"\n", num_read_oplocks));
+
+ if (num_read_oplocks == 0) {
DEBUG(10, ("No read oplocks around\n"));
return;
}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 68c2da2..1080895 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -647,6 +647,8 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
/* The following definitions come from smbd/oplock.c */
+bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck);
+
void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp);
NTSTATUS set_file_oplock(files_struct *fsp);
bool remove_oplock(files_struct *fsp);
--
1.9.1
From 0f0e3c937ef530f3f81fa79ba6bc53eac530a652 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 18:49:46 +0200
Subject: [PATCH 5/5] s3:smb2_break: First test for
NT_STATUS_INVALID_OPLOCK_PROTOCOL, then for in_oplock_level being reasonable
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
source3/smbd/smb2_break.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index 5c079ec..4492456 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -52,11 +52,6 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
in_oplock_level = CVAL(inbody, 0x02);
- if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
- in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
- return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
- }
-
/* 0x03 1 bytes reserved */
/* 0x04 4 bytes reserved */
in_file_id_persistent = BVAL(inbody, 0x08);
@@ -67,6 +62,17 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
}
+ /* Are we awaiting a break message ? */
+ if (in_fsp->oplock_timeout == NULL) {
+ return smbd_smb2_request_error(
+ req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
+ }
+
+ if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
+ in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
+ return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+ }
+
subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
req, in_fsp, in_oplock_level);
if (subreq == NULL) {
@@ -177,12 +183,6 @@ static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
fsp_str_dbg(fsp),
fsp_fnum_dbg(fsp)));
- /* Are we awaiting a break message ? */
- if (fsp->oplock_timeout == NULL) {
- tevent_req_nterror(req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
- return tevent_req_post(req, ev);
- }
-
if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
(break_to_none)) {
result = remove_oplock(fsp);
--
1.9.1
-------------- next part --------------
From 2e4cd4777cf2af03ff8c981810afe556a374d255 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 01/16] s3: smbd: Implementation of SMB2.1 leases.
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/include/smb.h | 1 +
source3/librpc/idl/open_files.idl | 30 +++
source3/librpc/idl/wscript_build | 1 +
source3/librpc/wscript_build | 7 +-
source3/locking/locking.c | 141 +++++++++-
source3/locking/proto.h | 11 +-
source3/smbd/durable.c | 28 +-
source3/smbd/files.c | 34 +++
source3/smbd/globals.h | 10 +-
source3/smbd/open.c | 543 ++++++++++++++++++++++++++++++--------
source3/smbd/oplock.c | 291 +++++++++++++++-----
source3/smbd/proto.h | 12 +
source3/smbd/server.c | 5 +
source3/smbd/smb2_break.c | 194 +++++++++++++-
source3/smbd/smb2_create.c | 151 ++++++++++-
source3/smbd/smb2_server.c | 25 ++
source3/utils/status.c | 2 +
source3/wscript_build | 6 +
18 files changed, 1292 insertions(+), 200 deletions(-)
diff --git a/source3/include/smb.h b/source3/include/smb.h
index aab4ff5..7bace88 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -577,6 +577,7 @@ enum remote_arch_types {RA_UNKNOWN, RA_WFWG, RA_OS2, RA_WIN95, RA_WINNT,
#define EXCLUSIVE_OPLOCK OPLOCK_EXCLUSIVE
#define BATCH_OPLOCK OPLOCK_BATCH
#define LEVEL_II_OPLOCK OPLOCK_LEVEL_II
+#define LEASE_OPLOCK 0x100
/* The following are Samba-private. */
#define INTERNAL_OPEN_ONLY 0x8
diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl
index 4278301..48e6bbe 100644
--- a/source3/librpc/idl/open_files.idl
+++ b/source3/librpc/idl/open_files.idl
@@ -3,6 +3,8 @@
import "server_id.idl";
import "security.idl";
import "file_id.idl";
+import "smb2_lease_struct.idl";
+import "misc.idl";
[
pointer_default(unique)
@@ -14,6 +16,7 @@ interface open_files
server_id pid;
hyper op_mid;
uint16 op_type;
+ uint32 lease_idx;
uint32 access_mask;
uint32 share_access;
uint32 private_options;
@@ -31,6 +34,31 @@ interface open_files
[skip] boolean8 stale;
} share_mode_entry;
+ typedef [public,bitmap8bit] bitmap {
+ SHARE_MODE_NO_CACHING = 0x00,
+ SHARE_MODE_READ_CACHING = 0x01,
+ SHARE_MODE_HANDLE_CACHING = 0x02,
+ SHARE_MODE_WRITE_CACHING = 0x04
+ } share_mode_caching;
+
+ typedef [public,flag(NDR_PAHEX)] struct {
+ GUID client_guid;
+ smb2_lease_key lease_key;
+ share_mode_caching current_state;
+ /*
+ * breaking_to_state indicates to which level
+ * the current state is broken when a conflicting
+ * request is processed. The calculation is as follows:
+ *
+ * breaking_to_state = current_state;
+ * breaking_to_state &= ~(remove_state)
+ * breaking_to_state &= allowed_shared_state
+ */
+ share_mode_caching breaking_to_state;
+ boolean8 breaking;
+ uint16 epoch;
+ } share_mode_oplock;
+
typedef [public] struct {
uint32 name_hash;
security_token *delete_nt_token;
@@ -43,6 +71,8 @@ interface open_files
[string,charset(UTF8)] char *stream_name;
uint32 num_share_modes;
[size_is(num_share_modes)] share_mode_entry share_modes[];
+ uint32 num_leases;
+ [size_is(num_leases)] share_mode_oplock leases[];
uint32 num_delete_tokens;
[size_is(num_delete_tokens)] delete_token delete_tokens[];
timespec old_write_time;
diff --git a/source3/librpc/idl/wscript_build b/source3/librpc/idl/wscript_build
index c38fe7b..f9b1bd7 100644
--- a/source3/librpc/idl/wscript_build
+++ b/source3/librpc/idl/wscript_build
@@ -8,6 +8,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
'''messaging.idl libnetapi.idl open_files.idl
perfcount.idl secrets.idl libnet_join.idl
smbXsrv.idl
+ leases_db.idl
''',
options='--includedir=%s --header --ndr-parser' % topinclude,
output_dir='../gen_ndr')
diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build
index 77ae048..5c83cf2 100644
--- a/source3/librpc/wscript_build
+++ b/source3/librpc/wscript_build
@@ -17,7 +17,7 @@ bld.SAMBA3_SUBSYSTEM('NDR_MESSAGING',
bld.SAMBA3_SUBSYSTEM('NDR_OPEN_FILES',
source='gen_ndr/ndr_open_files.c',
- public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY'
+ public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY NDR_SMB2_LEASE_STRUCT'
)
bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
@@ -25,6 +25,11 @@ bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
public_deps='ndr NDR_SERVER_ID NDR_SECURITY NDR_AUTH'
)
+bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB',
+ source='gen_ndr/ndr_leases_db.c',
+ public_deps='ndr'
+ )
+
bld.SAMBA3_SUBSYSTEM('NDR_SECRETS',
source='gen_ndr/ndr_secrets.c',
public_deps='ndr'
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index a320068..170fa1d 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -46,6 +46,7 @@
#include "messages.h"
#include "util_tdb.h"
#include "../librpc/gen_ndr/ndr_open_files.h"
+#include "locking/leases_db.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_LOCKING
@@ -608,6 +609,7 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e)
num_props += ((e->op_type == NO_OPLOCK) ? 1 : 0);
num_props += (EXCLUSIVE_OPLOCK_TYPE(e->op_type) ? 1 : 0);
num_props += (LEVEL_II_OPLOCK_TYPE(e->op_type) ? 1 : 0);
+ num_props += (e->op_type == LEASE_OPLOCK);
if ((num_props > 1) && serverid_exists(&e->pid)) {
smb_panic("Invalid share mode entry");
@@ -694,7 +696,8 @@ void remove_stale_share_mode_entries(struct share_mode_data *d)
}
bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
- uid_t uid, uint64_t mid, uint16 op_type)
+ uid_t uid, uint64_t mid, uint16 op_type,
+ uint32_t lease_idx)
{
struct share_mode_data *d = lck->data;
struct share_mode_entry *tmp, *e;
@@ -716,6 +719,7 @@ bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
e->access_mask = fsp->access_mask;
e->op_mid = mid;
e->op_type = op_type;
+ e->lease_idx = lease_idx;
e->time.tv_sec = fsp->open_time.tv_sec;
e->time.tv_usec = fsp->open_time.tv_usec;
e->id = fsp->file_id;
@@ -816,16 +820,70 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck,
bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
{
+ struct share_mode_data *d = lck->data;
struct share_mode_entry *e;
+ uint16_t op_type;
+ uint32_t lease_idx;
+ uint32_t i;
e = find_share_mode_entry(lck, fsp);
if (e == NULL) {
return False;
}
+ op_type = e->op_type;
e->op_type = NO_OPLOCK;
- lck->data->modified = True;
- return True;
+
+ d->modified = True;
+
+ if (op_type != LEASE_OPLOCK) {
+ return true;
+ }
+
+ /*
+ * This used to reference a lease. If there's no other one referencing
+ * it, remove it.
+ */
+
+ lease_idx = e->lease_idx;
+ e->lease_idx = UINT32_MAX;
+
+ for (i=0; i<d->num_share_modes; i++) {
+ if (d->share_modes[i].lease_idx == lease_idx) {
+ break;
+ }
+ }
+ if (i < d->num_share_modes) {
+ /*
+ * Found another one
+ */
+ return true;
+ }
+
+ d->num_leases -= 1;
+ d->leases[lease_idx] = d->leases[d->num_leases];
+
+ /*
+ * We changed the lease array. Fix all references to it.
+ */
+ for (i=0; i<d->num_share_modes; i++) {
+ if (d->share_modes[i].lease_idx == d->num_leases) {
+ d->share_modes[i].lease_idx = lease_idx;
+ }
+ }
+
+ {
+ NTSTATUS status;
+
+ status = leases_db_del(
+ &fsp->conn->sconn->client->connections->smb2.client.guid,
+ &fsp->lease->lease.lease_key);
+
+ DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+ nt_errstr(status)));
+ }
+
+ return true;
}
/*******************************************************************
@@ -846,6 +904,83 @@ bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
return True;
}
+/*
+ * "key" has been broken, it is referenced somewhere in "lck". If "fsp"
+ * applies to it, reset fsp->sent_oplock_break.
+ */
+
+bool fsp_lease_broken(struct share_mode_lock *lck,
+ struct file_id lck_id,
+ const struct smb2_lease_key *key,
+ files_struct *fsp, uint32_t new_lease_state)
+{
+ struct share_mode_data *d = lck->data;
+ struct share_mode_entry *e;
+
+ if (!file_id_equal(&fsp->file_id, &lck_id)) {
+ return false;
+ }
+ if (fsp->oplock_type != LEASE_OPLOCK) {
+ return false;
+ }
+
+ e = find_share_mode_entry(lck, fsp);
+ if (e == NULL) {
+ DEBUG(1, ("downgrade_lease_fsps: Could not find share mode "
+ "entry\n"));
+ return false;
+ }
+
+ if (!smb2_lease_key_equal(key, &d->leases[e->lease_idx].lease_key)) {
+ return false;
+ }
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ TALLOC_FREE(fsp->oplock_timeout);
+ fsp->lease->lease.lease_state = new_lease_state;
+ return true;
+}
+
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+ struct share_mode_lock *lck,
+ const struct smb2_lease_key *key,
+ uint32_t new_lease_state)
+{
+ struct share_mode_data *d = lck->data;
+ struct share_mode_oplock *l;
+ uint32_t i;
+
+ for (i=0; i<d->num_leases; i++) {
+ if (smb2_lease_key_equal(key, &d->leases[i].lease_key)) {
+ break;
+ }
+ }
+ if (i == d->num_leases) {
+ DEBUG(10, ("lease not found\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ l = &d->leases[i];
+
+ /*
+ * Can't upgrade anything: l->current_state must be a strict bitwise
+ * superset of new_lease_state
+ */
+
+ if ((new_lease_state & l->current_state) != new_lease_state) {
+ DEBUG(10, ("Attempt to upgrade from %d to %d\n",
+ (int)l->current_state, (int)new_lease_state));
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ l->current_state = new_lease_state;
+ l->breaking_to_state = 0;
+ l->breaking = 0;
+
+ d->modified = true;
+
+ return NT_STATUS_OK;
+}
+
/****************************************************************************
Adds a delete on close token.
****************************************************************************/
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 8eccff8..94e9b8f 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -167,13 +167,22 @@ void get_file_infos(struct file_id id,
bool is_valid_share_mode_entry(const struct share_mode_entry *e);
bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx);
bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
- uid_t uid, uint64_t mid, uint16 op_type);
+ uid_t uid, uint64_t mid, uint16 op_type,
+ uint32_t lease_idx);
void remove_stale_share_mode_entries(struct share_mode_data *d);
bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp);
bool mark_share_mode_disconnected(struct share_mode_lock *lck,
struct files_struct *fsp);
bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
+bool fsp_lease_broken(struct share_mode_lock *lck,
+ struct file_id lck_id,
+ const struct smb2_lease_key *key,
+ files_struct *fsp, uint32_t new_lease_state);
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+ struct share_mode_lock *lck,
+ const struct smb2_lease_key *key,
+ uint32_t new_lease_state);
bool get_delete_on_close_token(struct share_mode_lock *lck,
uint32_t name_hash,
const struct security_token **pp_nt_tok,
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 9489cf1..660865d 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -168,7 +168,7 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
return NT_STATUS_INVALID_PARAMETER;
}
- if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
+ if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
return NT_STATUS_NOT_SUPPORTED;
}
@@ -724,6 +724,32 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
fsp->aio_write_behind = false;
fsp->oplock_type = e->op_type;
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ struct share_mode_oplock *o = &lck->data->leases[e->lease_idx];
+ struct smb2_lease_key key;
+
+ key.data[0] = o->lease_key.data[0];
+ key.data[1] = o->lease_key.data[1];
+
+ fsp->lease = find_fsp_lease(fsp, &key);
+
+ if (fsp->lease != NULL) {
+ fsp->lease->ref_count += 1;
+ } else {
+ fsp->lease = talloc_zero(fsp->conn->sconn,
+ struct fsp_lease);
+ if (fsp->lease == NULL) {
+ TALLOC_FREE(lck);
+ fsp_free(fsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+ fsp->lease->ref_count = 1;
+ fsp->lease->lease.lease_key = key;
+ fsp->lease->lease.lease_state = o->current_state;
+ fsp->lease->lease.lease_epoch = o->epoch;
+ }
+ }
+
fsp->initial_allocation_size = cookie.initial_allocation_size;
fsp->fh->position_information = cookie.position_information;
fsp->update_write_time_triggered = cookie.update_write_time_triggered;
diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index a9e8357..13d1138 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -387,6 +387,24 @@ files_struct *file_find_di_next(files_struct *start_fsp)
return NULL;
}
+struct files_struct *file_find_one_fsp_from_lease_key(
+ struct smbd_server_connection *sconn,
+ const struct smb2_lease_key *lease_key)
+{
+ struct files_struct *fsp;
+
+ for (fsp = sconn->files; fsp; fsp=fsp->next) {
+ if ((fsp->lease != NULL) &&
+ (fsp->lease->lease.lease_key.data[0] ==
+ lease_key->data[0]) &&
+ (fsp->lease->lease.lease_key.data[1] ==
+ lease_key->data[1])) {
+ return fsp;
+ }
+ }
+ return NULL;
+}
+
/****************************************************************************
Find any fsp open with a pathname below that of an already open path.
****************************************************************************/
@@ -472,6 +490,14 @@ void fsp_free(files_struct *fsp)
fsp->fh->ref_count--;
}
+ if (fsp->lease != NULL) {
+ if (fsp->lease->ref_count == 1) {
+ TALLOC_FREE(fsp->lease);
+ } else {
+ fsp->lease->ref_count--;
+ }
+ }
+
fsp->conn->num_files_open--;
/* this is paranoia, just in case someone tries to reuse the
@@ -740,3 +766,11 @@ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
smb_fname_str_dbg(fsp->fsp_name),
&fsp->name_hash);
}
+
+uint32_t fsp_lease_type(struct files_struct *fsp)
+{
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ return fsp->lease->lease.lease_state;
+ }
+ return map_oplock_to_lease_type(fsp->oplock_type);
+}
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 36e7f0f..f297ff7 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -250,6 +250,14 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
struct smbXsrv_tcon *tcon,
struct smbXsrv_open *op,
uint8_t oplock_level);
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
+ struct smbXsrv_session *session,
+ struct smbXsrv_tcon *tcon,
+ uint16_t new_epoch,
+ uint32_t lease_flags,
+ struct smb2_lease_key *lease_key,
+ uint32_t current_lease_state,
+ uint32_t new_lease_state);
NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
struct tevent_req *subreq,
@@ -298,7 +306,7 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
struct deferred_open_record;
/* SMB1 -> SMB2 glue. */
-void send_break_message_smb2(files_struct *fsp, int level);
+void send_break_message_smb2(files_struct *fsp, uint32_t break_to);
struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req);
bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
struct smb_request *req,
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 1952823..7ac8fd8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -35,6 +35,12 @@
#include "serverid.h"
#include "messages.h"
#include "source3/lib/dbwrap/dbwrap_watch.h"
+#include "locking/leases_db.h"
+
+static bool is_same_lease(const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ const struct smb2_lease *lease);
+
extern const struct generic_mapping file_generic_mapping;
@@ -1257,6 +1263,10 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
share_mode_entry_to_message(msg, exclusive);
/* Overload entry->op_type */
+ /*
+ * This is a cut from uint32 to uint16, but so far only the lower 3
+ * bits (LEASE_WRITE/HANDLE/READ are used anyway.
+ */
SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
status = messaging_send_buf(msg_ctx, exclusive->pid,
@@ -1376,31 +1386,28 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
static bool delay_for_oplock(files_struct *fsp,
int oplock_request,
+ const struct smb2_lease *lease,
struct share_mode_lock *lck,
bool have_sharing_violation,
uint32_t create_disposition)
{
struct share_mode_data *d = lck->data;
- struct share_mode_entry *entry;
uint32_t num_non_stat_opens = 0;
uint32_t i;
- uint16_t break_to;
+ bool have_broken = false;
+ bool will_overwrite;
- if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
+ if ((oplock_request & INTERNAL_OPEN_ONLY) ||
+ is_stat_open(fsp->access_mask)) {
return false;
}
+
for (i=0; i<d->num_share_modes; i++) {
struct share_mode_entry *e = &d->share_modes[i];
if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
continue;
}
num_non_stat_opens += 1;
-
- /*
- * We found the a non-stat open, which in the exclusive/batch
- * case will be inspected further down.
- */
- entry = e;
}
if (num_non_stat_opens == 0) {
/*
@@ -1408,24 +1415,31 @@ static bool delay_for_oplock(files_struct *fsp,
*/
return false;
}
- if (num_non_stat_opens != 1) {
- /*
- * More than one open around. There can't be any exclusive or
- * batch left, this is all level2.
- */
- return false;
+
+ if (have_sharing_violation) {
+ for (i=0; i<d->num_share_modes; i++) {
+ struct share_mode_entry *e = &d->share_modes[i];
+ uint32_t e_lease_type = get_lease_type(d, e);
+
+ if (!(e_lease_type & SMB2_LEASE_HANDLE)) {
+ continue;
+ }
+ if (is_same_lease(d, e, lease)) {
+ continue;
+ }
+ if (share_mode_stale_pid(d, i)) {
+ continue;
+ }
+ send_break_message(fsp->conn->sconn->msg_ctx, e,
+ e_lease_type & ~SMB2_LEASE_HANDLE);
+ have_broken = true;
+ }
}
- if (server_id_is_disconnected(&entry->pid)) {
- /*
- * TODO: clean up.
- * This could be achieved by sending a break message
- * to ourselves. Special considerations for files
- * with delete_on_close flag set!
- *
- * For now we keep it simple and do not
- * allow delete on close for durable handles.
- */
+ if (have_broken) {
+ return true;
+ }
+ if (have_sharing_violation) {
return false;
}
@@ -1433,50 +1447,67 @@ static bool delay_for_oplock(files_struct *fsp,
case FILE_SUPERSEDE:
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
- break_to = NO_OPLOCK;
+ will_overwrite = true;
break;
default:
- break_to = LEVEL_II_OPLOCK;
+ will_overwrite = false;
break;
}
- if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
- if (share_mode_stale_pid(d, 0)) {
- return false;
+ for (i=0; i<d->num_share_modes; i++) {
+ struct share_mode_entry *e = &d->share_modes[i];
+ uint32_t e_lease_type = get_lease_type(d, e);
+
+ DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
+ (unsigned)i, (unsigned)e_lease_type,
+ (unsigned)will_overwrite));
+
+ if (e_lease_type & SMB2_LEASE_WRITE) {
+ uint32_t break_to;
+
+ if (share_mode_stale_pid(d, i)) {
+ return false;
+ }
+ if ((e->op_type == LEASE_OPLOCK) &&
+ (lease != NULL) &&
+ smb2_lease_key_equal(
+ &lease->lease_key,
+ &d->leases[e->lease_idx].lease_key)) {
+ return false;
+ }
+
+ break_to = e_lease_type & ~SMB2_LEASE_WRITE;
+ if (will_overwrite) {
+ /*
+ * There's no H only lease that we could break
+ * to
+ */
+ break_to = SMB2_LEASE_NONE;
+ }
+
+ DEBUG(10, ("breaking SMB2_LEASE_WRITE to %d\n",
+ (int)break_to));
+ send_break_message(fsp->conn->sconn->msg_ctx, e,
+ break_to);
+ return true;
}
- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
- return true;
- }
- if (have_sharing_violation) {
- /*
- * Non-batch exclusive is not broken if we have a sharing
- * violation
- */
- return false;
- }
- if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
- (break_to == NO_OPLOCK)) {
- if (share_mode_stale_pid(d, 0)) {
- return false;
+
+ if (will_overwrite && (e_lease_type & SMB2_LEASE_READ)) {
+ if (share_mode_stale_pid(d, i)) {
+ continue;
+ }
+ DEBUG(10, ("breaking SMB2_LEASE_READ\n"));
+ send_break_message(fsp->conn->sconn->msg_ctx, e,
+ SMB2_LEASE_NONE);
+ /*
+ * This is an async break. No need to wait for a
+ * response.
+ */
+ continue;
}
- DEBUG(10, ("Asynchronously breaking level2 oplock for "
- "create_disposition=%u\n",
- (unsigned)create_disposition));
- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
- return false;
- }
- if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
- /*
- * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
- */
- return false;
- }
- if (share_mode_stale_pid(d, 0)) {
- return false;
}
- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
- return true;
+ return have_broken;
}
static bool file_has_brlocks(files_struct *fsp)
@@ -1490,74 +1521,302 @@ static bool file_has_brlocks(files_struct *fsp)
return (brl_num_locks(br_lck) > 0);
}
-static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
- struct files_struct *fsp,
- struct share_mode_lock *lck,
- int oplock_request)
+static int find_share_mode_oplock(struct share_mode_data *d,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *key)
{
- bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
- lp_level2_oplocks(SNUM(fsp->conn));
- bool got_level2_oplock, got_a_none_oplock;
uint32_t i;
- bool ok;
+
+ for (i=0; i<d->num_leases; i++) {
+ struct share_mode_oplock *l = &d->leases[i];
+ if (GUID_equal(client_guid, &l->client_guid) &&
+ smb2_lease_key_equal(key, &l->lease_key)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static bool is_same_lease(const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ const struct smb2_lease *lease)
+{
+ if (e->op_type != LEASE_OPLOCK) {
+ return false;
+ }
+ if (lease == NULL) {
+ return false;
+ }
+ return smb2_lease_key_equal(&d->leases[e->lease_idx].lease_key,
+ &lease->lease_key);
+}
+
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+ const struct smb2_lease_key *key)
+{
+ files_struct *fsp;
+
+ /*
+ * TODO: Measure how expensive this loop is with thousands of open
+ * handles...
+ */
+
+ for (fsp = file_find_di_first(new_fsp->conn->sconn, new_fsp->file_id);
+ fsp != NULL;
+ fsp = file_find_di_next(fsp)) {
+
+ if (fsp == new_fsp) {
+ continue;
+ }
+ if (fsp->oplock_type != LEASE_OPLOCK) {
+ continue;
+ }
+ if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) {
+ return fsp->lease;
+ }
+ }
+
+ return NULL;
+}
+
+static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
+ const struct smb2_lease *lease,
+ uint32_t *p_lease_idx,
+ uint32_t granted)
+{
+ const struct GUID *client_guid;
+ struct share_mode_oplock *o;
+ struct share_mode_oplock *tmp;
NTSTATUS status;
+ int idx;
+
+
+ /*
+ * TODO: in future we can have multiple connections...
+ */
+ client_guid = &fsp->conn->sconn->client->connections->smb2.client.guid;
+
+ idx = find_share_mode_oplock(d, client_guid, &lease->lease_key);
+
+ if (idx != -1) {
+
+ bool do_upgrade;
+ uint32_t existing, requested;
+
+ fsp->lease = find_fsp_lease(fsp, &lease->lease_key);
+ if (fsp->lease == NULL) {
+ DEBUG(1, ("Did not find existing lease for file %s\n",
+ fsp_str_dbg(fsp)));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ fsp->lease->ref_count += 1;
+
+ *p_lease_idx = idx;
+ o = &d->leases[idx];
+
+ /*
+ * Upgrade only if the requested lease is a strict upgrade.
+ */
+ existing = o->current_state;
+ requested = lease->lease_state;
+
+ /*
+ * Tricky: This test makes sure that "requested" is a
+ * strict bitwise superset of "existing".
+ */
+ do_upgrade = ((existing & requested) == existing);
+
+ /*
+ * Upgrade only if other leases don't prevent what was asked
+ * for.
+ */
+ do_upgrade &= (granted == requested);
+
+ DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+ "granted=%"PRIu32", do_upgrade=%d\n",
+ existing, requested, granted, (int)do_upgrade));
+
+ if (do_upgrade) {
+ o->current_state = granted;
+ }
+ fsp->lease->lease.lease_state = o->current_state;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Create new lease
+ */
+
+ tmp = talloc_realloc(d, d->leases, struct share_mode_oplock,
+ d->num_leases+1);
+ if (tmp == NULL) {
+ /*
+ * See [MS-SMB2]
+ */
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ d->leases = tmp;
+
+ fsp->lease = talloc(fsp->conn->sconn, struct fsp_lease);
+ if (fsp->lease == NULL) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ fsp->lease->ref_count = 1;
+ fsp->lease->lease = *lease;
+ fsp->lease->lease.lease_state = granted;
+ fsp->lease->lease.lease_epoch += 1;
+
+ *p_lease_idx = d->num_leases;
- /* Start by granting what the client asked for,
- but ensure no SAMBA_PRIVATE bits can be set. */
- fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
+ d->leases[d->num_leases] = (struct share_mode_oplock) {
+ .client_guid = *client_guid,
+ .lease_key = lease->lease_key,
+ .epoch = lease->lease_epoch,
+ .current_state = granted,
+ };
- if (fsp->oplock_type == NO_OPLOCK) {
- goto type_selected;
+ status = leases_db_add(client_guid, &lease->lease_key,
+ &fsp->file_id, fsp->fsp_name->base_name,
+ fsp->fsp_name->stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("%s: leases_db_add failed: %s\n", __func__,
+ nt_errstr(status)));
+ TALLOC_FREE(fsp->lease);
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
}
+ d->num_leases += 1;
+ d->modified = true;
+
+ return NT_STATUS_OK;
+
+}
+
+static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp,
+ struct share_mode_lock *lck,
+ int oplock_request,
+ uint32_t create_disposition,
+ struct smb2_lease *lease)
+{
+ struct share_mode_data *d = lck->data;
+ bool got_handle_lease, got_oplock;
+ uint32_t i;
+ uint32_t granted;
+ uint32_t lease_idx = UINT32_MAX;
+ NTSTATUS status;
+ bool ret;
+
if (oplock_request & INTERNAL_OPEN_ONLY) {
/* No oplocks on internal open. */
- fsp->oplock_type = NO_OPLOCK;
- goto type_selected;
+ oplock_request = NO_OPLOCK;
+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+ fsp->oplock_type, fsp_str_dbg(fsp)));
+ }
+
+ if (oplock_request == LEASE_OPLOCK) {
+ granted = lease->lease_state;
+
+ if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
+ DEBUG(10, ("No read or write lease requested\n"));
+ granted = SMB2_LEASE_NONE;
+ }
+ if (granted == SMB2_LEASE_WRITE) {
+ DEBUG(10, ("pure write lease requested\n"));
+ granted = SMB2_LEASE_NONE;
+ }
+ if (granted == (SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE)) {
+ DEBUG(10, ("write and handle lease requested\n"));
+ granted = SMB2_LEASE_NONE;
+ }
+ } else {
+ granted = map_oplock_to_lease_type(
+ oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
}
if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
fsp_str_dbg(fsp)));
- fsp->oplock_type = NO_OPLOCK;
- goto type_selected;
+ granted &= ~SMB2_LEASE_READ;
}
- got_level2_oplock = false;
- got_a_none_oplock = false;
+ got_handle_lease = false;
+ got_oplock = false;
+
+ for (i=0; i<d->num_share_modes; i++) {
+ struct share_mode_entry *e = &d->share_modes[i];
+ uint32_t e_lease_type;
- for (i=0; i<lck->data->num_share_modes; i++) {
- int op_type = lck->data->share_modes[i].op_type;
+ e_lease_type = get_lease_type(d, e);
- if (LEVEL_II_OPLOCK_TYPE(op_type)) {
- got_level2_oplock = true;
+ if ((granted & SMB2_LEASE_WRITE) &&
+ !is_same_lease(d, e, lease) &&
+ !share_mode_stale_pid(d, i)) {
+ /*
+ * Can grant only one writer
+ */
+ granted &= ~SMB2_LEASE_WRITE;
+ }
+
+ if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+ !share_mode_stale_pid(d, i)) {
+ got_handle_lease = true;
}
- if (op_type == NO_OPLOCK) {
- got_a_none_oplock = true;
+
+ if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
+ !share_mode_stale_pid(d, i)) {
+ got_oplock = true;
}
}
- /*
- * Match what was requested (fsp->oplock_type) with
- * what was found in the existing share modes.
- */
+ if (oplock_request == LEASE_OPLOCK) {
- if (got_level2_oplock || got_a_none_oplock) {
- if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ fsp->oplock_type = LEASE_OPLOCK;
+
+ if (got_oplock) {
+ granted &= SMB2_LEASE_READ;
+ }
+
+ status = grant_fsp_lease(fsp, lck->data, lease, &lease_idx,
+ granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+
+ }
+ lease->lease_state = d->leases[lease_idx].current_state;
+ DEBUG(10, ("lease_state=%d\n", lease->lease_state));
+ } else {
+ switch (granted) {
+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
+ fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
+ break;
+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
+ fsp->oplock_type = EXCLUSIVE_OPLOCK;
+ break;
+ case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
+ case SMB2_LEASE_READ:
fsp->oplock_type = LEVEL_II_OPLOCK;
+ break;
+ default:
+ fsp->oplock_type = NO_OPLOCK;
+ break;
}
- }
+ if (fsp->oplock_type == LEVEL_II_OPLOCK) {
+ bool allow_level2 =
+ (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ lp_level2_oplocks(SNUM(fsp->conn));
- /*
- * Don't grant level2 to clients that don't want them
- * or if we've turned them off.
- */
- if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
- fsp->oplock_type = NO_OPLOCK;
- goto type_selected;
+ if (!allow_level2) {
+ fsp->oplock_type = NO_OPLOCK;
+ }
+ }
+ if (got_handle_lease) {
+ fsp->oplock_type = NO_OPLOCK;
+ }
}
- type_selected:
+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+ fsp->oplock_type, fsp_str_dbg(fsp)));
+
status = set_file_oplock(fsp);
if (!NT_STATUS_IS_OK(status)) {
/*
@@ -1566,22 +1825,16 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
fsp->oplock_type = NO_OPLOCK;
}
- ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
+ if (!set_share_mode(lck, fsp, get_current_uid(fsp->conn),
req ? req->mid : 0,
- fsp->oplock_type);
- if (!ok) {
+ fsp->oplock_type, lease_idx)) {
return NT_STATUS_NO_MEMORY;
}
-
- ok = update_num_read_oplocks(fsp, lck);
- if (!ok) {
+ ret = update_num_read_oplocks(fsp, lck);
+ if (!ret) {
del_share_mode(lck, fsp);
return NT_STATUS_INTERNAL_ERROR;
}
-
- DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
- fsp->oplock_type, fsp_str_dbg(fsp)));
-
return NT_STATUS_OK;
}
@@ -2474,8 +2727,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
smb_panic("validate_oplock_types failed");
}
- if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
- schedule_defer_open(lck, fsp->file_id, request_time, req);
+ if (delay_for_oplock(fsp, 0, lease, lck, false,
+ create_disposition)) {
+ schedule_defer_open(lck, fsp->file_id, request_time,
+ req);
TALLOC_FREE(lck);
DEBUG(10, ("Sent oplock break request to kernel "
"oplock holder\n"));
@@ -2596,7 +2851,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
if ((req != NULL) &&
delay_for_oplock(
- fsp, oplock_request, lck,
+ fsp, oplock_request, lease, lck,
NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
create_disposition)) {
schedule_defer_open(lck, fsp->file_id, request_time, req);
@@ -2807,7 +3062,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
if (file_existed) {
/*
- * stat opens on existing files don't get oplocks.
+ * stat opens on existing files don't get oplocks or leases.
*
* Note that we check for stat open on the *open_access_mask*,
* i.e. the access mask we actually used to do the open,
@@ -2817,7 +3072,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* which adds FILE_WRITE_DATA to open_access_mask.
*/
if (is_stat_open(open_access_mask)) {
- oplock_request = NO_OPLOCK;
+ if (lease) {
+ lease->lease_state = SMB2_LEASE_NONE;
+ } else {
+ oplock_request = NO_OPLOCK;
+ }
}
}
@@ -2839,7 +3098,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* Setup the oplock info in both the shared memory and
* file structs.
*/
- status = grant_fsp_oplock_type(req, fsp, lck, oplock_request);
+
+ status = grant_fsp_oplock_type(req, fsp, lck, oplock_request,
+ create_disposition, lease);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(lck);
fd_close(fsp);
@@ -3337,7 +3598,7 @@ static NTSTATUS open_directory(connection_struct *conn,
}
if (!set_share_mode(lck, fsp, get_current_uid(conn),
- req ? req->mid : 0, NO_OPLOCK)) {
+ req ? req->mid : 0, NO_OPLOCK, UINT32_MAX)) {
TALLOC_FREE(lck);
fd_close(fsp);
file_free(req, fsp);
@@ -3822,6 +4083,48 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
}
/*
+ * If we already have a lease, it must match the new file id. [MS-SMB2]
+ * 3.3.5.9.8 speaks about INVALID_PARAMETER if an already used lease key is
+ * used for a different file name.
+ */
+
+struct lease_fname_match_state {
+ const struct smb_filename *fname;
+ bool match;
+};
+
+static void lease_fname_match_parser(
+ struct file_id id, const char *filename, const char *stream_name,
+ void *private_data)
+{
+ struct lease_fname_match_state *state =
+ (struct lease_fname_match_state *)private_data;
+
+ state->match =
+ strequal(filename, state->fname->base_name) &&
+ strequal(stream_name, state->fname->stream_name);
+}
+
+static bool lease_fname_match(struct smbd_server_connection *sconn,
+ struct smb2_lease_key *lease_key,
+ const struct smb_filename *fname)
+{
+ struct lease_fname_match_state state =
+ { .fname = fname, .match = true };
+ NTSTATUS status;
+
+ status = leases_db_parse(&sconn->client->connections->smb2.client.guid,
+ lease_key, lease_fname_match_parser, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Not found or error means okay: We can make the lease pass
+ */
+ return true;
+ }
+ return state.match;
+}
+
+/*
* Wrapper around open_file_ntcreate and open_directory
*/
@@ -3877,6 +4180,12 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
oplock_request |= INTERNAL_OPEN_ONLY;
}
+ if ((lease != NULL) &&
+ !lease_fname_match(req->sconn, &lease->lease_key, smb_fname)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
&& (access_mask & DELETE_ACCESS)
&& !is_ntfs_stream_smb_fname(smb_fname)) {
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index aa99f68..0a7ac17 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -116,8 +116,6 @@ static void release_file_oplock(files_struct *fsp)
flush_write_cache(fsp, SAMBA_OPLOCK_RELEASE_FLUSH);
delete_write_cache(fsp);
-
- TALLOC_FREE(fsp->oplock_timeout);
}
/****************************************************************************
@@ -144,8 +142,36 @@ static void downgrade_file_oplock(files_struct *fsp)
flush_write_cache(fsp, SAMBA_OPLOCK_RELEASE_FLUSH);
delete_write_cache(fsp);
+}
- TALLOC_FREE(fsp->oplock_timeout);
+uint32_t map_oplock_to_lease_type(uint16_t op_type)
+{
+ uint32_t ret;
+
+ switch(op_type) {
+ case BATCH_OPLOCK:
+ case BATCH_OPLOCK|EXCLUSIVE_OPLOCK:
+ ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE;
+ break;
+ case EXCLUSIVE_OPLOCK:
+ ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE;
+ break;
+ case LEVEL_II_OPLOCK:
+ ret = SMB2_LEASE_READ;
+ break;
+ default:
+ ret = SMB2_LEASE_NONE;
+ break;
+ }
+ return ret;
+}
+
+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e)
+{
+ if (e->op_type == LEASE_OPLOCK) {
+ return d->leases[e->lease_idx].current_state;
+ }
+ return map_oplock_to_lease_type(e->op_type);
}
bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
@@ -155,26 +181,12 @@ bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
uint32_t num_read_oplocks = 0;
uint32_t i;
- if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
- /*
- * If we're the only one, we don't need a brlock entry
- */
- SMB_ASSERT(d->num_share_modes == 1);
- SMB_ASSERT(EXCLUSIVE_OPLOCK_TYPE(d->share_modes[0].op_type));
- return true;
- }
-
for (i=0; i<d->num_share_modes; i++) {
struct share_mode_entry *e = &d->share_modes[i];
+ uint32_t e_lease_type = get_lease_type(d, e);
- if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
+ if (e_lease_type & SMB2_LEASE_READ) {
num_read_oplocks += 1;
- continue;
- }
-
- if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
- num_read_oplocks += 1;
- continue;
}
}
@@ -225,6 +237,7 @@ bool remove_oplock(files_struct *fsp)
file_id_string_tos(&fsp->file_id)));
}
release_file_oplock(fsp);
+ TALLOC_FREE(fsp->oplock_timeout);
ret = update_num_read_oplocks(fsp, lck);
if (!ret) {
@@ -262,13 +275,15 @@ bool downgrade_oplock(files_struct *fsp)
fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
file_id_string_tos(&fsp->file_id)));
}
+
downgrade_file_oplock(fsp);
+ TALLOC_FREE(fsp->oplock_timeout);
ret = update_num_read_oplocks(fsp, lck);
if (!ret) {
- DEBUG(0, ("%s: update_num_read_oplocks failed for "
- "file %s, %s, %s\n",
- __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
+ DEBUG(0,("%s: failed to downgrade share oplock "
+ "for file %s, %s, file_id %s\n",
+ __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
file_id_string_tos(&fsp->file_id)));
}
@@ -276,6 +291,64 @@ bool downgrade_oplock(files_struct *fsp)
return ret;
}
+struct downgrade_lease_fsps_state {
+ struct file_id id;
+ struct share_mode_lock *lck;
+ const struct smb2_lease_key *key;
+ uint32_t new_lease_state;
+ unsigned count;
+};
+
+static struct files_struct *downgrade_lease_fsps(struct files_struct *fsp,
+ void *private_data)
+{
+ struct downgrade_lease_fsps_state *state =
+ (struct downgrade_lease_fsps_state *)private_data;
+ bool downgraded;
+
+ downgraded = fsp_lease_broken(state->lck, state->id, state->key, fsp,
+ state->new_lease_state);
+ state->count += downgraded ? 1 : 0;
+ return NULL;
+}
+
+NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
+ const struct file_id id,
+ const struct smb2_lease_key *key,
+ uint32_t lease_state)
+{
+ struct share_mode_lock *lck;
+ NTSTATUS status;
+
+ DEBUG(10, ("%s: Downgrading %s to %x\n", __func__,
+ file_id_string_tos(&id), (unsigned)lease_state));
+
+ lck = get_existing_share_mode_lock(talloc_tos(), id);
+ if (lck == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ status = downgrade_share_lease(sconn, lck, key, lease_state);
+
+ /*
+ * This sucks. We have to reset fsp->sent_oplock_break on all fsps
+ * that reference this lease.
+ */
+ {
+ struct downgrade_lease_fsps_state state = {
+ .id = id, .lck = lck, .key = key,
+ .count = 0, .new_lease_state = lease_state
+ };
+
+ files_forall(sconn, downgrade_lease_fsps, &state);
+
+ /* Paranoia */
+ SMB_ASSERT(state.count > 0);
+ }
+
+ TALLOC_FREE(lck);
+ return status;
+}
+
/****************************************************************************
Set up an oplock break message.
****************************************************************************/
@@ -447,7 +520,6 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
{
struct share_mode_entry msg;
files_struct *fsp;
- bool break_to_level2 = False;
bool use_kernel;
struct smbd_server_connection *sconn =
talloc_get_type_abort(private_data,
@@ -488,23 +560,42 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
/*
* Nothing to do anymore
*/
+ DEBUG(10, ("fsp->sent_oplock_break = %d\n",
+ fsp->sent_oplock_break));
return;
}
- if (break_to == fsp->oplock_type) {
- DEBUG(3, ("Already downgraded oplock on %s: %s\n",
- file_id_string_tos(&fsp->file_id),
- fsp_str_dbg(fsp)));
- return;
+ if (!(global_client_caps & CAP_LEVEL_II_OPLOCKS)) {
+ DEBUG(10, ("client_caps without level2 oplocks\n"));
+ break_to &= ~SMB2_LEASE_READ;
}
use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
+ if (use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) {
+ DEBUG(10, ("Kernel oplocks don't allow level2\n"));
+ break_to &= ~SMB2_LEASE_READ;
+ }
- if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
- (break_to != NO_OPLOCK) &&
- !(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) &&
- lp_level2_oplocks(SNUM(fsp->conn))) {
- break_to_level2 = True;
+ if (!lp_level2_oplocks(SNUM(fsp->conn))) {
+ DEBUG(10, ("no level2 oplocks by config\n"));
+ break_to &= ~SMB2_LEASE_READ;
+ }
+
+ DEBUG(10, ("msg.op_type=%u, break_to=%u\n",
+ (unsigned)msg.op_type, (unsigned)break_to));
+
+ if (fsp->oplock_type == NO_OPLOCK) {
+ DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
+ file_id_string_tos(&fsp->file_id),
+ fsp_str_dbg(fsp)));
+ return;
+ }
+ if ((break_to & SMB2_LEASE_READ) &&
+ (fsp->oplock_type == LEVEL_II_OPLOCK)) {
+ DEBUG(3, ("Already downgraded oplock to level2 on %s: %s\n",
+ file_id_string_tos(&fsp->file_id),
+ fsp_str_dbg(fsp)));
+ return;
}
/* Need to wait before sending a break
@@ -514,21 +605,33 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
}
if (sconn->using_smb2) {
- send_break_message_smb2(fsp, break_to_level2 ?
- OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+ send_break_message_smb2(fsp, break_to);
} else {
- send_break_message_smb1(fsp, break_to_level2 ?
- OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+ send_break_message_smb1(fsp, (break_to & SMB2_LEASE_READ) ?
+ OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
}
- if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
+ if ((fsp_lease_type(fsp) == SMB2_LEASE_READ) &&
+ (break_to == SMB2_LEASE_NONE)) {
/*
* This is an async break without a reply and thus no timeout
*/
- remove_oplock(fsp);
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ /*
+ * We must leave the lease around, it might be
+ * upgraded later
+ */
+ downgrade_lease(fsp->conn->sconn, fsp->file_id,
+ &fsp->lease->lease.lease_key,
+ break_to);
+ } else {
+ remove_oplock(fsp);
+ }
return;
}
- fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+ fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ?
+ LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+
add_oplock_timeout_handler(fsp);
}
@@ -596,6 +699,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
struct break_to_none_state {
struct smbd_server_connection *sconn;
struct file_id id;
+ struct smb2_lease_key lease_key;
};
static void do_break_to_none(struct tevent_context *ctx,
struct tevent_immediate *im,
@@ -614,7 +718,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
struct tevent_immediate *im;
struct break_to_none_state *state;
struct byte_range_lock *brl;
- uint32_t num_read_oplocks = 0;
+ uint32_t num_read_oplocks;
/*
* If this file is level II oplocked then we need
@@ -631,6 +735,8 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
return;
}
+ num_read_oplocks = 0;
+
brl = brl_get_locks_readonly(fsp);
if (brl != NULL) {
num_read_oplocks = brl_num_read_oplocks(brl);
@@ -642,6 +748,10 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
DEBUG(10, ("No read oplocks around\n"));
return;
}
+ if ((num_read_oplocks == 1) && (fsp->oplock_type == LEASE_OPLOCK)) {
+ DEBUG(10, ("We're the only reader, don't break\n"));
+ return;
+ }
/*
* When we get here we might have a brlock entry locked. Also
@@ -650,7 +760,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
* anyway, so we postpone this into an immediate event.
*/
- state = talloc(sconn, struct break_to_none_state);
+ state = talloc_zero(sconn, struct break_to_none_state);
if (state == NULL) {
DEBUG(1, ("talloc failed\n"));
return;
@@ -658,6 +768,13 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
state->sconn = sconn;
state->id = fsp->file_id;
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ state->lease_key = fsp->lease->lease.lease_key;
+ DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n",
+ state->lease_key.data[0],
+ state->lease_key.data[1]));
+ }
+
im = tevent_create_immediate(state);
if (im == NULL) {
DEBUG(1, ("tevent_create_immediate failed\n"));
@@ -667,14 +784,28 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
tevent_schedule_immediate(im, sconn->ev_ctx, do_break_to_none, state);
}
+static void send_break_to_none(struct messaging_context *msg_ctx,
+ const struct share_mode_entry *e)
+{
+ char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+
+ share_mode_entry_to_message(msg, e);
+ /* Overload entry->op_type */
+ SSVAL(msg, OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
+
+ messaging_send_buf(msg_ctx, e->pid, MSG_SMB_BREAK_REQUEST,
+ (uint8 *)msg, sizeof(msg));
+}
+
static void do_break_to_none(struct tevent_context *ctx,
struct tevent_immediate *im,
void *private_data)
{
struct break_to_none_state *state = talloc_get_type_abort(
private_data, struct break_to_none_state);
- int i;
+ uint32_t i;
struct share_mode_lock *lck;
+ struct share_mode_data *d;
lck = get_existing_share_mode_lock(talloc_tos(), state->id);
if (lck == NULL) {
@@ -682,15 +813,61 @@ static void do_break_to_none(struct tevent_context *ctx,
__func__, file_id_string_tos(&state->id)));
goto done;
}
+ d = lck->data;
- DEBUG(10,("%s: num_share_modes = %d\n", __func__,
- lck->data->num_share_modes ));
+ /*
+ * Walk leases and oplocks separately: We have to send one break per
+ * lease. If we have multiple share_mode_entry having a common lease,
+ * we would break the lease twice if we don't walk the leases list
+ * separately.
+ */
- for(i = 0; i < lck->data->num_share_modes; i++) {
- struct share_mode_entry *share_entry = &lck->data->share_modes[i];
- char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+ for (i=0; i<d->num_leases; i++) {
+ struct share_mode_oplock *l = &d->leases[i];
+ struct share_mode_entry *e;
+ uint32_t j;
- if (!is_valid_share_mode_entry(share_entry)) {
+ if ((l->current_state & SMB2_LEASE_READ) == 0) {
+ continue;
+ }
+ if ((l->lease_key.data[0] == state->lease_key.data[0]) &&
+ (l->lease_key.data[1] == state->lease_key.data[1])) {
+ DEBUG(10, ("Don't break our own lease\n"));
+ continue;
+ }
+
+ for (j=0; j<d->num_share_modes; j++) {
+ e = &d->share_modes[j];
+
+ if (!is_valid_share_mode_entry(e)) {
+ continue;
+ }
+ if (e->lease_idx == i) {
+ break;
+ }
+ }
+ if (j == d->num_share_modes) {
+ DEBUG(0, ("leases[%"PRIu32"] has no share mode\n",
+ i));
+ continue;
+ }
+
+ DEBUG(10, ("Breaking lease# %"PRIu32" with share_entry# "
+ "%"PRIu32"\n", i, j));
+
+ send_break_to_none(state->sconn->msg_ctx, e);
+ }
+
+ for(i = 0; i < d->num_share_modes; i++) {
+ struct share_mode_entry *e = &d->share_modes[i];
+
+ if (!is_valid_share_mode_entry(e)) {
+ continue;
+ }
+ if (e->op_type == LEASE_OPLOCK) {
+ /*
+ * Took care of those in the loop above
+ */
continue;
}
@@ -705,15 +882,15 @@ static void do_break_to_none(struct tevent_context *ctx,
* NO_OPLOCK states. JRA.
*/
- DEBUG(10,("%s: share_entry[%i]->op_type == %d\n", __func__,
- i, share_entry->op_type ));
+ DEBUG(10, ("%s: share_entry[%i]->op_type == %d\n", __func__,
+ i, e->op_type ));
- if (share_entry->op_type == NO_OPLOCK) {
+ if (e->op_type == NO_OPLOCK) {
continue;
}
/* Paranoia .... */
- if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
+ if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
DEBUG(0,("%s: PANIC. "
"share mode entry %d is an exlusive "
"oplock !\n", __func__, i ));
@@ -721,13 +898,7 @@ static void do_break_to_none(struct tevent_context *ctx,
abort();
}
- share_mode_entry_to_message(msg, share_entry);
- /* Overload entry->op_type */
- SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
-
- messaging_send_buf(state->sconn->msg_ctx, share_entry->pid,
- MSG_SMB_BREAK_REQUEST,
- (uint8 *)msg, sizeof(msg));
+ send_break_to_none(state->sconn->msg_ctx, e);
}
/* We let the message receivers handle removing the oplock state
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 1080895..613af8a 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -369,6 +369,9 @@ files_struct *file_find_dif(struct smbd_server_connection *sconn,
files_struct *file_find_di_first(struct smbd_server_connection *sconn,
struct file_id id);
files_struct *file_find_di_next(files_struct *start_fsp);
+struct files_struct *file_find_one_fsp_from_lease_key(
+ struct smbd_server_connection *sconn,
+ const struct smb2_lease_key *lease_key);
bool file_find_subpath(files_struct *dir_fsp);
void file_sync_all(connection_struct *conn);
void fsp_free(files_struct *fsp);
@@ -387,6 +390,7 @@ NTSTATUS file_name_hash(connection_struct *conn,
const char *name, uint32_t *p_name_hash);
NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
const struct smb_filename *smb_fname_in);
+uint32_t fsp_lease_type(struct files_struct *fsp);
/* The following definitions come from smbd/ipc.c */
@@ -609,6 +613,8 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
const char *inherit_from_dir,
const char *fname,
SMB_STRUCT_STAT *psbuf);
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+ const struct smb2_lease_key *key);
bool is_stat_open(uint32 access_mask);
struct deferred_open_record;
bool is_deferred_open_async(const struct deferred_open_record *rec);
@@ -647,12 +653,18 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
/* The following definitions come from smbd/oplock.c */
+uint32_t map_oplock_to_lease_type(uint16_t op_type);
+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e);
bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck);
void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp);
NTSTATUS set_file_oplock(files_struct *fsp);
bool remove_oplock(files_struct *fsp);
bool downgrade_oplock(files_struct *fsp);
+NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
+ const struct file_id id,
+ const struct smb2_lease_key *key,
+ uint32_t lease_state);
void contend_level2_oplocks_begin(files_struct *fsp,
enum level2_contention_type type);
void contend_level2_oplocks_end(files_struct *fsp,
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 0d649e1..60394dd 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -47,6 +47,7 @@
#include "../lib/util/pidfile.h"
#include "lib/smbd_shim.h"
#include "scavenger.h"
+#include "locking/leases_db.h"
struct smbd_open_socket;
struct smbd_child_pid;
@@ -1450,6 +1451,10 @@ extern void build_options(bool screen);
if (!locking_init())
exit_daemon("Samba cannot init locking", EACCES);
+ if (!leases_db_init(false)) {
+ exit_daemon("Samba cannot init leases", EACCES);
+ }
+
if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
exit_daemon("Samba cannot init notification", EACCES);
}
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index 4492456..cb57504 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -25,6 +25,9 @@
#include "../libcli/smb/smb_common.h"
#include "../lib/util/tevent_ntstatus.h"
+static NTSTATUS smbd_smb2_request_process_lease_break(
+ struct smbd_smb2_request *req);
+
static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct smbd_smb2_request *smb2req,
@@ -45,6 +48,12 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
struct tevent_req *subreq;
status = smbd_smb2_request_verify_sizes(req, 0x18);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ /*
+ * Retry as a lease break
+ */
+ return smbd_smb2_request_process_lease_break(req);
+ }
if (!NT_STATUS_IS_OK(status)) {
return smbd_smb2_request_error(req, status);
}
@@ -222,16 +231,163 @@ static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
return NT_STATUS_OK;
}
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+ uint32_t in_lease_state);
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+ uint32_t *out_lease_state);
+
+
+static NTSTATUS smbd_smb2_request_process_lease_break(
+ struct smbd_smb2_request *req)
+{
+ NTSTATUS status;
+ const uint8_t *inbody;
+ struct smb2_lease_key in_lease_key;
+ uint32_t in_lease_state;
+ struct tevent_req *subreq;
+
+ status = smbd_smb2_request_verify_sizes(req, 0x24);
+ if (!NT_STATUS_IS_OK(status)) {
+ return smbd_smb2_request_error(req, status);
+ }
+
+ inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+ in_lease_key.data[0] = BVAL(inbody, 8);
+ in_lease_key.data[1] = BVAL(inbody, 16);
+ in_lease_state = IVAL(inbody, 24);
+
+ subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
+ in_lease_key, in_lease_state);
+ if (subreq == NULL) {
+ return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+ }
+ tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
+
+ return smbd_smb2_request_pending_queue(req, subreq, 500);
+}
+
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
+{
+ struct smbd_smb2_request *req = tevent_req_callback_data(
+ subreq, struct smbd_smb2_request);
+ const uint8_t *inbody;
+ struct smb2_lease_key in_lease_key;
+ uint32_t out_lease_state = 0;
+ DATA_BLOB outbody;
+ NTSTATUS status;
+ NTSTATUS error; /* transport error */
+
+ status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ error = smbd_smb2_request_error(req, status);
+ if (!NT_STATUS_IS_OK(error)) {
+ smbd_server_connection_terminate(req->xconn,
+ nt_errstr(error));
+ return;
+ }
+ return;
+ }
+
+ inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+ in_lease_key.data[0] = BVAL(inbody, 8);
+ in_lease_key.data[1] = BVAL(inbody, 16);
+
+ outbody = smbd_smb2_generate_outbody(req, 0x24);
+ if (outbody.data == NULL) {
+ error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+ if (!NT_STATUS_IS_OK(error)) {
+ smbd_server_connection_terminate(req->xconn,
+ nt_errstr(error));
+ return;
+ }
+ return;
+ }
+
+ SSVAL(outbody.data, 0x00, 0x24); /* struct size */
+ SSVAL(outbody.data, 0x02, 0); /* reserved */
+ SIVAL(outbody.data, 0x04, 0); /* flags, must be 0 */
+ SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
+ SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
+ SIVAL(outbody.data, 0x18, out_lease_state);
+ SBVAL(outbody.data, 0x1c, 0); /* leaseduration, must be 0 */
+
+ error = smbd_smb2_request_done(req, outbody, NULL);
+ if (!NT_STATUS_IS_OK(error)) {
+ smbd_server_connection_terminate(req->xconn,
+ nt_errstr(error));
+ return;
+ }
+}
+
+struct smbd_smb2_lease_break_state {
+ uint32_t lease_state;
+};
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+ uint32_t in_lease_state)
+{
+ struct tevent_req *req;
+ struct smbd_smb2_lease_break_state *state;
+ struct files_struct *fsp;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smbd_smb2_lease_break_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->lease_state = in_lease_state;
+
+ fsp = file_find_one_fsp_from_lease_key(smb2_req->sconn, &in_lease_key);
+ if (fsp == NULL) {
+ DEBUG(10, ("No fsp for lease key found\n"));
+ tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return tevent_req_post(req, ev);
+ }
+
+ status = downgrade_lease(smb2_req->sconn, fsp->file_id, &in_lease_key,
+ in_lease_state);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("downgrade_lease returned %s\n",
+ nt_errstr(status)));
+ return tevent_req_post(req, ev);
+ }
+ TALLOC_FREE(fsp->oplock_timeout);
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+ uint32_t *out_lease_state)
+{
+ struct smbd_smb2_lease_break_state *state = tevent_req_data(
+ req, struct smbd_smb2_lease_break_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *out_lease_state = state->lease_state;
+ return NT_STATUS_OK;
+}
+
/*********************************************************
Create and send an asynchronous
SMB2 OPLOCK_BREAK_NOTIFICATION.
*********************************************************/
-void send_break_message_smb2(files_struct *fsp, int level)
+void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
{
- uint8_t smb2_oplock_level = (level == OPLOCKLEVEL_II) ?
- SMB2_OPLOCK_LEVEL_II :
- SMB2_OPLOCK_LEVEL_NONE;
NTSTATUS status;
struct smbXsrv_connection *xconn = NULL;
struct smbXsrv_session *session = NULL;
@@ -257,7 +413,7 @@ void send_break_message_smb2(files_struct *fsp, int level)
"for file %s, %s, smb2 level %u session %llu not found\n",
fsp_str_dbg(fsp),
fsp_fnum_dbg(fsp),
- (unsigned int)smb2_oplock_level,
+ (unsigned int)break_to,
(unsigned long long)fsp->vuid));
return;
}
@@ -266,13 +422,29 @@ void send_break_message_smb2(files_struct *fsp, int level)
"for file %s, %s, smb2 level %u\n",
fsp_str_dbg(fsp),
fsp_fnum_dbg(fsp),
- (unsigned int)smb2_oplock_level ));
+ (unsigned int)break_to ));
+
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ bool no_ack;
- status = smbd_smb2_send_oplock_break(xconn,
- session,
- fsp->conn->tcon,
- fsp->op,
- smb2_oplock_level);
+ no_ack = ((fsp->lease->lease.lease_state == SMB2_LEASE_READ) &&
+ (break_to == SMB2_LEASE_NONE));
+
+ status = smbd_smb2_send_lease_break(
+ xconn, session, fsp->conn->tcon, 0,
+ no_ack ? 0 : SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED,
+ &fsp->lease->lease.lease_key,
+ fsp->lease->lease.lease_state, break_to);
+ } else {
+ uint8_t smb2_oplock_level;
+ smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
+ SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
+ status = smbd_smb2_send_oplock_break(xconn,
+ session,
+ fsp->conn->tcon,
+ fsp->op,
+ smb2_oplock_level);
+ }
if (!NT_STATUS_IS_OK(status)) {
smbd_server_connection_terminate(xconn,
nt_errstr(status));
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 48bc486..67d4fc0 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -25,6 +25,7 @@
#include "smbd/globals.h"
#include "../libcli/smb/smb_common.h"
#include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/ndr_smb2_lease_struct.h"
#include "../lib/util/tevent_ntstatus.h"
#include "messages.h"
@@ -40,9 +41,7 @@ int map_smb2_oplock_levels_to_samba(uint8_t in_oplock_level)
case SMB2_OPLOCK_LEVEL_BATCH:
return BATCH_OPLOCK;
case SMB2_OPLOCK_LEVEL_LEASE:
- DEBUG(2,("map_smb2_oplock_levels_to_samba: "
- "LEASE_OPLOCK_REQUESTED\n"));
- return NO_OPLOCK;
+ return LEASE_OPLOCK;
default:
DEBUG(2,("map_smb2_oplock_levels_to_samba: "
"unknown level %u\n",
@@ -59,6 +58,8 @@ static uint8_t map_samba_oplock_levels_to_smb2(int oplock_type)
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
} else if (oplock_type == LEVEL_II_OPLOCK) {
return SMB2_OPLOCK_LEVEL_II;
+ } else if (oplock_type == LEASE_OPLOCK) {
+ return SMB2_OPLOCK_LEVEL_LEASE;
} else {
return SMB2_OPLOCK_LEVEL_NONE;
}
@@ -374,6 +375,62 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq)
}
}
+static bool smb2_lease_key_valid(const struct smb2_lease_key *key)
+{
+ return ((key->data[0] != 0) || (key->data[1] != 0));
+}
+
+static NTSTATUS smbd_smb2_create_durable_lease_check(
+ const char *requested_filename, const struct files_struct *fsp,
+ const struct smb2_lease *lease_ptr)
+{
+ struct smb_filename *smb_fname = NULL;
+ NTSTATUS status;
+
+ if (lease_ptr == NULL) {
+ if (fsp->oplock_type != LEASE_OPLOCK) {
+ return NT_STATUS_OK;
+ }
+ DEBUG(10, ("Reopened file has lease, but no lease "
+ "requested\n"));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (fsp->oplock_type != LEASE_OPLOCK) {
+ DEBUG(10, ("Lease requested, but reopened file has no "
+ "lease\n"));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (!smb2_lease_key_equal(&lease_ptr->lease_key,
+ &fsp->lease->lease.lease_key)) {
+ DEBUG(10, ("Different lease key requested than found "
+ "in reopened file\n"));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = filename_convert(talloc_tos(), fsp->conn, false,
+ requested_filename, UCF_PREP_CREATEFILE,
+ NULL, &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("filename_convert returned %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (!strequal(fsp->fsp_name->base_name, smb_fname->base_name)) {
+ DEBUG(10, ("Lease requested for file %s, reopened file "
+ "is named %s\n", smb_fname->base_name,
+ fsp->fsp_name->base_name));
+ TALLOC_FREE(smb_fname);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ TALLOC_FREE(smb_fname);
+
+ return NT_STATUS_OK;
+}
+
struct smbd_smb2_create_state {
struct smbd_smb2_request *smb2req;
struct smb_request *smb1req;
@@ -507,6 +564,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
num_blobs_allowed = 1;
}
+ if (smb2_create_blob_find(&in_context_blobs,
+ SMB2_CREATE_TAG_RQLS) != NULL) {
+ num_blobs_allowed += 1;
+ }
+
if (in_context_blobs.num_blobs != num_blobs_allowed) {
tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return tevent_req_post(req, ev);
@@ -539,6 +601,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
num_blobs_allowed = 1;
+ if (smb2_create_blob_find(&in_context_blobs,
+ SMB2_CREATE_TAG_RQLS) != NULL) {
+ num_blobs_allowed += 1;
+ }
+
if (in_context_blobs.num_blobs != num_blobs_allowed) {
tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return tevent_req_post(req, ev);
@@ -600,11 +667,15 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
struct smb2_create_blob *qfid = NULL;
struct GUID _create_guid = GUID_zero();
struct GUID *create_guid = NULL;
+ struct smb2_create_blob *rqls = NULL;
bool update_open = false;
bool durable_requested = false;
uint32_t durable_timeout_msec = 0;
bool do_durable_reconnect = false;
uint64_t persistent_id = 0;
+ struct smb2_lease lease;
+ struct smb2_lease *lease_ptr;
+ ssize_t lease_len = -1;
exta = smb2_create_blob_find(&in_context_blobs,
SMB2_CREATE_TAG_EXTA);
@@ -618,6 +689,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
SMB2_CREATE_TAG_TWRP);
qfid = smb2_create_blob_find(&in_context_blobs,
SMB2_CREATE_TAG_QFID);
+ rqls = smb2_create_blob_find(&in_context_blobs,
+ SMB2_CREATE_TAG_RQLS);
fname = talloc_strdup(state, in_name);
if (tevent_req_nomem(fname, req)) {
@@ -804,6 +877,39 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
}
}
+ lease_ptr = NULL;
+
+ if (rqls) {
+ lease_len = smb2_lease_pull(
+ rqls->data.data, rqls->data.length, &lease);
+ if (lease_len == -1) {
+ tevent_req_nterror(
+ req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ lease_ptr = &lease;
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("Got lease request size %d\n",
+ (int)lease_len));
+ NDR_PRINT_DEBUG(smb2_lease, lease_ptr);
+ }
+
+ if (!smb2_lease_key_valid(&lease.lease_key)) {
+ lease_ptr = NULL;
+ requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+ }
+
+ /* TODO client->connections ... */
+ if ((smb2req->sconn->client->connections->protocol <
+ PROTOCOL_SMB3_00)
+ && (lease_len == 52)) {
+ DEBUG(10, ("v2 lease key only for SMB3\n"));
+ lease_ptr = NULL;
+ requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+ }
+ }
+
/* these are ignored for SMB2 */
in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
@@ -863,6 +969,18 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
return tevent_req_post(req, ev);
}
+ DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n",
+ (unsigned)result->oplock_type, lease_ptr));
+
+ status = smbd_smb2_create_durable_lease_check(
+ fname, result, lease_ptr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ close_file(smb1req, result, SHUTDOWN_CLOSE);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
data_blob_free(&op->global->backend_cookie);
op->global->backend_cookie = new_cookie;
@@ -948,7 +1066,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
in_create_options,
in_file_attributes,
map_smb2_oplock_levels_to_samba(requested_oplock_level),
- NULL,
+ lease_ptr,
allocation_size,
0, /* private_flags */
sec_desc,
@@ -1003,7 +1121,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
}
if (durable_requested &&
- BATCH_OPLOCK_TYPE(result->oplock_type))
+ (fsp_lease_type(result) & SMB2_LEASE_HANDLE))
{
status = SMB_VFS_DURABLE_COOKIE(result,
op,
@@ -1086,6 +1204,29 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
return tevent_req_post(req, ev);
}
}
+
+ if ((rqls != NULL) && (result->oplock_type == LEASE_OPLOCK)) {
+ uint8_t buf[52];
+
+ lease = result->lease->lease;
+ lease.lease_flags &=
+ SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET;
+
+ if (!smb2_lease_push(&lease, buf, lease_len)) {
+ tevent_req_nterror(
+ req, NT_STATUS_INTERNAL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ status = smb2_create_blob_add(
+ state, &out_context_blobs,
+ SMB2_CREATE_TAG_RQLS,
+ data_blob_const(buf, lease_len));
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ }
}
smb2req->compat_chain_fsp = smb1req->chain_fsp;
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 689bfd7..36ac0d7 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2888,6 +2888,31 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
}
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
+ struct smbXsrv_session *session,
+ struct smbXsrv_tcon *tcon,
+ uint16_t new_epoch,
+ uint32_t lease_flags,
+ struct smb2_lease_key *lease_key,
+ uint32_t current_lease_state,
+ uint32_t new_lease_state)
+{
+ uint8_t body[0x2c];
+
+ SSVAL(body, 0x00, sizeof(body));
+ SSVAL(body, 0x02, new_epoch);
+ SIVAL(body, 0x04, lease_flags);
+ SBVAL(body, 0x08, lease_key->data[0]);
+ SBVAL(body, 0x10, lease_key->data[1]);
+ SIVAL(body, 0x18, current_lease_state);
+ SIVAL(body, 0x1c, new_lease_state);
+ SIVAL(body, 0x20, 0); /* BreakReason, MUST be 0 */
+ SIVAL(body, 0x24, 0); /* AccessMaskHint, MUST be 0 */
+ SIVAL(body, 0x28, 0); /* ShareMaskHint, MUST be 0 */
+
+ return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
+}
+
static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
{
NTSTATUS status;
diff --git a/source3/utils/status.c b/source3/utils/status.c
index 978d3c5..f46f336 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -177,6 +177,8 @@ static int print_share_mode(const struct share_mode_entry *e,
d_printf("BATCH ");
} else if (e->op_type & LEVEL_II_OPLOCK) {
d_printf("LEVEL_II ");
+ } else if (e->op_type == LEASE_OPLOCK) {
+ d_printf("LEASE ");
} else {
d_printf("NONE ");
}
diff --git a/source3/wscript_build b/source3/wscript_build
index 836d7b3..7a0fd7f 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -619,6 +619,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
LIBAFS
RPC_SERVICE
NDR_SMBXSRV
+ LEASES_DB
LIBASYS
sysquotas
ccan-hash
@@ -637,9 +638,14 @@ bld.SAMBA3_SUBSYSTEM('LOCKING',
deps='''
tdb_compat
talloc
+ LEASES_DB
NDR_OPEN_FILES
FNAME_UTIL''')
+bld.SAMBA3_SUBSYSTEM('LEASES_DB',
+ source='locking/leases_db.c',
+ deps='NDR_LEASES_DB')
+
if bld.CONFIG_GET("WITH_PROFILE"):
bld.SAMBA3_SUBSYSTEM('PROFILE',
source='profile/profile.c',
--
1.9.1
From f3a903fb04cf9cc64064d1d7a028147542e59d50 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 02:28:38 +0200
Subject: [PATCH 02/16] s3: leases - epoch handling
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/open.c | 6 +++---
source3/smbd/oplock.c | 23 +++++++++++++++++++++++
source3/smbd/proto.h | 3 +++
source3/smbd/smb2_break.c | 9 ++++++++-
source3/smbd/smb2_create.c | 6 ++++++
5 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 7ac8fd8..c47e913 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1521,7 +1521,7 @@ static bool file_has_brlocks(files_struct *fsp)
return (brl_num_locks(br_lck) > 0);
}
-static int find_share_mode_oplock(struct share_mode_data *d,
+int find_share_mode_oplock(struct share_mode_data *d,
const struct GUID *client_guid,
const struct smb2_lease_key *key)
{
@@ -1670,8 +1670,8 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
d->leases[d->num_leases] = (struct share_mode_oplock) {
.client_guid = *client_guid,
- .lease_key = lease->lease_key,
- .epoch = lease->lease_epoch,
+ .lease_key = fsp->lease->lease.lease_key,
+ .epoch = fsp->lease->lease.lease_epoch,
.current_state = granted,
};
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 0a7ac17..83dec36 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -598,6 +598,29 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
return;
}
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ /* Need to increment the epoch */
+ struct share_mode_lock *lck;
+ int idx;
+
+ lck = get_existing_share_mode_lock(
+ talloc_tos(), fsp->file_id);
+
+ idx = find_share_mode_oplock(
+ lck->data,
+ &fsp->conn->sconn->client->connections->
+ smb2.client.guid,
+ &fsp->lease->lease.lease_key);
+ if (idx != -1) {
+ struct share_mode_oplock *o;
+ o = &lck->data->leases[idx];
+ o->epoch += 1;
+ fsp->lease->lease.lease_epoch = o->epoch;
+ }
+
+ TALLOC_FREE(lck);
+ }
+
/* Need to wait before sending a break
message if we sent ourselves this message. */
if (serverid_equal(&self, &src)) {
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 613af8a..08ad413 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -627,6 +627,9 @@ void msg_file_was_renamed(struct messaging_context *msg,
DATA_BLOB *data);
NTSTATUS open_streams_for_delete(connection_struct *conn,
const char *fname);
+int find_share_mode_oplock(struct share_mode_data *d,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *key);
NTSTATUS create_file_default(connection_struct *conn,
struct smb_request *req,
uint16_t root_dir_fid,
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index cb57504..abe6709 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -426,12 +426,19 @@ void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
if (fsp->oplock_type == LEASE_OPLOCK) {
bool no_ack;
+ uint16_t new_epoch;
no_ack = ((fsp->lease->lease.lease_state == SMB2_LEASE_READ) &&
(break_to == SMB2_LEASE_NONE));
+ if (xconn->protocol >= PROTOCOL_SMB3_00) {
+ new_epoch = fsp->lease->lease.lease_epoch;
+ } else {
+ new_epoch = 0;
+ }
+
status = smbd_smb2_send_lease_break(
- xconn, session, fsp->conn->tcon, 0,
+ xconn, session, fsp->conn->tcon, new_epoch,
no_ack ? 0 : SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED,
&fsp->lease->lease.lease_key,
fsp->lease->lease.lease_state, break_to);
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 67d4fc0..abcfa1a 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -900,6 +900,12 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
}
+ if (smb2req->sconn->client->connections->protocol <
+ PROTOCOL_SMB2_10) {
+ lease_ptr = NULL;
+ requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+ }
+
/* TODO client->connections ... */
if ((smb2req->sconn->client->connections->protocol <
PROTOCOL_SMB3_00)
--
1.9.1
From ac90f0493583bdd9c56b124a53a1660b6bae3edf Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 22:56:41 +0200
Subject: [PATCH 03/16] s3: leases - Only increment epoch for V2 leases
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/open.c | 4 +++-
source3/smbd/oplock.c | 3 ++-
source3/smbd/smb2_create.c | 4 ++--
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index c47e913..6aa63a4 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1664,7 +1664,9 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
fsp->lease->ref_count = 1;
fsp->lease->lease = *lease;
fsp->lease->lease.lease_state = granted;
- fsp->lease->lease.lease_epoch += 1;
+ if (fsp->lease->lease.lease_version > 1) {
+ fsp->lease->lease.lease_epoch += 1;
+ }
*p_lease_idx = d->num_leases;
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 83dec36..e97ade6 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -598,7 +598,8 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
return;
}
- if (fsp->oplock_type == LEASE_OPLOCK) {
+ if ((fsp->oplock_type == LEASE_OPLOCK) &&
+ (fsp->lease->lease.lease_version != 1)) {
/* Need to increment the epoch */
struct share_mode_lock *lck;
int idx;
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index abcfa1a..38ef04c 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -908,8 +908,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
/* TODO client->connections ... */
if ((smb2req->sconn->client->connections->protocol <
- PROTOCOL_SMB3_00)
- && (lease_len == 52)) {
+ PROTOCOL_SMB3_00) &&
+ (lease.lease_version != 1)) {
DEBUG(10, ("v2 lease key only for SMB3\n"));
lease_ptr = NULL;
requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
--
1.9.1
From f22d4519cd39f3b432d6d3fa0695f73852554e5e Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 10 Oct 2014 16:36:54 -0700
Subject: [PATCH 04/16] s3: smbd: leases - expand the leases db to hold a list
of file_ids (dev,ino) with this lease.
Will enable us to solve the dynamic share path problem.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/librpc/idl/leases_db.idl | 24 +++
source3/locking/leases_db.c | 368 +++++++++++++++++++++++++++++++++++++++
source3/locking/leases_db.h | 46 +++++
3 files changed, 438 insertions(+)
create mode 100644 source3/librpc/idl/leases_db.idl
create mode 100644 source3/locking/leases_db.c
create mode 100644 source3/locking/leases_db.h
diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl
new file mode 100644
index 0000000..28ec479
--- /dev/null
+++ b/source3/librpc/idl/leases_db.idl
@@ -0,0 +1,24 @@
+#include "idl_types.h"
+
+import "misc.idl";
+import "smb2_lease_struct.idl";
+import "file_id.idl";
+
+[
+ pointer_default(unique)
+]
+
+interface leases_db
+{
+ typedef [public] struct {
+ GUID client_guid;
+ smb2_lease_key lease_key;
+ } leases_db_key;
+
+ typedef [public] struct {
+ uint32 num_file_ids;
+ [size_is(num_file_ids)] file_id ids[];
+ [string,charset(UTF8)] char *filename;
+ [string,charset(UTF8)] char *stream_name;
+ } leases_db_value;
+}
diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c
new file mode 100644
index 0000000..29b8e35
--- /dev/null
+++ b/source3/locking/leases_db.c
@@ -0,0 +1,368 @@
+/*
+ Unix SMB/CIFS implementation.
+ Map lease keys to file ids
+ Copyright (C) Volker Lendecke 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "locking/leases_db.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "util_tdb.h"
+#include "ndr.h"
+#include "librpc/gen_ndr/ndr_leases_db.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LOCKING
+
+/* the leases database handle */
+static struct db_context *leases_db;
+
+bool leases_db_init(bool read_only)
+{
+ if (leases_db) {
+ return true;
+ }
+
+ leases_db = db_open(NULL, lock_path("leases.tdb"), 0,
+ TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST|
+ TDB_INCOMPATIBLE_HASH,
+ read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
+
+ if (leases_db == NULL) {
+ DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
+ return false;
+ }
+
+ return true;
+}
+
+static bool leases_db_key(TALLOC_CTX *mem_ctx,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ TDB_DATA *key)
+{
+ struct leases_db_key db_key = {
+ .client_guid = *client_guid,
+ .lease_key = *lease_key };
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("%s:\n", __func__));
+ NDR_PRINT_DEBUG(leases_db_key, &db_key);
+ }
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, mem_ctx, &db_key,
+ (ndr_push_flags_fn_t)ndr_push_leases_db_key);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ return false;
+ }
+
+ *key = make_tdb_data(blob.data, blob.length);
+ return true;
+}
+
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id,
+ const char *filename,
+ const char *stream_name)
+{
+ TDB_DATA db_key, db_value;
+ DATA_BLOB blob;
+ struct db_record *rec;
+ NTSTATUS status;
+ bool ok;
+ struct leases_db_value new_value;
+ struct leases_db_value *value = NULL;
+ enum ndr_err_code ndr_err;
+
+ if (!leases_db_init(false)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+ if (!ok) {
+ DEBUG(10, ("%s: leases_db_key failed\n", __func__));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+ TALLOC_FREE(db_key.dptr);
+ if (rec == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ db_value = dbwrap_record_get_value(rec);
+ if (db_value.dsize != 0) {
+ uint32_t i;
+
+ DEBUG(10, ("%s: record exists\n", __func__));
+
+ value = talloc(talloc_tos(), struct leases_db_value);
+ if (value == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ blob.data = db_value.dptr;
+ blob.length = db_value.dsize;
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, value, value,
+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ /* id must be unique. */
+ for (i = 0; i < value->num_file_ids; i++) {
+ if (file_id_equal(id, &value->ids[i])) {
+ status = NT_STATUS_OBJECT_NAME_COLLISION;
+ goto out;
+ }
+ }
+
+ value->ids = talloc_realloc(value, value->ids, struct file_id,
+ value->num_file_ids + 1);
+ if (value->ids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ value->ids[value->num_file_ids] = *id;
+ value->num_file_ids += 1;
+
+ } else {
+ new_value = (struct leases_db_value) {
+ .num_file_ids = 1,
+ .ids = id,
+ .filename = filename,
+ .stream_name = stream_name,
+ };
+ value = &new_value;
+ }
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, talloc_tos(), value,
+ (ndr_push_flags_fn_t)ndr_push_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ db_value = make_tdb_data(blob.data, blob.length);
+
+ status = dbwrap_record_store(rec, db_value, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+ __func__, nt_errstr(status)));
+ }
+
+ out:
+
+ if (value != &new_value) {
+ TALLOC_FREE(value);
+ }
+ TALLOC_FREE(rec);
+ return status;
+}
+
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id)
+{
+ TDB_DATA db_key, db_value;
+ struct db_record *rec;
+ NTSTATUS status;
+ struct leases_db_value *value;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ uint32_t i;
+ bool ok;
+
+ if (!leases_db_init(false)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+ TALLOC_FREE(db_key.dptr);
+ if (rec == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ db_value = dbwrap_record_get_value(rec);
+ if (db_value.dsize == 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ value = talloc(talloc_tos(), struct leases_db_value);
+ if (value == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ blob.data = db_value.dptr;
+ blob.length = db_value.dsize;
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, value, value,
+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ /* id must exist. */
+ for (i = 0; i < value->num_file_ids; i++) {
+ if (file_id_equal(id, &value->ids[i])) {
+ break;
+ }
+ }
+
+ if (i == value->num_file_ids) {
+ status = NT_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ value->ids[i] = value->ids[value->num_file_ids-1];
+ value->num_file_ids -= 1;
+
+ if (value->num_file_ids == 0) {
+ status = dbwrap_record_delete(rec);
+ } else {
+ ndr_err = ndr_push_struct_blob(
+ &blob, talloc_tos(), value,
+ (ndr_push_flags_fn_t)ndr_push_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ db_value = make_tdb_data(blob.data, blob.length);
+
+ status = dbwrap_record_store(rec, db_value, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+ __func__, nt_errstr(status)));
+ }
+ }
+
+ out:
+
+ TALLOC_FREE(value);
+ TALLOC_FREE(rec);
+ return status;
+}
+
+struct leases_db_fetch_state {
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids, const char *filename,
+ const char *stream_name, void *private_data);
+ void *private_data;
+ NTSTATUS status;
+};
+
+static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
+{
+ struct leases_db_fetch_state *state =
+ (struct leases_db_fetch_state *)private_data;
+ DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
+ enum ndr_err_code ndr_err;
+ struct leases_db_value *value;
+
+ value = talloc(talloc_tos(), struct leases_db_value);
+ if (value == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, value, value,
+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ TALLOC_FREE(value);
+ state->status = ndr_map_error2ntstatus(ndr_err);
+ return;
+ }
+
+ state->parser(value->num_file_ids,
+ value->ids, value->filename, value->stream_name,
+ state->private_data);
+
+ TALLOC_FREE(value);
+ state->status = NT_STATUS_OK;
+}
+
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids,
+ const char *filename,
+ const char *stream_name,
+ void *private_data),
+ void *private_data)
+{
+ TDB_DATA db_key;
+ struct leases_db_fetch_state state;
+ NTSTATUS status;
+ bool ok;
+
+ if (!leases_db_init(true)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state = (struct leases_db_fetch_state) {
+ .parser = parser,
+ .private_data = private_data,
+ .status = NT_STATUS_OK
+ };
+
+ status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
+ &state);
+ TALLOC_FREE(db_key.dptr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return state.status;
+}
diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h
new file mode 100644
index 0000000..f570356
--- /dev/null
+++ b/source3/locking/leases_db.h
@@ -0,0 +1,46 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * leases.tdb functions
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LEASES_DB_H_
+#define _LEASES_DB_H_
+
+struct GUID;
+struct smb2_lease_key;
+struct file_id;
+
+bool leases_db_init(bool read_only);
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id,
+ const char *filename,
+ const char *stream_name);
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id);
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids,
+ const char *filename,
+ const char *stream_name,
+ void *private_data),
+ void *private_data);
+
+#endif /* _LEASES_DB_H_ */
--
1.9.1
From c36985a806f36be7658c574ee384ca7fb23c479d Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 10 Oct 2014 14:32:19 -0700
Subject: [PATCH 05/16] s3: smbd: Leases - ensure all share mode removal
functions go through a common lease refcount manager.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/locking/locking.c | 137 ++++++++++++++++++++++++++++------------------
1 file changed, 83 insertions(+), 54 deletions(-)
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 170fa1d..8132c71 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -618,6 +618,85 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e)
}
/*
+ * See if we need to remove a lease being referred to by a
+ * share mode that is being marked stale or deleted.
+ */
+
+static void remove_share_mode_lease(struct share_mode_data *d,
+ struct share_mode_entry *e)
+{
+ struct GUID client_guid;
+ struct smb2_lease_key lease_key;
+ uint16_t op_type;
+ uint32_t lease_idx;
+ uint32_t i;
+
+ op_type = e->op_type;
+ e->op_type = NO_OPLOCK;
+
+ d->modified = true;
+
+ if (op_type != LEASE_OPLOCK) {
+ return;
+ }
+
+ /*
+ * This used to reference a lease. If there's no other one referencing
+ * it, remove it.
+ */
+
+ lease_idx = e->lease_idx;
+ e->lease_idx = UINT32_MAX;
+
+ for (i=0; i<d->num_share_modes; i++) {
+ if (d->share_modes[i].stale) {
+ continue;
+ }
+ if (e == &d->share_modes[i]) {
+ /* Not ourselves. */
+ continue;
+ }
+ if (d->share_modes[i].lease_idx == lease_idx) {
+ break;
+ }
+ }
+ if (i < d->num_share_modes) {
+ /*
+ * Found another one
+ */
+ return;
+ }
+
+ memcpy(&client_guid,
+ &d->leases[lease_idx].client_guid,
+ sizeof(client_guid));
+ lease_key = d->leases[lease_idx].lease_key;
+
+ d->num_leases -= 1;
+ d->leases[lease_idx] = d->leases[d->num_leases];
+
+ /*
+ * We changed the lease array. Fix all references to it.
+ */
+ for (i=0; i<d->num_share_modes; i++) {
+ if (d->share_modes[i].lease_idx == d->num_leases) {
+ d->share_modes[i].lease_idx = lease_idx;
+ }
+ }
+
+ {
+ NTSTATUS status;
+
+ status = leases_db_del(&client_guid,
+ &lease_key,
+ &e->id);
+
+ DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+ nt_errstr(status)));
+ }
+}
+
+/*
* In case d->share_modes[i] conflicts with something or otherwise is
* being used, we need to make sure the corresponding process still
* exists.
@@ -675,6 +754,8 @@ bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx)
}
}
+ remove_share_mode_lease(d, e);
+
d->modified = true;
return true;
}
@@ -773,6 +854,7 @@ bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp)
if (e == NULL) {
return False;
}
+ remove_share_mode_lease(lck->data, e);
*e = lck->data->share_modes[lck->data->num_share_modes-1];
lck->data->num_share_modes -= 1;
lck->data->modified = True;
@@ -822,67 +904,14 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
{
struct share_mode_data *d = lck->data;
struct share_mode_entry *e;
- uint16_t op_type;
- uint32_t lease_idx;
- uint32_t i;
e = find_share_mode_entry(lck, fsp);
if (e == NULL) {
return False;
}
- op_type = e->op_type;
- e->op_type = NO_OPLOCK;
-
+ remove_share_mode_lease(d, e);
d->modified = True;
-
- if (op_type != LEASE_OPLOCK) {
- return true;
- }
-
- /*
- * This used to reference a lease. If there's no other one referencing
- * it, remove it.
- */
-
- lease_idx = e->lease_idx;
- e->lease_idx = UINT32_MAX;
-
- for (i=0; i<d->num_share_modes; i++) {
- if (d->share_modes[i].lease_idx == lease_idx) {
- break;
- }
- }
- if (i < d->num_share_modes) {
- /*
- * Found another one
- */
- return true;
- }
-
- d->num_leases -= 1;
- d->leases[lease_idx] = d->leases[d->num_leases];
-
- /*
- * We changed the lease array. Fix all references to it.
- */
- for (i=0; i<d->num_share_modes; i++) {
- if (d->share_modes[i].lease_idx == d->num_leases) {
- d->share_modes[i].lease_idx = lease_idx;
- }
- }
-
- {
- NTSTATUS status;
-
- status = leases_db_del(
- &fsp->conn->sconn->client->connections->smb2.client.guid,
- &fsp->lease->lease.lease_key);
-
- DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
- nt_errstr(status)));
- }
-
return true;
}
--
1.9.1
From e1c53fdb1e8137519821537e74e2d7e2431b1176 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 10 Oct 2014 17:05:46 -0700
Subject: [PATCH 06/16] s3: smbd: leases - Fix the dynamic share file case
[homes].
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/open.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 135 insertions(+), 16 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 6aa63a4..5d9256e 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -4091,39 +4091,148 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
*/
struct lease_fname_match_state {
+ /* Input parameters. */
const struct smb_filename *fname;
- bool match;
+ bool file_existed;
+ struct file_id id;
+ /* Return parameters. */
+ uint32_t num_file_ids;
+ struct file_id *ids;
+ NTSTATUS match_status;
};
static void lease_fname_match_parser(
- struct file_id id, const char *filename, const char *stream_name,
+ uint32_t num_file_ids,
+ struct file_id *ids, const char *filename, const char *stream_name,
void *private_data)
{
struct lease_fname_match_state *state =
(struct lease_fname_match_state *)private_data;
- state->match =
- strequal(filename, state->fname->base_name) &&
- strequal(stream_name, state->fname->stream_name);
+ if (!strequal(filename, state->fname->base_name) ||
+ !strequal(stream_name, state->fname->stream_name)) {
+ /* Names don't match lease key. */
+ state->match_status = NT_STATUS_INVALID_PARAMETER;
+ return;
+ }
+
+ if (!state->file_existed) {
+ /* New file. */
+ state->match_status = NT_STATUS_OK;
+ return;
+ }
+
+ if (num_file_ids == 1 && file_id_equal(&ids[0],&state->id)) {
+ /* Common case - non-dynamic share. We're ok.. */
+ state->match_status = NT_STATUS_OK;
+ return;
+ }
+
+ /* More than one file id, or not equal. Don't allow leases. */
+ state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED;
+ state->num_file_ids = num_file_ids;
+ state->ids = talloc_memdup(talloc_tos(),
+ ids,
+ num_file_ids * sizeof(struct file_id));
+ if (state->ids == NULL) {
+ state->match_status = NT_STATUS_NO_MEMORY;
+ }
}
-static bool lease_fname_match(struct smbd_server_connection *sconn,
- struct smb2_lease_key *lease_key,
- const struct smb_filename *fname)
+static NTSTATUS lease_match(connection_struct *conn,
+ struct smb_request *req,
+ struct smb2_lease_key *lease_key,
+ const struct smb_filename *fname)
{
- struct lease_fname_match_state state =
- { .fname = fname, .match = true };
+ struct smbd_server_connection *sconn = req->sconn;
+ struct lease_fname_match_state state = {
+ .fname = fname,
+ .match_status = NT_STATUS_OK
+ };
+ uint32_t i;
NTSTATUS status;
+ state.file_existed = VALID_STAT(fname->st);
+ if (state.file_existed) {
+ state.id = vfs_file_id_from_sbuf(conn, &fname->st);
+ }
+
status = leases_db_parse(&sconn->client->connections->smb2.client.guid,
lease_key, lease_fname_match_parser, &state);
if (!NT_STATUS_IS_OK(status)) {
/*
* Not found or error means okay: We can make the lease pass
*/
- return true;
+ return NT_STATUS_OK;
}
- return state.match;
+ if (!NT_STATUS_EQUAL(state.match_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ /*
+ * Anything but NT_STATUS_OPLOCK_NOT_GRANTED, let the caller
+ * deal with it.
+ */
+ return state.match_status;
+ }
+
+ /* We have to break all existing leases. */
+ for (i = 0; i < state.num_file_ids; i++) {
+ struct share_mode_lock *lck;
+ struct share_mode_data *d;
+ uint32_t j;
+
+ if (file_id_equal(&state.ids[i], &state.id)) {
+ /* Don't need to break our own file. */
+ continue;
+ }
+ lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]);
+ if (lck == NULL) {
+ /* Race condition - file already closed. */
+ continue;
+ }
+ d = lck->data;
+ for (j=0; j<d->num_share_modes; j++) {
+ struct share_mode_entry *e = &d->share_modes[j];
+ uint32_t e_lease_type = get_lease_type(d, e);
+
+ if (e_lease_type == SMB2_LEASE_NONE) {
+ continue;
+ }
+ if (share_mode_stale_pid(d, j)) {
+ continue;
+ }
+
+ send_break_message(conn->sconn->msg_ctx, e,
+ SMB2_LEASE_NONE);
+
+ /*
+ * Windows 7 and 8 lease clients
+ * are broken in that they will not
+ * respond to lease break requests
+ * whilst waiting for an outstanding
+ * open request on that lease handle
+ * on the same TCP connection, due
+ * to holding an internal inode lock.
+ *
+ * This means we can't reschedule
+ * ourselves here, but must return
+ * from the create.
+ *
+ * Work around:
+ *
+ * Send the breaks and then return
+ * SMB2_LEASE_NONE in the lease handle
+ * to cause them to acknowledge the
+ * lease break. Consulatation with
+ * Microsoft engineering confirmed
+ * this approach is safe.
+ */
+ }
+ TALLOC_FREE(lck);
+ }
+ /*
+ * Ensure we don't grant anything more so we
+ * never upgrade.
+ */
+ return NT_STATUS_OPLOCK_NOT_GRANTED;
}
/*
@@ -4182,10 +4291,20 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
oplock_request |= INTERNAL_OPEN_ONLY;
}
- if ((lease != NULL) &&
- !lease_fname_match(req->sconn, &lease->lease_key, smb_fname)) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
+ if (lease != NULL) {
+ status = lease_match(conn,
+ req,
+ &lease->lease_key,
+ smb_fname);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ /* Dynamic share file. No leases and update epoch... */
+ lease->lease_state = SMB2_LEASE_NONE;
+ if (lease->lease_version > 1) {
+ lease->lease_epoch += 1;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
}
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
--
1.9.1
From 2041becaf6aaab3cde2e6c3e426c92a22e81e081 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:24:31 -0700
Subject: [PATCH 07/16] s3: smbd: leases - If find_fsp_lease() doesn't find a
lease this isn't an error.
As the lease exists in the oplock db, then it just must be leased in
another smbd (multi-connection case).
This unifies code that previously existed only inside the durable
reconnect case.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/durable.c | 21 +++++----------------
source3/smbd/open.c | 24 ++++++++++++++++--------
source3/smbd/proto.h | 4 +++-
3 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 660865d..73bcd58 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -731,22 +731,11 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
key.data[0] = o->lease_key.data[0];
key.data[1] = o->lease_key.data[1];
- fsp->lease = find_fsp_lease(fsp, &key);
-
- if (fsp->lease != NULL) {
- fsp->lease->ref_count += 1;
- } else {
- fsp->lease = talloc_zero(fsp->conn->sconn,
- struct fsp_lease);
- if (fsp->lease == NULL) {
- TALLOC_FREE(lck);
- fsp_free(fsp);
- return NT_STATUS_NO_MEMORY;
- }
- fsp->lease->ref_count = 1;
- fsp->lease->lease.lease_key = key;
- fsp->lease->lease.lease_state = o->current_state;
- fsp->lease->lease.lease_epoch = o->epoch;
+ fsp->lease = find_fsp_lease(fsp, &key, o);
+ if (fsp->lease == NULL) {
+ TALLOC_FREE(lck);
+ fsp_free(fsp);
+ return NT_STATUS_NO_MEMORY;
}
}
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 5d9256e..df48a10 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1552,7 +1552,8 @@ static bool is_same_lease(const struct share_mode_data *d,
}
struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
- const struct smb2_lease_key *key)
+ const struct smb2_lease_key *key,
+ const struct share_mode_oplock *o)
{
files_struct *fsp;
@@ -1572,11 +1573,21 @@ struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
continue;
}
if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) {
+ fsp->lease->ref_count += 1;
return fsp->lease;
}
}
- return NULL;
+ /* Not found - must be leased in another smbd. */
+ new_fsp->lease = talloc_zero(new_fsp->conn->sconn, struct fsp_lease);
+ if (new_fsp->lease == NULL) {
+ return NULL;
+ }
+ new_fsp->lease->ref_count = 1;
+ new_fsp->lease->lease.lease_key = *key;
+ new_fsp->lease->lease.lease_state = o->current_state;
+ new_fsp->lease->lease.lease_epoch = o->epoch;
+ return new_fsp->lease;
}
static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
@@ -1585,7 +1596,6 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
uint32_t granted)
{
const struct GUID *client_guid;
- struct share_mode_oplock *o;
struct share_mode_oplock *tmp;
NTSTATUS status;
int idx;
@@ -1599,20 +1609,18 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
idx = find_share_mode_oplock(d, client_guid, &lease->lease_key);
if (idx != -1) {
-
+ struct share_mode_oplock *o = &d->leases[idx];
bool do_upgrade;
uint32_t existing, requested;
- fsp->lease = find_fsp_lease(fsp, &lease->lease_key);
+ fsp->lease = find_fsp_lease(fsp, &lease->lease_key, o);
if (fsp->lease == NULL) {
DEBUG(1, ("Did not find existing lease for file %s\n",
fsp_str_dbg(fsp)));
- return NT_STATUS_INTERNAL_ERROR;
+ return NT_STATUS_NO_MEMORY;
}
- fsp->lease->ref_count += 1;
*p_lease_idx = idx;
- o = &d->leases[idx];
/*
* Upgrade only if the requested lease is a strict upgrade.
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 08ad413..22b1a82 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -613,8 +613,10 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
const char *inherit_from_dir,
const char *fname,
SMB_STRUCT_STAT *psbuf);
+struct share_mode_oplock;
struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
- const struct smb2_lease_key *key);
+ const struct smb2_lease_key *key,
+ const struct share_mode_oplock *o);
bool is_stat_open(uint32 access_mask);
struct deferred_open_record;
bool is_deferred_open_async(const struct deferred_open_record *rec);
--
1.9.1
From dd001563b1b03d8fa05fd0d45c034984841e6c48 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 4 Nov 2014 21:46:14 -0800
Subject: [PATCH 08/16] s3: leases: Add fsp_client_guid() utility function to
return the connected client guid.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/files.c | 5 +++++
source3/smbd/proto.h | 1 +
2 files changed, 6 insertions(+)
diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index 13d1138..6a987c4 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -774,3 +774,8 @@ uint32_t fsp_lease_type(struct files_struct *fsp)
}
return map_oplock_to_lease_type(fsp->oplock_type);
}
+
+const struct GUID *fsp_client_guid(const files_struct *fsp)
+{
+ return &fsp->conn->sconn->client->connections->smb2.client.guid;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 22b1a82..ecf89a7 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -391,6 +391,7 @@ NTSTATUS file_name_hash(connection_struct *conn,
NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
const struct smb_filename *smb_fname_in);
uint32_t fsp_lease_type(struct files_struct *fsp);
+const struct GUID *fsp_client_guid(const files_struct *fsp);
/* The following definitions come from smbd/ipc.c */
--
1.9.1
From d38c1bab1815bb2a4220d582f761628da5503d26 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 4 Nov 2014 21:47:14 -0800
Subject: [PATCH 09/16] s3: leases: Ensure we check client_guid as well as
lease key to ensure lease identity.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/locking/locking.c | 10 ++++++++--
source3/smbd/open.c | 39 ++++++++++++++++++++-------------------
2 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 8132c71..006d6d3 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -960,7 +960,10 @@ bool fsp_lease_broken(struct share_mode_lock *lck,
return false;
}
- if (!smb2_lease_key_equal(key, &d->leases[e->lease_idx].lease_key)) {
+ if (!smb2_lease_equal(&fsp->conn->sconn->client->connections->smb2.client.guid,
+ key,
+ &d->leases[e->lease_idx].client_guid,
+ &d->leases[e->lease_idx].lease_key)) {
return false;
}
fsp->sent_oplock_break = NO_BREAK_SENT;
@@ -979,7 +982,10 @@ NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
uint32_t i;
for (i=0; i<d->num_leases; i++) {
- if (smb2_lease_key_equal(key, &d->leases[i].lease_key)) {
+ if (smb2_lease_equal(&sconn->client->connections->smb2.client.guid,
+ key,
+ &d->leases[i].client_guid,
+ &d->leases[i].lease_key)) {
break;
}
}
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index df48a10..e353ec1 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -37,11 +37,11 @@
#include "source3/lib/dbwrap/dbwrap_watch.h"
#include "locking/leases_db.h"
-static bool is_same_lease(const struct share_mode_data *d,
+static bool is_same_lease(const files_struct *fsp,
+ const struct share_mode_data *d,
const struct share_mode_entry *e,
const struct smb2_lease *lease);
-
extern const struct generic_mapping file_generic_mapping;
struct deferred_open_record {
@@ -1424,7 +1424,7 @@ static bool delay_for_oplock(files_struct *fsp,
if (!(e_lease_type & SMB2_LEASE_HANDLE)) {
continue;
}
- if (is_same_lease(d, e, lease)) {
+ if (is_same_lease(fsp, d, e, lease)) {
continue;
}
if (share_mode_stale_pid(d, i)) {
@@ -1470,9 +1470,10 @@ static bool delay_for_oplock(files_struct *fsp,
}
if ((e->op_type == LEASE_OPLOCK) &&
(lease != NULL) &&
- smb2_lease_key_equal(
- &lease->lease_key,
- &d->leases[e->lease_idx].lease_key)) {
+ smb2_lease_equal(fsp_client_guid(fsp),
+ &lease->lease_key,
+ &d->leases[e->lease_idx].client_guid,
+ &d->leases[e->lease_idx].lease_key)) {
return false;
}
@@ -1529,15 +1530,18 @@ int find_share_mode_oplock(struct share_mode_data *d,
for (i=0; i<d->num_leases; i++) {
struct share_mode_oplock *l = &d->leases[i];
- if (GUID_equal(client_guid, &l->client_guid) &&
- smb2_lease_key_equal(key, &l->lease_key)) {
+ if (smb2_lease_equal(client_guid,
+ key,
+ &l->client_guid,
+ &l->lease_key)) {
return i;
}
}
return -1;
}
-static bool is_same_lease(const struct share_mode_data *d,
+static bool is_same_lease(const files_struct *fsp,
+ const struct share_mode_data *d,
const struct share_mode_entry *e,
const struct smb2_lease *lease)
{
@@ -1547,8 +1551,11 @@ static bool is_same_lease(const struct share_mode_data *d,
if (lease == NULL) {
return false;
}
- return smb2_lease_key_equal(&d->leases[e->lease_idx].lease_key,
- &lease->lease_key);
+
+ return smb2_lease_equal(fsp_client_guid(fsp),
+ &lease->lease_key,
+ &d->leases[e->lease_idx].client_guid,
+ &d->leases[e->lease_idx].lease_key);
}
struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
@@ -1595,17 +1602,11 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
uint32_t *p_lease_idx,
uint32_t granted)
{
- const struct GUID *client_guid;
+ const struct GUID *client_guid = fsp_client_guid(fsp);
struct share_mode_oplock *tmp;
NTSTATUS status;
int idx;
-
- /*
- * TODO: in future we can have multiple connections...
- */
- client_guid = &fsp->conn->sconn->client->connections->smb2.client.guid;
-
idx = find_share_mode_oplock(d, client_guid, &lease->lease_key);
if (idx != -1) {
@@ -1759,7 +1760,7 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
e_lease_type = get_lease_type(d, e);
if ((granted & SMB2_LEASE_WRITE) &&
- !is_same_lease(d, e, lease) &&
+ !is_same_lease(fsp, d, e, lease) &&
!share_mode_stale_pid(d, i)) {
/*
* Can grant only one writer
--
1.9.1
From 5a4726c5a4ead1bb3231f673f7ecc975c7cd9c29 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 4 Nov 2014 21:47:51 -0800
Subject: [PATCH 10/16] s3: leases: Ensure the client guids match when doing a
durable reconnect.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/durable.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 73bcd58..6e668ce 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -737,6 +737,17 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
fsp_free(fsp);
return NT_STATUS_NO_MEMORY;
}
+
+ /*
+ * Ensure the existing client guid matches the
+ * stored one in the share_mode_oplock.
+ */
+ if (!GUID_equal(fsp_client_guid(fsp),
+ &o->client_guid)) {
+ TALLOC_FREE(lck);
+ fsp_free(fsp);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
}
fsp->initial_allocation_size = cookie.initial_allocation_size;
--
1.9.1
From a93e2365fa96adba87adf2d4c854ad0d39c5ddb7 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 11/16] NEEDED? s4:torture:smb2: ignore the allocation size in
the create return against samba3
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source4/torture/smb2/durable_open.c | 5 ++++-
source4/torture/smb2/lease.c | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index c3d63d1..7e1e6a2 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -25,6 +25,7 @@
#include "libcli/smb2/smb2_calls.h"
#include "../libcli/smb/smbXcli_base.h"
#include "torture/torture.h"
+#include "torture/util.h"
#include "torture/smb2/proto.h"
#include "../libcli/smb/smbXcli_base.h"
@@ -53,7 +54,9 @@
#define CHECK_CREATED(__io, __created, __attribute) \
do { \
CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
- CHECK_VAL((__io)->out.alloc_size, 0); \
+ if (!TARGET_IS_SAMBA3(tctx)) { \
+ CHECK_VAL((__io)->out.alloc_size, 0); \
+ } \
CHECK_VAL((__io)->out.size, 0); \
CHECK_VAL((__io)->out.file_attr, (__attribute)); \
CHECK_VAL((__io)->out.reserved2, 0); \
diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 4326958..7ea2eab 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -25,6 +25,7 @@
#include "libcli/smb2/smb2_calls.h"
#include "torture/torture.h"
#include "torture/smb2/proto.h"
+#include "torture/util.h"
#include "libcli/smb/smbXcli_base.h"
#define CHECK_VAL(v, correct) do { \
@@ -45,7 +46,9 @@
#define CHECK_CREATED(__io, __created, __attribute) \
do { \
CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
- CHECK_VAL((__io)->out.alloc_size, 0); \
+ if (!TARGET_IS_SAMBA3(tctx)) { \
+ CHECK_VAL((__io)->out.alloc_size, 0); \
+ } \
CHECK_VAL((__io)->out.size, 0); \
CHECK_VAL((__io)->out.file_attr, (__attribute)); \
CHECK_VAL((__io)->out.reserved2, 0); \
--
1.9.1
From 533f5033d494ffa091b9a7f8b6a14e4a04253cc9 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:34:53 -0700
Subject: [PATCH 12/16] s3:param: Add "smb2 leases" parameter. Default "false".
Signed-off-by: Jeremy Allison <jra at samba.org>
---
docs-xml/smbdotconf/locking/kerneloplocks.xml | 1 +
docs-xml/smbdotconf/locking/oplocks.xml | 1 +
docs-xml/smbdotconf/locking/smb2leases.xml | 24 ++++++++++++++++++++++++
lib/param/param_table.c | 9 +++++++++
source3/param/loadparm.c | 1 +
5 files changed, 36 insertions(+)
create mode 100644 docs-xml/smbdotconf/locking/smb2leases.xml
diff --git a/docs-xml/smbdotconf/locking/kerneloplocks.xml b/docs-xml/smbdotconf/locking/kerneloplocks.xml
index 8e3bba5..d8fe223 100644
--- a/docs-xml/smbdotconf/locking/kerneloplocks.xml
+++ b/docs-xml/smbdotconf/locking/kerneloplocks.xml
@@ -25,5 +25,6 @@
<related>oplocks</related>
<related>level2 oplocks</related>
+<related>smb2 leases</related>
<value type="default">no</value>
</samba:parameter>
diff --git a/docs-xml/smbdotconf/locking/oplocks.xml b/docs-xml/smbdotconf/locking/oplocks.xml
index a56e921..a5e163b 100644
--- a/docs-xml/smbdotconf/locking/oplocks.xml
+++ b/docs-xml/smbdotconf/locking/oplocks.xml
@@ -25,5 +25,6 @@
<related>kernel oplocks</related>
<related>level2 oplocks</related>
+<related>smb2 leases</related>
<value type="default">yes</value>
</samba:parameter>
diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
new file mode 100644
index 0000000..e7b1fb4
--- /dev/null
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -0,0 +1,24 @@
+<samba:parameter name="smb2 leases"
+ context="G"
+ type="boolean"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>
+ This boolean option tells <command moreinfo="none">smbd</command> whether to
+ globally negotiate SMB2 leases on file open requests. Leasing is an SMB2-only
+ feature which allows clients to aggressively cache files locally above and
+ beyond the caching allowed by SMB1 oplocks. This (experimental) parameter is
+ set to off by default until the SMB2 leasing code is declared fully stable.
+ </para>
+
+ <para>
+ This is only available with <smbconfoption name="oplocks">yes</smbconfoption>
+ and <smbconfoption name="kernel oplocks">no</smbconfoption>.
+ </para>
+</description>
+
+<related>oplocks</related>
+<related>kernel oplocks</related>
+<related>level2 oplocks</related>
+<value type="default">no</value>
+</samba:parameter>
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index 7a13e6f..f46dd99 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -3010,6 +3010,15 @@ struct parm_struct parm_table[] = {
.flags = FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL,
},
{
+ .label = "smb2 leases",
+ .type = P_BOOL,
+ .p_class = P_GLOBAL,
+ .offset = GLOBAL_VAR(smb2_leases),
+ .special = NULL,
+ .enum_list = NULL,
+ .flags = FLAG_ADVANCED,
+ },
+ {
.label = "locking",
.type = P_BOOL,
.p_class = P_LOCAL,
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index d6ba8fb..4a85045 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -854,6 +854,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
Globals.smb2_max_write = DEFAULT_SMB2_MAX_WRITE;
Globals.smb2_max_trans = DEFAULT_SMB2_MAX_TRANSACT;
Globals.ismb2_max_credits = DEFAULT_SMB2_MAX_CREDITS;
+ Globals.smb2_leases = false;
string_set(Globals.ctx, &Globals.ncalrpc_dir, get_dyn_NCALRPCDIR());
--
1.9.1
From ac44bd0e85a36778ee5bd1a71b5426ad34578c99 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 13/16] s3:smb2_negprot: announce support for SMB2.1 leases.
We only do this with "smb2 leases = yes"
and the default values for "oplocks = yes"
and "kernel oplocks = no".
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
source3/smbd/smb2_negprot.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c
index 6904972..9a1ca9c 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -230,6 +230,14 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
capabilities |= SMB2_CAP_DFS;
}
+ if (protocol >= PROTOCOL_SMB2_10 &&
+ lp_smb2_leases() &&
+ lp_oplocks(GLOBAL_SECTION_SNUM) &&
+ !lp_kernel_oplocks(GLOBAL_SECTION_SNUM))
+ {
+ capabilities |= SMB2_CAP_LEASING;
+ }
+
if ((protocol >= PROTOCOL_SMB2_24) &&
(lp_smb_encrypt(-1) != SMB_SIGNING_OFF) &&
(in_capabilities & SMB2_CAP_ENCRYPTION)) {
--
1.9.1
From 7c87f55b83e784d46f138aa41d67225c5b315481 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:34:53 -0700
Subject: [PATCH 14/16] selftest:Samba3: use "smb2 leases = yes"
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
selftest/knownfail | 14 +-------------
selftest/target/Samba3.pm | 1 +
2 files changed, 2 insertions(+), 13 deletions(-)
diff --git a/selftest/knownfail b/selftest/knownfail
index 1c4f446..66b4084 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -189,25 +189,13 @@
^samba3.smb2.notify.valid-req
^samba3.smb2.notify.dir
^samba3.smb2.notify.rec
-^samba3.smb2.durable-open.lock-lease
^samba3.smb2.durable-open.delete_on_close2
-^samba3.smb2.durable-v2-open.open-lease
-^samba3.smb2.durable-v2-open.persistent-open-lease
^samba3.smb2.durable-v2-open.app-instance
^samba4.smb2.ioctl.req_resume_key\(dc\) # not supported by s4 ntvfs server
^samba4.smb2.ioctl.copy_chunk_\w*\(dc\) # not supported by s4 ntvfs server
^samba3.smb2.dir.one
^samba3.smb2.dir.modify
-^samba3.smb2.lease.request
-^samba3.smb2.lease.upgrade
-^samba3.smb2.lease.break
-^samba3.smb2.lease.oplock
-^samba3.smb2.lease.multibreak
-^samba3.smb2.lease.v2_request
-^samba3.smb2.lease.v2_request_parent
-^samba3.smb2.lease.break_twice
-^samba3.smb2.lease.nobreakself
-^samba3.smb2.lease.v2_epoch1
+^samba3.smb2.lease.v2_request\(.*\)$
^samba3.smb2.oplock.batch20
^samba3.smb2.oplock.stream1
^samba3.smb2.streams.rename
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 48b8164..ae0a3a6 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1051,6 +1051,7 @@ sub provision($$$$$$)
kernel oplocks = no
kernel change notify = no
+ smb2 leases = yes
syslog = no
printing = bsd
--
1.9.1
From ec35d449002792dce8b89676ec8870c762e6d6c0 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 8 Nov 2014 09:45:59 +0100
Subject: [PATCH 15/16] s3:smbd: document the interaction between "smb2 leases"
and "write cache size"
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
docs-xml/smbdotconf/locking/smb2leases.xml | 3 +++
docs-xml/smbdotconf/tuning/writecachesize.xml | 3 +++
source3/smbd/fileio.c | 5 +++++
3 files changed, 11 insertions(+)
diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
index e7b1fb4..5b6fc31 100644
--- a/docs-xml/smbdotconf/locking/smb2leases.xml
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -15,10 +15,13 @@
This is only available with <smbconfoption name="oplocks">yes</smbconfoption>
and <smbconfoption name="kernel oplocks">no</smbconfoption>.
</para>
+
+ <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
</description>
<related>oplocks</related>
<related>kernel oplocks</related>
<related>level2 oplocks</related>
+<related>write cache size</related>
<value type="default">no</value>
</samba:parameter>
diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml
index 12965b4..d7e2cc5 100644
--- a/docs-xml/smbdotconf/tuning/writecachesize.xml
+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml
@@ -21,8 +21,11 @@
<para>The integer parameter specifies the size of this cache
(per oplocked file) in bytes.</para>
+
+ <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
</description>
+<related>smb2 leases</related>
<value type="default">0</value>
<value type="example">262144<comment> for a 256k cache size per file</comment></value>
</samba:parameter>
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
index 37c3f66..6ba7285 100644
--- a/source3/smbd/fileio.c
+++ b/source3/smbd/fileio.c
@@ -339,6 +339,11 @@ ssize_t write_file(struct smb_request *req,
if (!fsp->modified &&
EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) &&
(wcp == NULL)) {
+ /*
+ * Note: no write cache with leases!
+ * as the handles would have to share the write cache
+ * that's possible but an improvement for another day...
+ */
setup_write_cache(fsp, fsp->fsp_name->st.st_ex_size);
wcp = fsp->wcp;
}
--
1.9.1
From c8ca2664dc56d74e52397bfa6b02f28c8fd30a67 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 8 Nov 2014 09:47:01 +0100
Subject: [PATCH 16/16] docs-xml: document the interaction between "write cache
size" and "aio read/write size"
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
docs-xml/smbdotconf/tuning/writecachesize.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml
index d7e2cc5..e2fa17f 100644
--- a/docs-xml/smbdotconf/tuning/writecachesize.xml
+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml
@@ -25,6 +25,8 @@
<para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
</description>
+<related>aio read size</related>
+<related>aio write size</related>
<related>smb2 leases</related>
<value type="default">0</value>
<value type="example">262144<comment> for a 256k cache size per file</comment></value>
--
1.9.1
-------------- next part --------------
From 5eacb9604c0658852bbbc271c2c954b75f2e8b15 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 30 Oct 2014 10:10:07 +0100
Subject: [PATCH 1/3] SQ grant_fsp_oplock_type: minimize the diff
---
source3/smbd/open.c | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index e353ec1..aa250f2 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1700,22 +1700,22 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
d->modified = true;
return NT_STATUS_OK;
-
}
-static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp,
+static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
+ struct files_struct *fsp,
struct share_mode_lock *lck,
int oplock_request,
- uint32_t create_disposition,
struct smb2_lease *lease)
{
struct share_mode_data *d = lck->data;
- bool got_handle_lease, got_oplock;
+ bool got_handle_lease = false;
+ bool got_oplock = false;
uint32_t i;
uint32_t granted;
uint32_t lease_idx = UINT32_MAX;
+ bool ok;
NTSTATUS status;
- bool ret;
if (oplock_request & INTERNAL_OPEN_ONLY) {
/* No oplocks on internal open. */
@@ -1750,9 +1750,6 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
granted &= ~SMB2_LEASE_READ;
}
- got_handle_lease = false;
- got_oplock = false;
-
for (i=0; i<d->num_share_modes; i++) {
struct share_mode_entry *e = &d->share_modes[i];
uint32_t e_lease_type;
@@ -1825,9 +1822,6 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
}
}
- DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
- fsp->oplock_type, fsp_str_dbg(fsp)));
-
status = set_file_oplock(fsp);
if (!NT_STATUS_IS_OK(status)) {
/*
@@ -1836,16 +1830,22 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
fsp->oplock_type = NO_OPLOCK;
}
- if (!set_share_mode(lck, fsp, get_current_uid(fsp->conn),
+ ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
req ? req->mid : 0,
- fsp->oplock_type, lease_idx)) {
+ fsp->oplock_type, lease_idx);
+ if (!ok) {
return NT_STATUS_NO_MEMORY;
}
- ret = update_num_read_oplocks(fsp, lck);
- if (!ret) {
+
+ ok = update_num_read_oplocks(fsp, lck);
+ if (!ok) {
del_share_mode(lck, fsp);
return NT_STATUS_INTERNAL_ERROR;
}
+
+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+ fsp->oplock_type, fsp_str_dbg(fsp)));
+
return NT_STATUS_OK;
}
@@ -3109,9 +3109,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* Setup the oplock info in both the shared memory and
* file structs.
*/
-
- status = grant_fsp_oplock_type(req, fsp, lck, oplock_request,
- create_disposition, lease);
+ status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(lck);
fd_close(fsp);
--
1.9.1
From 1dfd83f7d5ac12834ebcd23e9f685fa9f006a6a1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 10:56:52 +0100
Subject: [PATCH 2/3] SQ fix grant_fsp_oplock_type() for leases allow_level2
... simplify
---
source3/smbd/open.c | 31 ++++++++++++++++---------------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index aa250f2..af99f36 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1776,14 +1776,23 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
}
}
- if (oplock_request == LEASE_OPLOCK) {
+ if ((granted & SMB2_LEASE_READ) && !(granted & SMB2_LEASE_WRITE)) {
+ bool allow_level2 =
+ (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ lp_level2_oplocks(SNUM(fsp->conn));
- fsp->oplock_type = LEASE_OPLOCK;
+ if (!allow_level2) {
+ granted = SMB2_LEASE_NONE;
+ }
+ }
+ if (oplock_request == LEASE_OPLOCK) {
if (got_oplock) {
- granted &= SMB2_LEASE_READ;
+ granted &= ~SMB2_LEASE_HANDLE;
}
+ fsp->oplock_type = LEASE_OPLOCK;
+
status = grant_fsp_lease(fsp, lck->data, lease, &lease_idx,
granted);
if (!NT_STATUS_IS_OK(status)) {
@@ -1793,6 +1802,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
lease->lease_state = d->leases[lease_idx].current_state;
DEBUG(10, ("lease_state=%d\n", lease->lease_state));
} else {
+ if (got_handle_lease) {
+ granted = SMB2_LEASE_NONE;
+ }
+
switch (granted) {
case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
@@ -1808,18 +1821,6 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
fsp->oplock_type = NO_OPLOCK;
break;
}
- if (fsp->oplock_type == LEVEL_II_OPLOCK) {
- bool allow_level2 =
- (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
- lp_level2_oplocks(SNUM(fsp->conn));
-
- if (!allow_level2) {
- fsp->oplock_type = NO_OPLOCK;
- }
- }
- if (got_handle_lease) {
- fsp->oplock_type = NO_OPLOCK;
- }
}
status = set_file_oplock(fsp);
--
1.9.1
From 71a36122fb64811b064b58621d81ad80010fb42f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 10:57:32 +0100
Subject: [PATCH 3/3] SQ no leases for kernel oplocks
---
source3/smbd/open.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index af99f36..838e550 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1727,6 +1727,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
if (oplock_request == LEASE_OPLOCK) {
granted = lease->lease_state;
+ if (lp_kernel_oplocks(SNUM(fsp->conn)) {
+ DEBUG(10, ("No lease granted because kernel oplocks are enabled\n"));
+ granted = SMB2_LEASE_NONE;
+ }
if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
DEBUG(10, ("No read or write lease requested\n"));
granted = SMB2_LEASE_NONE;
@@ -1821,14 +1825,14 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
fsp->oplock_type = NO_OPLOCK;
break;
}
- }
- status = set_file_oplock(fsp);
- if (!NT_STATUS_IS_OK(status)) {
- /*
- * Could not get the kernel oplock
- */
- fsp->oplock_type = NO_OPLOCK;
+ status = set_file_oplock(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Could not get the kernel oplock
+ */
+ fsp->oplock_type = NO_OPLOCK;
+ }
}
ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
--
1.9.1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20141111/03371bcf/attachment.pgp>
More information about the samba-technical
mailing list