Current SMB2 leases patchset (WIP).
Jeremy Allison
jra at samba.org
Fri Oct 24 18:20:25 MDT 2014
Here is a snapshot of Volker's leases
code that applies onto master with a
few fixes and additional changes created
by me. It's currently being tested at
a couple of vendors, but I wanted to
make it more widely available (yes
I know I should upload it to a branch
on git.samba.org :-) for people to
look at and test.
If you apply it, you'll need to
add:
smb2 leases = yes
in the [global] section of your
smb.conf (it's set to off be
default).
I think it's getting close to official
submission for master (and hopefully
then into 4.2.0).
Enjoy !
Cheers,
Jeremy.
-------------- next part --------------
From 81954bef097990817479ecc5c29792400891b70d Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 22 Sep 2014 21:21:36 +0200
Subject: [PATCH 01/21] s3: leases: mask off
SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET.
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
libcli/smb/smb2_lease.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c
index f97f096..6fc26f2 100644
--- a/libcli/smb/smb2_lease.c
+++ b/libcli/smb/smb2_lease.c
@@ -47,6 +47,7 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len,
switch (version) {
case 1:
ZERO_STRUCT(lease->parent_lease_key);
+ lease->lease_flags &= ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET;
lease->lease_epoch = 0;
break;
case 2:
--
2.1.0.rc2.206.gedb03e5
From 34f067bc9455f0b53191b9eced4e297ebda3ff89 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 7 Jul 2014 11:51:04 +0000
Subject: [PATCH 02/21] smbd: Implementation of SMB2.1 leases.
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
libcli/smb/smb2_lease.c | 6 +
libcli/smb/smb2_lease.h | 2 +
selftest/knownfail | 12 -
source3/include/smb.h | 1 +
source3/librpc/idl/leases_db.idl | 23 ++
source3/librpc/idl/open_files.idl | 30 ++
source3/librpc/idl/wscript_build | 1 +
source3/librpc/wscript_build | 7 +-
source3/locking/brlock.c | 125 ++++----
source3/locking/leases_db.c | 251 ++++++++++++++++
source3/locking/leases_db.h | 44 +++
source3/locking/locking.c | 140 ++++++++-
source3/locking/proto.h | 17 +-
source3/locking/share_mode_lock.c | 4 +-
source3/smbd/durable.c | 28 +-
source3/smbd/files.c | 34 +++
source3/smbd/globals.h | 10 +-
source3/smbd/open.c | 577 +++++++++++++++++++++++++++---------
source3/smbd/oplock.c | 362 ++++++++++++++++------
source3/smbd/proto.h | 14 +
source3/smbd/server.c | 5 +
source3/smbd/smb2_break.c | 194 +++++++++++-
source3/smbd/smb2_create.c | 151 +++++++++-
source3/smbd/smb2_negprot.c | 4 +
source3/smbd/smb2_server.c | 25 ++
source3/utils/status.c | 2 +
source3/wscript_build | 6 +
source4/torture/smb2/durable_open.c | 5 +-
source4/torture/smb2/lease.c | 5 +-
29 files changed, 1757 insertions(+), 328 deletions(-)
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/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c
index 6fc26f2..70dd3d4 100644
--- a/libcli/smb/smb2_lease.c
+++ b/libcli/smb/smb2_lease.c
@@ -86,3 +86,9 @@ bool smb2_lease_push(const struct smb2_lease *lease, uint8_t *buf, size_t len)
return true;
}
+
+bool smb2_lease_key_equal(const struct smb2_lease_key *k1,
+ const struct smb2_lease_key *k2)
+{
+ return ((k1->data[0] == k2->data[0]) && (k1->data[1] == k2->data[1]));
+}
diff --git a/libcli/smb/smb2_lease.h b/libcli/smb/smb2_lease.h
index ba8178d..9db239d 100644
--- a/libcli/smb/smb2_lease.h
+++ b/libcli/smb/smb2_lease.h
@@ -32,5 +32,7 @@
ssize_t smb2_lease_pull(const uint8_t *buf, size_t len,
struct smb2_lease *lease);
bool smb2_lease_push(const struct smb2_lease *lease, uint8_t *buf, size_t len);
+bool smb2_lease_key_equal(const struct smb2_lease_key *k1,
+ const struct smb2_lease_key *k2);
#endif /* _LIBCLI_SMB_SMB2_LEASE_H_ */
diff --git a/selftest/knownfail b/selftest/knownfail
index 3d73495..5a9a865 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -187,25 +187,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.oplock.batch20
^samba3.smb2.oplock.stream1
^samba3.smb2.streams.rename
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/leases_db.idl b/source3/librpc/idl/leases_db.idl
new file mode 100644
index 0000000..9ec8912
--- /dev/null
+++ b/source3/librpc/idl/leases_db.idl
@@ -0,0 +1,23 @@
+#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 {
+ file_id id;
+ [string,charset(UTF8)] char *filename;
+ [string,charset(UTF8)] char *stream_name;
+ } leases_db_value;
+}
diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl
index 4278301..6604ff3 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;
+ uint16 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/brlock.c b/source3/locking/brlock.c
index 295e147..4ad9a21 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 have_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;
}
@@ -1841,7 +1841,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;
@@ -1865,15 +1864,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)) {
@@ -1882,19 +1873,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);
@@ -1917,6 +1909,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.
@@ -1926,16 +1944,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);
@@ -1950,30 +1966,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;
@@ -1999,28 +1997,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)
@@ -2052,7 +2041,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
if (br_lock == NULL) {
goto fail;
}
- br_lock->have_read_oplocks = rw->have_read_oplocks;
+ br_lock->num_read_oplocks = rw->num_read_oplocks;
br_lock->num_locks = rw->num_locks;
br_lock->lock_data = (struct lock_struct *)talloc_memdup(
br_lock, rw->lock_data, lock_data_size);
@@ -2082,7 +2071,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
goto fail;
}
- 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/leases_db.c b/source3/locking/leases_db.c
new file mode 100644
index 0000000..69b1362
--- /dev/null
+++ b/source3/locking/leases_db.c
@@ -0,0 +1,251 @@
+/*
+ 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;
+ struct db_record *rec;
+ NTSTATUS status;
+ bool ok;
+ struct leases_db_value value;
+ DATA_BLOB blob;
+ 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) {
+ DEBUG(10, ("%s: record exists\n", __func__));
+ TALLOC_FREE(rec);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ value = (struct leases_db_value) {
+ .id = *id,
+ .filename = filename,
+ .stream_name = stream_name,
+ };
+
+ 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)));
+ TALLOC_FREE(rec);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ 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)));
+ }
+
+ TALLOC_FREE(rec);
+ return status;
+}
+
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key)
+{
+ TDB_DATA db_key;
+ struct db_record *rec;
+ NTSTATUS status;
+ 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;
+ }
+ status = dbwrap_record_delete(rec);
+ TALLOC_FREE(rec);
+ return status;
+}
+
+struct leases_db_fetch_state {
+ void (*parser)(struct file_id id, 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->id, 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)(struct file_id id,
+ 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..ff9c362
--- /dev/null
+++ b/source3/locking/leases_db.h
@@ -0,0 +1,44 @@
+/*
+ * 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);
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ void (*parser)(struct file_id id,
+ const char *filename,
+ const char *stream_name,
+ void *private_data),
+ void *private_data);
+
+#endif /* _LEASES_DB_H_ */
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index a320068..cf1b497 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,
+ uint16_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,69 @@ 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, 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 = UINT16_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 +903,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_INVALID_PARAMETER;
+ }
+
+ 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 46eec2a..f2cf724 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,
@@ -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,
+ uint16_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/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index 12f499b..ed0fb91 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -133,7 +133,7 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx,
blob.data = dbuf.dptr;
blob.length = dbuf.dsize;
- ndr_err = ndr_pull_struct_blob(
+ ndr_err = ndr_pull_struct_blob_all(
&blob, d, d, (ndr_pull_flags_fn_t)ndr_pull_share_mode_data);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
DEBUG(1, ("ndr_pull_share_mode_lock failed: %s\n",
@@ -473,7 +473,7 @@ static int traverse_fn(struct db_record *rec, void *_state)
blob.data = value.dptr;
blob.length = value.dsize;
- ndr_err = ndr_pull_struct_blob(
+ ndr_err = ndr_pull_struct_blob_all(
&blob, d, d, (ndr_pull_flags_fn_t)ndr_pull_share_mode_data);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
DEBUG(1, ("ndr_pull_share_mode_lock failed\n"));
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..39c537c 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 smbd_server_connection *sconn,
+ 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 ccea1e9..7eda52b 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,74 +1415,98 @@ 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;
}
switch (create_disposition) {
case FILE_SUPERSEDE:
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)
@@ -1489,88 +1520,330 @@ 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 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;
+ uint16_t i;
- /* 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);
+ 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,
+ uint16_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;
+
+ 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,
+ };
+
+ 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;
+ uint16_t lease_idx = UINT16_MAX;
+ NTSTATUS status;
+ bool ret;
if (oplock_request & INTERNAL_OPEN_ONLY) {
/* No oplocks on internal open. */
- fsp->oplock_type = NO_OPLOCK;
+ 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)));
- return;
+ }
+
+ 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;
+ granted &= ~SMB2_LEASE_READ;
}
- 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;
- }
+ got_handle_lease = false;
+ got_oplock = false;
- got_level2_oplock = false;
- got_a_none_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 (op_type == NO_OPLOCK) {
- got_a_none_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;
}
- }
-
- /*
- * Match what was requested (fsp->oplock_type) with
- * what was found in the existing share modes.
- */
- if (got_level2_oplock || got_a_none_oplock) {
- if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
- fsp->oplock_type = LEVEL_II_OPLOCK;
+ if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+ !share_mode_stale_pid(d, i)) {
+ got_handle_lease = true;
}
- }
- /*
- * 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;
+ if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
+ !share_mode_stale_pid(d, i)) {
+ got_oplock = true;
+ }
}
- if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
+ if (is_stat_open(fsp->access_mask) &&
+ ((create_disposition == FILE_OPEN) ||
+ (create_disposition == FILE_OPEN_IF))) {
/*
- * We're the first level2 oplock. Indicate that in brlock.tdb.
+ * No-overwrite stat open doesn't get oplocks
*/
- struct byte_range_lock *brl;
+ granted = SMB2_LEASE_NONE;
+ }
+
+ if (oplock_request == LEASE_OPLOCK) {
+
+ 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));
- brl = brl_get_locks(talloc_tos(), fsp);
- if (brl != NULL) {
- brl_set_have_read_oplocks(brl, true);
- TALLOC_FREE(brl);
+ if (!allow_level2) {
+ fsp->oplock_type = NO_OPLOCK;
+ }
+ }
+ if (got_handle_lease) {
+ 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)));
+
+ status = set_file_oplock(fsp);
+ 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(fsp->conn),
+ req ? req->mid : 0,
+ fsp->oplock_type, lease_idx)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = update_num_read_oplocks(fsp, lck);
+ if (!ret) {
+ del_share_mode(lck, fsp);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
}
static bool request_timed_out(struct timeval request_time,
@@ -2465,8 +2738,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"));
@@ -2587,7 +2862,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);
@@ -2738,8 +3013,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
}
}
- grant_fsp_oplock_type(fsp, lck, oplock_request);
-
/*
* We have the share entry *locked*.....
*/
@@ -2798,13 +3071,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES;
}
- if (file_existed) {
- /* stat opens on existing files don't get oplocks. */
- if (is_stat_open(open_access_mask)) {
- fsp->oplock_type = NO_OPLOCK;
- }
- }
-
if (new_file_created) {
info = FILE_WAS_CREATED;
} else {
@@ -2824,20 +3090,13 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* file structs.
*/
- status = set_file_oplock(fsp);
+ status = grant_fsp_oplock_type(req, fsp, lck, oplock_request,
+ create_disposition, lease);
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)) {
+ del_share_mode(lck, fsp);
TALLOC_FREE(lck);
fd_close(fsp);
- return NT_STATUS_NO_MEMORY;
+ return status;
}
/* Handle strange delete on close create semantics. */
@@ -3331,7 +3590,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, UINT16_MAX)) {
TALLOC_FREE(lck);
fd_close(fsp);
file_free(req, fsp);
@@ -3816,6 +4075,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
*/
@@ -3871,6 +4172,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 17cb22e..eae5f0f 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);
}
/****************************************************************************
@@ -141,8 +139,69 @@ static void downgrade_file_oplock(files_struct *fsp)
sconn->oplocks.exclusive_open--;
sconn->oplocks.level_II_open++;
fsp->sent_oplock_break = NO_BREAK_SENT;
+}
- 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)
+{
+ struct share_mode_data *d = lck->data;
+ struct byte_range_lock *br_lck;
+ uint32_t num_read_oplocks = 0;
+ uint32_t i;
+
+ 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_READ) {
+ num_read_oplocks += 1;
+ }
+ }
+
+ 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;
}
/****************************************************************************
@@ -167,44 +226,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 "
@@ -213,6 +234,16 @@ 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) {
+ 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;
}
@@ -224,7 +255,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)));
@@ -244,17 +274,78 @@ bool downgrade_oplock(files_struct *fsp)
}
downgrade_file_oplock(fsp);
+ TALLOC_FREE(fsp->oplock_timeout);
- 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: 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)));
}
TALLOC_FREE(lck);
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.
****************************************************************************/
@@ -426,7 +517,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,
@@ -467,23 +557,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 (!lp_level2_oplocks(SNUM(fsp->conn))) {
+ DEBUG(10, ("no level2 oplocks by config\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;
+ 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
@@ -493,21 +602,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);
}
@@ -575,6 +696,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,
@@ -593,6 +715,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;
/*
* If this file is level II oplocked then we need
@@ -609,11 +732,23 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
return;
}
+ num_read_oplocks = 0;
+
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;
}
+ 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
@@ -622,7 +757,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;
@@ -630,6 +765,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"));
@@ -639,6 +781,19 @@ 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)
@@ -647,6 +802,7 @@ static void do_break_to_none(struct tevent_context *ctx,
private_data, struct break_to_none_state);
int i;
struct share_mode_lock *lck;
+ struct share_mode_data *d;
lck = get_existing_share_mode_lock(talloc_tos(), state->id);
if (lck == NULL) {
@@ -654,15 +810,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;
}
@@ -677,15 +879,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 ));
@@ -693,13 +895,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 68c2da2..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,10 +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 5c079ec..a11cb5f 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_negprot.c b/source3/smbd/smb2_negprot.c
index 6904972..a27ab0c 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -230,6 +230,10 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
capabilities |= SMB2_CAP_DFS;
}
+ if (protocol >= PROTOCOL_SMB2_10) {
+ capabilities |= SMB2_CAP_LEASING;
+ }
+
if ((protocol >= PROTOCOL_SMB2_24) &&
(lp_smb_encrypt(-1) != SMB_SIGNING_OFF) &&
(in_capabilities & SMB2_CAP_ENCRYPTION)) {
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 689bfd7..cff10d8 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 smbd_server_connection *sconn,
+ 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(sconn, 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 7bbcea5..08faca8 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -177,6 +177,8 @@ static void 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 54ba3a7..4328500 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -618,6 +618,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
LIBAFS
RPC_SERVICE
NDR_SMBXSRV
+ LEASES_DB
LIBASYS
sysquotas
ccan-hash
@@ -635,9 +636,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',
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); \
--
2.1.0.rc2.206.gedb03e5
From 696f58b72387bf1e8e269cfebd58bc0897b22696 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Sun, 21 Sep 2014 09:32:25 +0200
Subject: [PATCH 03/21] smbd: use smbXsrv_connection as argument to
smbd_smb2_send_lease_break()
Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/globals.h | 2 +-
source3/smbd/smb2_server.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 39c537c..f297ff7 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -250,7 +250,7 @@ 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 smbd_server_connection *sconn,
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
struct smbXsrv_session *session,
struct smbXsrv_tcon *tcon,
uint16_t new_epoch,
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index cff10d8..36ac0d7 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2888,7 +2888,7 @@ 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 smbd_server_connection *sconn,
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
struct smbXsrv_session *session,
struct smbXsrv_tcon *tcon,
uint16_t new_epoch,
@@ -2910,7 +2910,7 @@ NTSTATUS smbd_smb2_send_lease_break(struct smbd_server_connection *sconn,
SIVAL(body, 0x24, 0); /* AccessMaskHint, MUST be 0 */
SIVAL(body, 0x28, 0); /* ShareMaskHint, MUST be 0 */
- return smbd_smb2_send_break(sconn, session, tcon, body, sizeof(body));
+ return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
}
static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
--
2.1.0.rc2.206.gedb03e5
From 15dbd4f9cb3463c8fccd6d1d47387a265f854dcb Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Sun, 21 Sep 2014 09:46:30 +0200
Subject: [PATCH 04/21] selftest: samba3.smb2.leases.v2_request_parent succeeds
Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
selftest/knownfail | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/selftest/knownfail b/selftest/knownfail
index 5a9a865..d4cae25 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -193,7 +193,7 @@
^samba4.smb2.ioctl.copy_chunk_\w*\(dc\) # not supported by s4 ntvfs server
^samba3.smb2.dir.one
^samba3.smb2.dir.modify
-^samba3.smb2.lease.v2_request
+^samba3.smb2.lease.v2_request\(.*\)$
^samba3.smb2.oplock.batch20
^samba3.smb2.oplock.stream1
^samba3.smb2.streams.rename
--
2.1.0.rc2.206.gedb03e5
From a84f84092cdd19bf22179b17e6115ae8380d3ff3 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 05/21] 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 7eda52b..7d5be7b 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1520,7 +1520,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)
{
@@ -1669,8 +1669,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 eae5f0f..eac7636 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -595,6 +595,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 a11cb5f..178dba9 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)
--
2.1.0.rc2.206.gedb03e5
From b8dbe317f88b3609a1d32456d4455a74b5fdf43c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 05:18:54 +0200
Subject: [PATCH 06/21] smbd: Add share_mode_forall->share_entry_forall
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/locking/proto.h | 4 ++--
source3/locking/share_mode_lock.c | 4 ++--
source3/rpc_server/srvsvc/srv_srvsvc_nt.c | 8 ++++----
source3/utils/status.c | 2 +-
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index f2cf724..49e74df 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -199,8 +199,8 @@ bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash);
bool set_sticky_write_time(struct file_id fileid, struct timespec write_time);
bool set_write_time(struct file_id fileid, struct timespec write_time);
struct timespec get_share_mode_write_time(struct share_mode_lock *lck);
-int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *,
- const char *, void *),
+int share_entry_forall(void (*fn)(const struct share_mode_entry *, const char *,
+ const char *, void *),
void *private_data);
bool share_mode_cleanup_disconnected(struct file_id id,
uint64_t open_persistent_id);
diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index ed0fb91..22a4474 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -499,8 +499,8 @@ static int traverse_fn(struct db_record *rec, void *_state)
share mode system.
********************************************************************/
-int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *,
- const char *, void *),
+int share_entry_forall(void (*fn)(const struct share_mode_entry *,
+ const char *, const char *, void *),
void *private_data)
{
struct forall_state state;
diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
index 855b8c7..d2f05f3 100644
--- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
+++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
@@ -166,7 +166,7 @@ static WERROR net_enum_files(TALLOC_CTX *ctx,
f_enum_cnt.username = username;
f_enum_cnt.ctr3 = *ctr3;
- share_mode_forall( enum_file_fn, (void *)&f_enum_cnt );
+ share_entry_forall( enum_file_fn, (void *)&f_enum_cnt );
*ctr3 = f_enum_cnt.ctr3;
@@ -867,7 +867,7 @@ static void net_count_files_for_all_sess(struct srvsvc_NetSessCtr1 *ctr1,
s_file_info.resume_handle = resume_handle;
s_file_info.num_entries = num_entries;
- share_mode_forall(count_sess_files_fn, &s_file_info);
+ share_entry_forall(count_sess_files_fn, &s_file_info);
}
/*******************************************************************
@@ -984,7 +984,7 @@ static void count_share_opens(struct srvsvc_NetConnInfo1 *arr,
sfs.resp_entries = resp_entries;
sfs.total_entries = total_entries;
- share_mode_forall(share_file_fn, &sfs);
+ share_entry_forall(share_file_fn, &sfs);
}
/****************************************************************************
@@ -2744,7 +2744,7 @@ WERROR _srvsvc_NetFileClose(struct pipes_struct *p,
r->out.result = WERR_BADFILE;
state.r = r;
state.msg_ctx = p->msg_ctx;
- share_mode_forall(enum_file_close_fn, &state);
+ share_entry_forall(enum_file_close_fn, &state);
return r->out.result;
}
diff --git a/source3/utils/status.c b/source3/utils/status.c
index 08faca8..cfc2bb6 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -528,7 +528,7 @@ int main(int argc, const char *argv[])
goto done;
}
- result = share_mode_forall(print_share_mode, NULL);
+ result = share_entry_forall(print_share_mode, NULL);
if (result == 0) {
d_printf("No locked files\n");
--
2.1.0.rc2.206.gedb03e5
From 11721830c910234dca6de2e0121c2bef59d3bd91 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 05:45:49 +0200
Subject: [PATCH 07/21] smbd: Introduce share_mode_forall
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/locking/proto.h | 4 ++
source3/locking/share_mode_lock.c | 81 +++++++++++++++++++++++++++------------
2 files changed, 60 insertions(+), 25 deletions(-)
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 49e74df..ad17ed0 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -199,6 +199,10 @@ bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash);
bool set_sticky_write_time(struct file_id fileid, struct timespec write_time);
bool set_write_time(struct file_id fileid, struct timespec write_time);
struct timespec get_share_mode_write_time(struct share_mode_lock *lck);
+int share_mode_forall(void (*fn)(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data),
+ void *private_data);
int share_entry_forall(void (*fn)(const struct share_mode_entry *, const char *,
const char *, void *),
void *private_data);
diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index 22a4474..f8054f4 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -441,14 +441,12 @@ struct share_mode_lock *fetch_share_mode_unlocked(TALLOC_CTX *mem_ctx,
}
struct forall_state {
- void (*fn)(const struct share_mode_entry *entry,
- const char *sharepath,
- const char *fname,
+ void (*fn)(struct file_id fid, const struct share_mode_data *data,
void *private_data);
void *private_data;
};
-static int traverse_fn(struct db_record *rec, void *_state)
+static int share_mode_traverse_fn(struct db_record *rec, void *_state)
{
struct forall_state *state = (struct forall_state *)_state;
uint32_t i;
@@ -457,13 +455,16 @@ static int traverse_fn(struct db_record *rec, void *_state)
DATA_BLOB blob;
enum ndr_err_code ndr_err;
struct share_mode_data *d;
+ struct file_id fid;
key = dbwrap_record_get_key(rec);
value = dbwrap_record_get_value(rec);
/* Ensure this is a locking_key record. */
- if (key.dsize != sizeof(struct file_id))
+ if (key.dsize != sizeof(fid)) {
return 0;
+ }
+ memcpy(&fid, key.dptr, sizeof(fid));
d = talloc(talloc_tos(), struct share_mode_data);
if (d == NULL) {
@@ -485,11 +486,55 @@ static int traverse_fn(struct db_record *rec, void *_state)
}
for (i=0; i<d->num_share_modes; i++) {
d->share_modes[i].stale = false; /* [skip] in idl */
- state->fn(&d->share_modes[i],
- d->servicepath, d->base_name,
- state->private_data);
}
+
+ state->fn(fid, d, state->private_data);
+
TALLOC_FREE(d);
+ return 0;
+}
+
+int share_mode_forall(void (*fn)(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data),
+ void *private_data)
+{
+ struct forall_state state = { .fn = fn, .private_data = private_data };
+ NTSTATUS status;
+ int count;
+
+ if (lock_db == NULL) {
+ return 0;
+ }
+
+ status = dbwrap_traverse_read(lock_db, share_mode_traverse_fn,
+ &state, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ return count;
+}
+
+struct share_entry_forall_state {
+ void (*fn)(const struct share_mode_entry *e,
+ const char *service_path, const char *base_name,
+ void *private_data);
+ void *private_data;
+};
+
+static int share_entry_traverse_fn(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data)
+{
+ struct share_entry_forall_state *state = private_data;
+ uint32_t i;
+
+ for (i=0; i<data->num_share_modes; i++) {
+ state->fn(&data->share_modes[i],
+ data->servicepath, data->base_name,
+ state->private_data);
+ }
return 0;
}
@@ -503,24 +548,10 @@ int share_entry_forall(void (*fn)(const struct share_mode_entry *,
const char *, const char *, void *),
void *private_data)
{
- struct forall_state state;
- NTSTATUS status;
- int count;
-
- if (lock_db == NULL)
- return 0;
-
- state.fn = fn;
- state.private_data = private_data;
+ struct share_entry_forall_state state = {
+ .fn = fn, .private_data = private_data };
- status = dbwrap_traverse_read(lock_db, traverse_fn, (void *)&state,
- &count);
-
- if (!NT_STATUS_IS_OK(status)) {
- return -1;
- } else {
- return count;
- }
+ return share_mode_forall(share_entry_traverse_fn, &state);
}
bool share_mode_cleanup_disconnected(struct file_id fid,
--
2.1.0.rc2.206.gedb03e5
From 7fb36f606d63d35abb2c3a3caa115800ee363a3e 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 08/21] 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>
---
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 178dba9..abe6709 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -61,11 +61,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);
@@ -76,6 +71,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) {
@@ -186,12 +192,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);
--
2.1.0.rc2.206.gedb03e5
From 95b9664b14ed0aa33b0637ead8579b664968486d 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 09/21] 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>
---
libcli/smb/smb2_lease.c | 1 +
librpc/idl/smb2_lease_struct.idl | 1 +
source3/smbd/open.c | 4 +++-
source3/smbd/oplock.c | 3 ++-
source3/smbd/smb2_create.c | 4 ++--
5 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c
index 70dd3d4..41eafc9 100644
--- a/libcli/smb/smb2_lease.c
+++ b/libcli/smb/smb2_lease.c
@@ -43,6 +43,7 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len,
lease->lease_state = IVAL(buf, 16);
lease->lease_flags = IVAL(buf, 20);
lease->lease_duration = BVAL(buf, 24);
+ lease->lease_version = version;
switch (version) {
case 1:
diff --git a/librpc/idl/smb2_lease_struct.idl b/librpc/idl/smb2_lease_struct.idl
index be80d14..5ccd8a3 100644
--- a/librpc/idl/smb2_lease_struct.idl
+++ b/librpc/idl/smb2_lease_struct.idl
@@ -28,6 +28,7 @@ interface smb2_lease_struct
uint32 lease_flags;
hyper lease_duration; /* should be 0 */
smb2_lease_key parent_lease_key;
+ uint16 lease_version;
uint16 lease_epoch;
} smb2_lease;
};
\ No newline at end of file
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 7d5be7b..4886f3d 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1663,7 +1663,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 eac7636..0026bd5 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -595,7 +595,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;
--
2.1.0.rc2.206.gedb03e5
From a38ab41b608dc3508b2a712909b9a713bc7a0780 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 10/21] s3: leases - break to none with FILE_OVERWRITE
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra 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 4886f3d..a5a8dd0 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1446,6 +1446,7 @@ static bool delay_for_oplock(files_struct *fsp,
switch (create_disposition) {
case FILE_SUPERSEDE:
case FILE_OVERWRITE_IF:
+ case FILE_OVERWRITE:
will_overwrite = true;
break;
default:
--
2.1.0.rc2.206.gedb03e5
From cdc652fb34b1e07f186e9536146abade6d302e79 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 24 Sep 2014 03:30:35 +0200
Subject: [PATCH 11/21] s3: leases - oplock break errors
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/smb2_break.c | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index abe6709..f9a69a5 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -42,6 +42,8 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
NTSTATUS status;
const uint8_t *inbody;
uint8_t in_oplock_level;
+ uint8_t open_oplock;
+ bool breaking;
uint64_t in_file_id_persistent;
uint64_t in_file_id_volatile;
struct files_struct *in_fsp;
@@ -71,15 +73,35 @@ 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) {
+ breaking = (in_fsp->oplock_timeout != NULL);
+ open_oplock = in_fsp->oplock_type;
+
+ if ((in_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) && !breaking) {
+ return smbd_smb2_request_error(
+ req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
+ }
+
+ if (((open_oplock == OPLOCK_BATCH) ||
+ (open_oplock == OPLOCK_EXCLUSIVE)) &&
+ (!((in_oplock_level == SMB2_OPLOCK_LEVEL_II) ||
+ (in_oplock_level == SMB2_OPLOCK_LEVEL_NONE))) &&
+ !breaking) {
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);
+ if ((open_oplock == OPLOCK_LEVEL_II) &&
+ (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE) &&
+ !breaking) {
+ return smbd_smb2_request_error(
+ req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
+ }
+
+ if (((in_oplock_level == SMB2_OPLOCK_LEVEL_II) ||
+ (in_oplock_level == SMB2_OPLOCK_LEVEL_NONE)) &&
+ !breaking) {
+ return smbd_smb2_request_error(
+ req, NT_STATUS_INVALID_DEVICE_STATE);
}
subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
--
2.1.0.rc2.206.gedb03e5
From 7d30bb44add8bc06dfad38ad839e976f71941964 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 24 Sep 2014 20:46:15 +0200
Subject: [PATCH 12/21] s3: leases - allow early return for share_mode_forall
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/locking/proto.h | 10 +++----
source3/locking/share_mode_lock.c | 38 ++++++++++++++-----------
source3/rpc_server/srvsvc/srv_srvsvc_nt.c | 46 +++++++++++++++++--------------
source3/utils/status.c | 8 +++---
4 files changed, 57 insertions(+), 45 deletions(-)
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index ad17ed0..f1bddcd 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -199,12 +199,12 @@ bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash);
bool set_sticky_write_time(struct file_id fileid, struct timespec write_time);
bool set_write_time(struct file_id fileid, struct timespec write_time);
struct timespec get_share_mode_write_time(struct share_mode_lock *lck);
-int share_mode_forall(void (*fn)(struct file_id fid,
- const struct share_mode_data *data,
- void *private_data),
+int share_mode_forall(int (*fn)(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data),
void *private_data);
-int share_entry_forall(void (*fn)(const struct share_mode_entry *, const char *,
- const char *, void *),
+int share_entry_forall(int (*fn)(const struct share_mode_entry *, const char *,
+ const char *, void *),
void *private_data);
bool share_mode_cleanup_disconnected(struct file_id id,
uint64_t open_persistent_id);
diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index f8054f4..5c5f3ed 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -441,8 +441,8 @@ struct share_mode_lock *fetch_share_mode_unlocked(TALLOC_CTX *mem_ctx,
}
struct forall_state {
- void (*fn)(struct file_id fid, const struct share_mode_data *data,
- void *private_data);
+ int (*fn)(struct file_id fid, const struct share_mode_data *data,
+ void *private_data);
void *private_data;
};
@@ -456,6 +456,7 @@ static int share_mode_traverse_fn(struct db_record *rec, void *_state)
enum ndr_err_code ndr_err;
struct share_mode_data *d;
struct file_id fid;
+ int ret;
key = dbwrap_record_get_key(rec);
value = dbwrap_record_get_value(rec);
@@ -488,15 +489,15 @@ static int share_mode_traverse_fn(struct db_record *rec, void *_state)
d->share_modes[i].stale = false; /* [skip] in idl */
}
- state->fn(fid, d, state->private_data);
+ ret = state->fn(fid, d, state->private_data);
TALLOC_FREE(d);
- return 0;
+ return ret;
}
-int share_mode_forall(void (*fn)(struct file_id fid,
- const struct share_mode_data *data,
- void *private_data),
+int share_mode_forall(int (*fn)(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data),
void *private_data)
{
struct forall_state state = { .fn = fn, .private_data = private_data };
@@ -517,9 +518,9 @@ int share_mode_forall(void (*fn)(struct file_id fid,
}
struct share_entry_forall_state {
- void (*fn)(const struct share_mode_entry *e,
- const char *service_path, const char *base_name,
- void *private_data);
+ int (*fn)(const struct share_mode_entry *e,
+ const char *service_path, const char *base_name,
+ void *private_data);
void *private_data;
};
@@ -531,9 +532,14 @@ static int share_entry_traverse_fn(struct file_id fid,
uint32_t i;
for (i=0; i<data->num_share_modes; i++) {
- state->fn(&data->share_modes[i],
- data->servicepath, data->base_name,
- state->private_data);
+ int ret;
+
+ ret = state->fn(&data->share_modes[i],
+ data->servicepath, data->base_name,
+ state->private_data);
+ if (ret != 0) {
+ return ret;
+ }
}
return 0;
@@ -544,9 +550,9 @@ static int share_entry_traverse_fn(struct file_id fid,
share mode system.
********************************************************************/
-int share_entry_forall(void (*fn)(const struct share_mode_entry *,
- const char *, const char *, void *),
- void *private_data)
+int share_entry_forall(int (*fn)(const struct share_mode_entry *,
+ const char *, const char *, void *),
+ void *private_data)
{
struct share_entry_forall_state state = {
.fn = fn, .private_data = private_data };
diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
index d2f05f3..0278182 100644
--- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
+++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
@@ -79,9 +79,9 @@ struct share_conn_stat {
/*******************************************************************
********************************************************************/
-static void enum_file_fn( const struct share_mode_entry *e,
- const char *sharepath, const char *fname,
- void *private_data )
+static int enum_file_fn( const struct share_mode_entry *e,
+ const char *sharepath, const char *fname,
+ void *private_data )
{
struct file_enum_count *fenum =
(struct file_enum_count *)private_data;
@@ -98,21 +98,21 @@ static void enum_file_fn( const struct share_mode_entry *e,
/* If the pid was not found delete the entry from connections.tdb */
if ( !process_exists(e->pid) ) {
- return;
+ return 0;
}
username = uidtoname(e->uid);
if ((fenum->username != NULL)
&& !strequal(username, fenum->username)) {
- return;
+ return 0;
}
f = talloc_realloc(fenum->ctx, fenum->ctr3->array,
struct srvsvc_NetFileInfo3, i+1);
if ( !f ) {
DEBUG(0,("conn_enum_fn: realloc failed for %d items\n", i+1));
- return;
+ return 0;
}
fenum->ctr3->array = f;
@@ -133,7 +133,7 @@ static void enum_file_fn( const struct share_mode_entry *e,
sharepath, fname );
}
if (!fullpath) {
- return;
+ return 0;
}
string_replace( fullpath, '/', '\\' );
@@ -150,6 +150,8 @@ static void enum_file_fn( const struct share_mode_entry *e,
fenum->ctr3->array[i].user = username;
fenum->ctr3->count++;
+
+ return 0;
}
/*******************************************************************
@@ -826,9 +828,9 @@ static WERROR init_srv_sess_info_0(struct pipes_struct *p,
* find out the session on which this file is open and bump up its count
**********************************************************************/
-static void count_sess_files_fn(const struct share_mode_entry *e,
- const char *sharepath, const char *fname,
- void *data)
+static int count_sess_files_fn(const struct share_mode_entry *e,
+ const char *sharepath, const char *fname,
+ void *data)
{
struct sess_file_info *info = data;
uint32_t rh = info->resume_handle;
@@ -846,9 +848,10 @@ static void count_sess_files_fn(const struct share_mode_entry *e,
serverid_equal(&e->pid, &sess->pid)) {
info->ctr->array[i].num_open++;
- return;
+ return 0;
}
}
+ return 0;
}
/*******************************************************************
@@ -950,9 +953,9 @@ static WERROR init_srv_sess_info_1(struct pipes_struct *p,
find the share connection on which this open exists.
********************************************************************/
-static void share_file_fn(const struct share_mode_entry *e,
- const char *sharepath, const char *fname,
- void *data)
+static int share_file_fn(const struct share_mode_entry *e,
+ const char *sharepath, const char *fname,
+ void *data)
{
struct share_file_stat *sfs = data;
uint32_t i;
@@ -962,10 +965,11 @@ static void share_file_fn(const struct share_mode_entry *e,
for (i=0; i < sfs->resp_entries; i++) {
if (serverid_equal(&e->pid, &sfs->svrid_arr[offset + i])) {
sfs->netconn_arr[i].num_open ++;
- return;
+ return 0;
}
}
}
+ return 0;
}
/*******************************************************************
@@ -2690,9 +2694,9 @@ struct enum_file_close_state {
struct messaging_context *msg_ctx;
};
-static void enum_file_close_fn( const struct share_mode_entry *e,
- const char *sharepath, const char *fname,
- void *private_data )
+static int enum_file_close_fn( const struct share_mode_entry *e,
+ const char *sharepath, const char *fname,
+ void *private_data )
{
char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
struct enum_file_close_state *state =
@@ -2700,11 +2704,11 @@ static void enum_file_close_fn( const struct share_mode_entry *e,
uint32_t fid = (((uint32_t)(procid_to_pid(&e->pid))<<16) | e->share_file_id);
if (fid != state->r->in.fid) {
- return; /* Not this file. */
+ return 0; /* Not this file. */
}
if (!process_exists(e->pid) ) {
- return;
+ return 0;
}
/* Ok - send the close message. */
@@ -2718,6 +2722,8 @@ static void enum_file_close_fn( const struct share_mode_entry *e,
messaging_send_buf(state->msg_ctx,
e->pid, MSG_SMB_CLOSE_FILE,
(uint8 *)msg, sizeof(msg)));
+
+ return 0;
}
/********************************************************************
diff --git a/source3/utils/status.c b/source3/utils/status.c
index cfc2bb6..0bcc2af 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -115,10 +115,10 @@ static bool Ucrit_addPid( struct server_id pid )
return True;
}
-static void print_share_mode(const struct share_mode_entry *e,
- const char *sharepath,
- const char *fname,
- void *dummy)
+static int print_share_mode(const struct share_mode_entry *e,
+ const char *sharepath,
+ const char *fname,
+ void *dummy)
{
static int count;
--
2.1.0.rc2.206.gedb03e5
From f4d3e4a202ce9854ca04a764a9d85cff14552455 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 25 Sep 2014 01:30:33 +0200
Subject: [PATCH 13/21] smbd: Don't rename a dir with files open underneath
This is an EXPENSIVE check. We'll have to guard this with an option
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/dir.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index e60bc2c..29184a3 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -25,6 +25,7 @@
#include "libcli/security/security.h"
#include "lib/util/bitmap.h"
#include "../lib/util/memcache.h"
+#include "../librpc/gen_ndr/open_files.h"
/*
This module implements directory related functions for Samba.
@@ -1809,6 +1810,113 @@ bool SearchDir(struct smb_Dir *dirp, const char *name, long *poffset)
return False;
}
+struct files_below_forall_state {
+ char *dirpath;
+ size_t dirpath_len;
+ int (*fn)(struct file_id fid, const struct share_mode_data *data,
+ void *private_data);
+ void *private_data;
+};
+
+static int files_below_forall_fn(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data)
+{
+ struct files_below_forall_state *state = private_data;
+ char tmpbuf[PATH_MAX];
+ char *fullpath, *to_free;
+ size_t len;
+
+ len = full_path_tos(data->servicepath, data->base_name,
+ tmpbuf, sizeof(tmpbuf),
+ &fullpath, &to_free);
+ if (len == -1) {
+ return 0;
+ }
+ if (state->dirpath_len >= len) {
+ /*
+ * Filter files above dirpath
+ */
+ return 0;
+ }
+ if (fullpath[state->dirpath_len] != '/') {
+ /*
+ * Filter file that don't have a path separator at the end of
+ * dirpath's length
+ */
+ return 0;
+ }
+
+ if (memcmp(state->dirpath, fullpath, len) != 0) {
+ /*
+ * Not a parent
+ */
+ return 0;
+ }
+
+ return state->fn(fid, data, private_data);
+}
+
+static int files_below_forall(connection_struct *conn,
+ const struct smb_filename *dir_name,
+ int (*fn)(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data),
+ void *private_data)
+{
+ struct files_below_forall_state state = {};
+ int ret;
+ char tmpbuf[PATH_MAX];
+ char *to_free;
+
+ state.dirpath_len = full_path_tos(conn->connectpath,
+ dir_name->base_name,
+ tmpbuf, sizeof(tmpbuf),
+ &state.dirpath, &to_free);
+ if (state.dirpath_len == -1) {
+ return -1;
+
+ }
+
+ ret = share_mode_forall(files_below_forall_fn, &state);
+ TALLOC_FREE(to_free);
+ return ret;
+}
+
+struct have_file_open_below_state {
+ bool found_one;
+};
+
+static int have_file_open_below_fn(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data)
+{
+ struct have_file_open_below_state *state = private_data;
+ state->found_one = true;
+ return 1;
+}
+
+static bool have_file_open_below(connection_struct *conn,
+ const struct smb_filename *name)
+{
+ struct have_file_open_below_state state = {};
+ int ret;
+
+ if (!VALID_STAT(name->st)) {
+ return false;
+ }
+ if (!S_ISDIR(name->st.st_ex_mode)) {
+ return false;
+ }
+
+ ret = files_below_forall(conn, name, have_file_open_below_fn, &state);
+ if (ret == -1) {
+ return false;
+ }
+
+ return state.found_one;
+}
+
/*****************************************************************
Is this directory empty ?
*****************************************************************/
@@ -1854,5 +1962,13 @@ NTSTATUS can_delete_directory_fsp(files_struct *fsp)
TALLOC_FREE(talloced);
TALLOC_FREE(dir_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (have_file_open_below(fsp->conn, fsp->fsp_name)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
return status;
}
--
2.1.0.rc2.206.gedb03e5
From 7204a450a005c36a9b382d5c57a14df47cfd4b7d Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 25 Sep 2014 01:32:00 +0200
Subject: [PATCH 14/21] s3: smbd - test rename dir deny with open files
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source4/torture/smb2/rename.c | 97 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/source4/torture/smb2/rename.c b/source4/torture/smb2/rename.c
index 9d0f4e1..07bdabd 100644
--- a/source4/torture/smb2/rename.c
+++ b/source4/torture/smb2/rename.c
@@ -928,6 +928,99 @@ done:
return ret;
}
+static bool torture_smb2_rename_dir_openfile(struct torture_context *torture,
+ struct smb2_tree *tree1)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_open io;
+ union smb_close cl;
+ union smb_setfileinfo sinfo;
+ struct smb2_handle d1, h1;
+
+ smb2_deltree(tree1, BASEDIR);
+ smb2_util_rmdir(tree1, BASEDIR);
+
+ torture_comment(torture, "Creating base directory\n");
+
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.create_flags = 0;
+ io.smb2.in.desired_access = 0x0017019f;
+ io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ io.smb2.in.share_access = 0;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+ io.smb2.in.security_flags = 0;
+ io.smb2.in.fname = BASEDIR;
+
+ status = smb2_create(tree1, torture, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ d1 = io.smb2.out.file.handle;
+
+ torture_comment(torture, "Creating test file\n");
+
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.create_flags = 0;
+ io.smb2.in.desired_access = 0x0017019f;
+ io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.smb2.in.share_access = 0;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+ io.smb2.in.security_flags = 0;
+ io.smb2.in.fname = BASEDIR "\\file.txt";
+
+ status = smb2_create(tree1, torture, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ h1 = io.smb2.out.file.handle;
+
+ torture_comment(torture, "Renaming directory\n");
+
+ ZERO_STRUCT(sinfo);
+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sinfo.rename_information.in.file.handle = d1;
+ sinfo.rename_information.in.overwrite = 0;
+ sinfo.rename_information.in.root_fid = 0;
+ sinfo.rename_information.in.new_name =
+ BASEDIR "-new";
+ status = smb2_setinfo_file(tree1, &sinfo);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ torture_comment(torture, "Closing directory\n");
+
+ ZERO_STRUCT(cl.smb2);
+ cl.smb2.level = RAW_CLOSE_SMB2;
+ cl.smb2.in.file.handle = d1;
+ status = smb2_close(tree1, &(cl.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ ZERO_STRUCT(d1);
+
+ torture_comment(torture, "Closing test file\n");
+
+ cl.smb2.in.file.handle = h1;
+ status = smb2_close(tree1, &(cl.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ ZERO_STRUCT(h1);
+
+done:
+
+ torture_comment(torture, "Cleaning up\n");
+
+ if (h1.data) {
+ ZERO_STRUCT(cl.smb2);
+ cl.smb2.level = RAW_CLOSE_SMB2;
+ cl.smb2.in.file.handle = h1;
+ status = smb2_close(tree1, &(cl.smb2));
+ }
+ smb2_deltree(tree1, BASEDIR);
+ return ret;
+}
+
struct rename_one_dir_cycle_state {
struct tevent_context *ev;
struct smb2_tree *tree;
@@ -1336,6 +1429,10 @@ struct torture_suite *torture_smb2_rename_init(void)
"msword",
torture_smb2_rename_msword);
+ torture_suite_add_1smb2_test(
+ suite, "rename_dir_openfile",
+ torture_smb2_rename_dir_openfile);
+
torture_suite_add_1smb2_test(suite,
"rename_dir_bench",
torture_smb2_rename_dir_bench);
--
2.1.0.rc2.206.gedb03e5
From fde539b531ed7536d81f9bfeb538fb9417580549 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 15/21] 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 | 133 ++++++++++++++++++++++++++++------------------
1 file changed, 81 insertions(+), 52 deletions(-)
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index cf1b497..804c68f 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -618,6 +618,83 @@ 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, 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 = UINT16_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);
+
+ 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 +752,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 +852,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,66 +902,15 @@ 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, 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 = UINT16_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;
}
--
2.1.0.rc2.206.gedb03e5
From fa582e9b57471783f31d61a1525b5b074dc8da77 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 16/21] 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 | 3 +-
source3/locking/leases_db.c | 159 +++++++++++++++++++++++++++++++++------
source3/locking/leases_db.h | 6 +-
source3/locking/locking.c | 3 +-
source3/smbd/open.c | 3 +-
5 files changed, 148 insertions(+), 26 deletions(-)
diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl
index 9ec8912..28ec479 100644
--- a/source3/librpc/idl/leases_db.idl
+++ b/source3/librpc/idl/leases_db.idl
@@ -16,7 +16,8 @@ interface leases_db
} leases_db_key;
typedef [public] struct {
- file_id id;
+ 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
index 69b1362..29b8e35 100644
--- a/source3/locking/leases_db.c
+++ b/source3/locking/leases_db.c
@@ -89,11 +89,12 @@ NTSTATUS leases_db_add(const struct GUID *client_guid,
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 value;
- DATA_BLOB blob;
+ struct leases_db_value new_value;
+ struct leases_db_value *value = NULL;
enum ndr_err_code ndr_err;
if (!leases_db_init(false)) {
@@ -114,25 +115,64 @@ NTSTATUS leases_db_add(const struct GUID *client_guid,
db_value = dbwrap_record_get_value(rec);
if (db_value.dsize != 0) {
+ uint32_t i;
+
DEBUG(10, ("%s: record exists\n", __func__));
- TALLOC_FREE(rec);
- return NT_STATUS_OBJECT_NAME_COLLISION;
- }
- value = (struct leases_db_value) {
- .id = *id,
- .filename = filename,
- .stream_name = stream_name,
- };
+ 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,
+ &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)));
- TALLOC_FREE(rec);
- return ndr_map_error2ntstatus(ndr_err);
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
}
db_value = make_tdb_data(blob.data, blob.length);
@@ -143,16 +183,26 @@ NTSTATUS leases_db_add(const struct GUID *client_guid,
__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 smb2_lease_key *lease_key,
+ const struct file_id *id)
{
- TDB_DATA db_key;
+ 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)) {
@@ -169,14 +219,79 @@ NTSTATUS leases_db_del(const struct GUID *client_guid,
if (rec == NULL) {
return NT_STATUS_NOT_FOUND;
}
- status = dbwrap_record_delete(rec);
+ 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)(struct file_id id, const char *filename,
- const char *stream_name, void *private_data);
+ 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;
};
@@ -206,8 +321,9 @@ static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
return;
}
- state->parser(value->id, value->filename, value->stream_name,
- state->private_data);
+ state->parser(value->num_file_ids,
+ value->ids, value->filename, value->stream_name,
+ state->private_data);
TALLOC_FREE(value);
state->status = NT_STATUS_OK;
@@ -215,7 +331,8 @@ static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
NTSTATUS leases_db_parse(const struct GUID *client_guid,
const struct smb2_lease_key *lease_key,
- void (*parser)(struct file_id id,
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids,
const char *filename,
const char *stream_name,
void *private_data),
diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h
index ff9c362..f570356 100644
--- a/source3/locking/leases_db.h
+++ b/source3/locking/leases_db.h
@@ -32,10 +32,12 @@ NTSTATUS leases_db_add(const struct GUID *client_guid,
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 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)(struct file_id id,
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids,
const char *filename,
const char *stream_name,
void *private_data),
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 804c68f..1df9133 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -687,7 +687,8 @@ static void remove_share_mode_lease(struct share_mode_data *d,
NTSTATUS status;
status = leases_db_del(&client_guid,
- &lease_key);
+ &lease_key,
+ &e->id);
DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
nt_errstr(status)));
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index a5a8dd0..358c6bf 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -4089,7 +4089,8 @@ struct lease_fname_match_state {
};
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 =
--
2.1.0.rc2.206.gedb03e5
From 2bc1bdce34b774cb653204af9355234f1d57cbc3 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 17/21] s3: smbd: leases - Fix the dynamic share file case
[homes].
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/open.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 130 insertions(+), 15 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 358c6bf..606da58 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -4084,8 +4084,14 @@ 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(
@@ -4096,28 +4102,130 @@ static void lease_fname_match_parser(
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;
+ }
+ 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);
}
- return state.match;
+ /*
+ * Ensure we don't grant anything more so we
+ * never upgrade.
+ */
+ return NT_STATUS_OPLOCK_NOT_GRANTED;
}
/*
@@ -4176,10 +4284,17 @@ 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... */
+ lease->lease_state = SMB2_LEASE_NONE;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
}
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
--
2.1.0.rc2.206.gedb03e5
From 711fb799ac0ade0734db317335698ed0ab1cb2a5 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 18/21] 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 606da58..ff77f3c 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);
--
2.1.0.rc2.206.gedb03e5
From 4e1cfcb2e41f42835c572762742d2334cc837737 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 19/21] s3: leases: Don't set fsp->oplock_type before we've
granted any oplocks.
It's not needed, and when we have leases it causes a crash in the call
stack when truncate is requested as follows:
open_file_ntcreate()->vfs_set_filelen()->smbd_contend_level2_oplocks_begin()->
contend_level2_oplocks_begin_default().
Inside which we check if fsp->oplock_type == LEASE_TYPE.
In this case we have not yet added the entry in the share
mode database so fsp->lease == NULL.
Signed-off-by: Jeremy Allison <jra 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 ff77f3c..b4525ab 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -2688,9 +2688,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;
}
--
2.1.0.rc2.206.gedb03e5
From a6e81b0a8d14a832db5de7e60c3bfb4a7f30067a 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 20/21] s3: smbd: Add "smb2 leases" parameter. Default "false".
Signed-off-by: Jeremy Allison <jra at samba.org>
---
docs-xml/smbdotconf/locking/smb2leases.xml | 19 +++++++++++++++++++
lib/param/param_table.c | 9 +++++++++
source3/param/loadparm.c | 1 +
source3/smbd/smb2_negprot.c | 2 +-
4 files changed, 30 insertions(+), 1 deletion(-)
create mode 100644 docs-xml/smbdotconf/locking/smb2leases.xml
diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
new file mode 100644
index 0000000..2927e56
--- /dev/null
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -0,0 +1,19 @@
+<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>
+</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 15ffa8c..f68d2cc 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -3001,6 +3001,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 | FLAG_SHARE,
+ },
+ {
.label = "locking",
.type = P_BOOL,
.p_class = P_LOCAL,
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 52ffbcc..8c388f3 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -853,6 +853,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());
diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c
index a27ab0c..e88165b 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -230,7 +230,7 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
capabilities |= SMB2_CAP_DFS;
}
- if (protocol >= PROTOCOL_SMB2_10) {
+ if (protocol >= PROTOCOL_SMB2_10 && lp_smb2_leases()) {
capabilities |= SMB2_CAP_LEASING;
}
--
2.1.0.rc2.206.gedb03e5
From 887b3954e91c0765c913ae93d4dcc0d2cb23b304 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 24 Oct 2014 13:57:04 -0700
Subject: [PATCH 21/21] s3: smbd: Add new option "strict rename".
Control whether smbd can rename directories containing
open files. Defaults to "no" (meaning we *can* do
such renames). autobuild test set with it to "yes".
Signed-off-by: Jeremy Allison <jra at samba.org>
---
docs-xml/smbdotconf/tuning/strictrename.xml | 25 +++++++++++++++++++++++++
lib/param/param_table.c | 9 +++++++++
selftest/target/Samba3.pm | 1 +
source3/param/loadparm.c | 1 +
source3/smbd/dir.c | 3 ++-
5 files changed, 38 insertions(+), 1 deletion(-)
create mode 100644 docs-xml/smbdotconf/tuning/strictrename.xml
diff --git a/docs-xml/smbdotconf/tuning/strictrename.xml b/docs-xml/smbdotconf/tuning/strictrename.xml
new file mode 100644
index 0000000..5478863
--- /dev/null
+++ b/docs-xml/smbdotconf/tuning/strictrename.xml
@@ -0,0 +1,25 @@
+<samba:parameter name="strict rename"
+ context="S"
+ type="boolean"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>By default a Windows SMB server prevents directory
+ renames when there are open file or directory handles below
+ it in the filesystem hierarchy. Historically Samba has always
+ allowed this as POSIX filesystem semantics require it.</para>
+
+ <para>This boolean parameter allows Samba to match the Windows
+ behavior. Setting this to "yes" is a very expensive change,
+ as it forces Samba to travers the entire open file handle
+ database on every directory rename request. In a clustered
+ Samba system the cost is even greater than the non-clustered
+ case.</para>
+
+ <para>For this reason the default is "no", and it is recommended
+ to be left that way unless a specific Windows application requires
+ it to be changed.</para>
+
+</description>
+
+<value type="default">no</value>
+</samba:parameter>
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index f68d2cc..b6ff571 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -1884,6 +1884,15 @@ struct parm_struct parm_table[] = {
.flags = FLAG_ADVANCED | FLAG_SHARE,
},
{
+ .label = "strict rename",
+ .type = P_BOOL,
+ .p_class = P_LOCAL,
+ .offset = LOCAL_VAR(strict_rename),
+ .special = NULL,
+ .enum_list = NULL,
+ .flags = FLAG_ADVANCED | FLAG_SHARE,
+ },
+ {
.label = "strict sync",
.type = P_BOOL,
.p_class = P_LOCAL,
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index de40ced..ebe2c09 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1073,6 +1073,7 @@ sub provision($$$$$$)
store dos attributes = yes
create mask = 755
dos filemode = yes
+ strict rename = yes
vfs objects = acl_xattr fake_acls xattr_tdb streams_depot
printing = vlp
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 8c388f3..a0f3eef 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -206,6 +206,7 @@ static struct loadparm_service sDefault =
.follow_symlinks = true,
.sync_always = false,
.strict_allocate = false,
+ .strict_rename = false,
.strict_sync = false,
.mangling_char = '~',
.copymap = NULL,
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 29184a3..10224d8 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1966,7 +1966,8 @@ NTSTATUS can_delete_directory_fsp(files_struct *fsp)
return status;
}
- if (have_file_open_below(fsp->conn, fsp->fsp_name)) {
+ if (lp_strict_rename(SNUM(conn)) &&
+ have_file_open_below(fsp->conn, fsp->fsp_name)) {
return NT_STATUS_ACCESS_DENIED;
}
--
2.1.0.rc2.206.gedb03e5
More information about the samba-technical
mailing list