The Samba-Bugzilla – Attachment 10411 Details for
Bug 10911
SMB2 leases are not yet supported.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Leases patch on top of master.
leases-wip-metze9.patch (text/plain), 149.92 KB, created by
Jeremy Allison
on 2014-11-05 18:32:44 UTC
(
hide
)
Description:
Leases patch on top of master.
Filename:
MIME Type:
Creator:
Jeremy Allison
Created:
2014-11-05 18:32:44 UTC
Size:
149.92 KB
patch
obsolete
>From 0c869f6c284e3edd49ae374d23dde96c41f58228 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Mon, 22 Sep 2014 21:21:36 +0200 >Subject: [PATCH 01/20] s3: leases: mask off > SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET. > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@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 ffeb8ff76738e90a4884c367371f21564bf280b7 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 28 Oct 2014 15:27:09 -0700 >Subject: [PATCH 02/20] s3: leases - convert have_read field to num_read. > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >--- > source3/locking/brlock.c | 123 +++++++++++++++++++++-------------------------- > 1 file changed, 56 insertions(+), 67 deletions(-) > >diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c >index 0bed9f9..6c73c72 100644 >--- a/source3/locking/brlock.c >+++ b/source3/locking/brlock.c >@@ -47,7 +47,7 @@ struct byte_range_lock { > struct files_struct *fsp; > unsigned int num_locks; > bool modified; >- bool have_read_oplocks; >+ uint32_t num_read_oplocks; > struct lock_struct *lock_data; > struct db_record *record; > }; >@@ -82,18 +82,18 @@ struct files_struct *brl_fsp(struct byte_range_lock *brl) > return brl->fsp; > } > >-bool brl_have_read_oplocks(const struct byte_range_lock *brl) >+uint32_t brl_num_read_oplocks(const struct byte_range_lock *brl) > { >- return brl->have_read_oplocks; >+ return brl->num_read_oplocks; > } > >-void brl_set_have_read_oplocks(struct byte_range_lock *brl, >- bool have_read_oplocks) >+void brl_set_num_read_oplocks(struct byte_range_lock *brl, >+ uint32_t num_read_oplocks) > { >- DEBUG(10, ("Setting have_read_oplocks to %s\n", >- have_read_oplocks ? "true" : "false")); >+ DEBUG(10, ("Setting num_read_oplocks to %"PRIu32"\n", >+ num_read_oplocks)); > SMB_ASSERT(brl->record != NULL); /* otherwise we're readonly */ >- brl->have_read_oplocks = have_read_oplocks; >+ brl->num_read_oplocks = num_read_oplocks; > brl->modified = true; > } > >@@ -1850,7 +1850,6 @@ int brl_forall(void (*fn)(struct file_id id, struct server_id pid, > > static void byte_range_lock_flush(struct byte_range_lock *br_lck) > { >- size_t data_len; > unsigned i; > struct lock_struct *locks = br_lck->lock_data; > >@@ -1874,15 +1873,7 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck) > } > } > >- data_len = br_lck->num_locks * sizeof(struct lock_struct); >- >- if (br_lck->have_read_oplocks) { >- data_len += 1; >- } >- >- DEBUG(10, ("data_len=%d\n", (int)data_len)); >- >- if (data_len == 0) { >+ if ((br_lck->num_locks == 0) && (br_lck->num_read_oplocks == 0)) { > /* No locks - delete this entry. */ > NTSTATUS status = dbwrap_record_delete(br_lck->record); > if (!NT_STATUS_IS_OK(status)) { >@@ -1891,19 +1882,20 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck) > smb_panic("Could not delete byte range lock entry"); > } > } else { >+ size_t lock_len, data_len; > TDB_DATA data; > NTSTATUS status; > >+ lock_len = br_lck->num_locks * sizeof(struct lock_struct); >+ data_len = lock_len + sizeof(br_lck->num_read_oplocks); >+ > data.dsize = data_len; > data.dptr = talloc_array(talloc_tos(), uint8_t, data_len); > SMB_ASSERT(data.dptr != NULL); > >- memcpy(data.dptr, br_lck->lock_data, >- br_lck->num_locks * sizeof(struct lock_struct)); >- >- if (br_lck->have_read_oplocks) { >- data.dptr[data_len-1] = 1; >- } >+ memcpy(data.dptr, br_lck->lock_data, lock_len); >+ memcpy(data.dptr + lock_len, &br_lck->num_read_oplocks, >+ sizeof(br_lck->num_read_oplocks)); > > status = dbwrap_record_store(br_lck->record, data, TDB_REPLACE); > TALLOC_FREE(data.dptr); >@@ -1926,6 +1918,32 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck) > return 0; > } > >+static bool brl_parse_data(struct byte_range_lock *br_lck, TDB_DATA data) >+{ >+ size_t data_len; >+ >+ if (data.dsize == 0) { >+ return true; >+ } >+ if (data.dsize % sizeof(struct lock_struct) != >+ sizeof(br_lck->num_read_oplocks)) { >+ DEBUG(1, ("Invalid data size: %u\n", (unsigned)data.dsize)); >+ return false; >+ } >+ >+ br_lck->num_locks = data.dsize / sizeof(struct lock_struct); >+ data_len = br_lck->num_locks * sizeof(struct lock_struct); >+ >+ br_lck->lock_data = talloc_memdup(br_lck, data.dptr, data_len); >+ if (br_lck->lock_data == NULL) { >+ DEBUG(1, ("talloc_memdup failed\n")); >+ return false; >+ } >+ memcpy(&br_lck->num_read_oplocks, data.dptr + data_len, >+ sizeof(br_lck->num_read_oplocks)); >+ return true; >+} >+ > /******************************************************************* > Fetch a set of byte range lock data from the database. > Leave the record locked. >@@ -1935,16 +1953,14 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck) > struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp) > { > TDB_DATA key, data; >- struct byte_range_lock *br_lck = talloc(mem_ctx, struct byte_range_lock); >+ struct byte_range_lock *br_lck; > >+ br_lck = talloc_zero(mem_ctx, struct byte_range_lock); > if (br_lck == NULL) { > return NULL; > } > > br_lck->fsp = fsp; >- br_lck->num_locks = 0; >- br_lck->have_read_oplocks = false; >- br_lck->modified = False; > > key.dptr = (uint8 *)&fsp->file_id; > key.dsize = sizeof(struct file_id); >@@ -1959,30 +1975,12 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp) > > data = dbwrap_record_get_value(br_lck->record); > >- br_lck->lock_data = NULL; >- >- talloc_set_destructor(br_lck, byte_range_lock_destructor); >- >- br_lck->num_locks = data.dsize / sizeof(struct lock_struct); >- >- if (br_lck->num_locks != 0) { >- br_lck->lock_data = talloc_array( >- br_lck, struct lock_struct, br_lck->num_locks); >- if (br_lck->lock_data == NULL) { >- DEBUG(0, ("malloc failed\n")); >- TALLOC_FREE(br_lck); >- return NULL; >- } >- >- memcpy(br_lck->lock_data, data.dptr, >- talloc_get_size(br_lck->lock_data)); >+ if (!brl_parse_data(br_lck, data)) { >+ TALLOC_FREE(br_lck); >+ return NULL; > } > >- DEBUG(10, ("data.dsize=%d\n", (int)data.dsize)); >- >- if ((data.dsize % sizeof(struct lock_struct)) == 1) { >- br_lck->have_read_oplocks = (data.dptr[data.dsize-1] == 1); >- } >+ talloc_set_destructor(br_lck, byte_range_lock_destructor); > > if (DEBUGLEVEL >= 10) { > unsigned int i; >@@ -2008,28 +2006,19 @@ static void brl_get_locks_readonly_parser(TDB_DATA key, TDB_DATA data, > { > struct brl_get_locks_readonly_state *state = > (struct brl_get_locks_readonly_state *)private_data; >- struct byte_range_lock *br_lock; >+ struct byte_range_lock *br_lck; > >- br_lock = talloc_pooled_object( >+ br_lck = talloc_pooled_object( > state->mem_ctx, struct byte_range_lock, 1, data.dsize); >- if (br_lock == NULL) { >+ if (br_lck == NULL) { > *state->br_lock = NULL; > return; > } >- br_lock->lock_data = (struct lock_struct *)talloc_memdup( >- br_lock, data.dptr, data.dsize); >- br_lock->num_locks = data.dsize / sizeof(struct lock_struct); >- >- if ((data.dsize % sizeof(struct lock_struct)) == 1) { >- br_lock->have_read_oplocks = (data.dptr[data.dsize-1] == 1); >- } else { >- br_lock->have_read_oplocks = false; >+ if (!brl_parse_data(br_lck, data)) { >+ *state->br_lock = NULL; >+ return; > } >- >- DEBUG(10, ("Got %d bytes, have_read_oplocks: %s\n", (int)data.dsize, >- br_lock->have_read_oplocks ? "true" : "false")); >- >- *state->br_lock = br_lock; >+ *state->br_lock = br_lck; > } > > struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) >@@ -2072,7 +2061,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) > return NULL; > } > >- br_lock->have_read_oplocks = false; >+ br_lock->num_read_oplocks = 0; > br_lock->num_locks = 0; > br_lock->lock_data = NULL; > >-- >2.1.0.rc2.206.gedb03e5 > > >From 3ff9f097b880f80a997940bed4b84f90fa29a864 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 03/20] s3: smbd: Implementation of SMB2.1 leases. > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@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/open_files.idl | 30 ++ > source3/librpc/idl/wscript_build | 1 + > source3/librpc/wscript_build | 7 +- > source3/locking/locking.c | 141 ++++++++- > source3/locking/proto.h | 17 +- > source3/smbd/durable.c | 28 +- > source3/smbd/files.c | 34 +++ > source3/smbd/globals.h | 10 +- > source3/smbd/open.c | 575 ++++++++++++++++++++++++++++-------- > source3/smbd/oplock.c | 364 +++++++++++++++++------ > 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 +- > 24 files changed, 1386 insertions(+), 253 deletions(-) > >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 6cca3dd..6b720f0 100644 >--- a/selftest/knownfail >+++ b/selftest/knownfail >@@ -188,25 +188,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/open_files.idl b/source3/librpc/idl/open_files.idl >index 4278301..48e6bbe 100644 >--- a/source3/librpc/idl/open_files.idl >+++ b/source3/librpc/idl/open_files.idl >@@ -3,6 +3,8 @@ > import "server_id.idl"; > import "security.idl"; > import "file_id.idl"; >+import "smb2_lease_struct.idl"; >+import "misc.idl"; > > [ > pointer_default(unique) >@@ -14,6 +16,7 @@ interface open_files > server_id pid; > hyper op_mid; > uint16 op_type; >+ uint32 lease_idx; > uint32 access_mask; > uint32 share_access; > uint32 private_options; >@@ -31,6 +34,31 @@ interface open_files > [skip] boolean8 stale; > } share_mode_entry; > >+ typedef [public,bitmap8bit] bitmap { >+ SHARE_MODE_NO_CACHING = 0x00, >+ SHARE_MODE_READ_CACHING = 0x01, >+ SHARE_MODE_HANDLE_CACHING = 0x02, >+ SHARE_MODE_WRITE_CACHING = 0x04 >+ } share_mode_caching; >+ >+ typedef [public,flag(NDR_PAHEX)] struct { >+ GUID client_guid; >+ smb2_lease_key lease_key; >+ share_mode_caching current_state; >+ /* >+ * breaking_to_state indicates to which level >+ * the current state is broken when a conflicting >+ * request is processed. The calculation is as follows: >+ * >+ * breaking_to_state = current_state; >+ * breaking_to_state &= ~(remove_state) >+ * breaking_to_state &= allowed_shared_state >+ */ >+ share_mode_caching breaking_to_state; >+ boolean8 breaking; >+ uint16 epoch; >+ } share_mode_oplock; >+ > typedef [public] struct { > uint32 name_hash; > security_token *delete_nt_token; >@@ -43,6 +71,8 @@ interface open_files > [string,charset(UTF8)] char *stream_name; > uint32 num_share_modes; > [size_is(num_share_modes)] share_mode_entry share_modes[]; >+ uint32 num_leases; >+ [size_is(num_leases)] share_mode_oplock leases[]; > uint32 num_delete_tokens; > [size_is(num_delete_tokens)] delete_token delete_tokens[]; > timespec old_write_time; >diff --git a/source3/librpc/idl/wscript_build b/source3/librpc/idl/wscript_build >index c38fe7b..f9b1bd7 100644 >--- a/source3/librpc/idl/wscript_build >+++ b/source3/librpc/idl/wscript_build >@@ -8,6 +8,7 @@ bld.SAMBA_PIDL_LIST('PIDL', > '''messaging.idl libnetapi.idl open_files.idl > perfcount.idl secrets.idl libnet_join.idl > smbXsrv.idl >+ leases_db.idl > ''', > options='--includedir=%s --header --ndr-parser' % topinclude, > output_dir='../gen_ndr') >diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build >index 77ae048..5c83cf2 100644 >--- a/source3/librpc/wscript_build >+++ b/source3/librpc/wscript_build >@@ -17,7 +17,7 @@ bld.SAMBA3_SUBSYSTEM('NDR_MESSAGING', > > bld.SAMBA3_SUBSYSTEM('NDR_OPEN_FILES', > source='gen_ndr/ndr_open_files.c', >- public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY' >+ public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY NDR_SMB2_LEASE_STRUCT' > ) > > bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV', >@@ -25,6 +25,11 @@ bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV', > public_deps='ndr NDR_SERVER_ID NDR_SECURITY NDR_AUTH' > ) > >+bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB', >+ source='gen_ndr/ndr_leases_db.c', >+ public_deps='ndr' >+ ) >+ > bld.SAMBA3_SUBSYSTEM('NDR_SECRETS', > source='gen_ndr/ndr_secrets.c', > public_deps='ndr' >diff --git a/source3/locking/locking.c b/source3/locking/locking.c >index a320068..170fa1d 100644 >--- a/source3/locking/locking.c >+++ b/source3/locking/locking.c >@@ -46,6 +46,7 @@ > #include "messages.h" > #include "util_tdb.h" > #include "../librpc/gen_ndr/ndr_open_files.h" >+#include "locking/leases_db.h" > > #undef DBGC_CLASS > #define DBGC_CLASS DBGC_LOCKING >@@ -608,6 +609,7 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e) > num_props += ((e->op_type == NO_OPLOCK) ? 1 : 0); > num_props += (EXCLUSIVE_OPLOCK_TYPE(e->op_type) ? 1 : 0); > num_props += (LEVEL_II_OPLOCK_TYPE(e->op_type) ? 1 : 0); >+ num_props += (e->op_type == LEASE_OPLOCK); > > if ((num_props > 1) && serverid_exists(&e->pid)) { > smb_panic("Invalid share mode entry"); >@@ -694,7 +696,8 @@ void remove_stale_share_mode_entries(struct share_mode_data *d) > } > > bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp, >- uid_t uid, uint64_t mid, uint16 op_type) >+ uid_t uid, uint64_t mid, uint16 op_type, >+ uint32_t lease_idx) > { > struct share_mode_data *d = lck->data; > struct share_mode_entry *tmp, *e; >@@ -716,6 +719,7 @@ bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp, > e->access_mask = fsp->access_mask; > e->op_mid = mid; > e->op_type = op_type; >+ e->lease_idx = lease_idx; > e->time.tv_sec = fsp->open_time.tv_sec; > e->time.tv_usec = fsp->open_time.tv_usec; > e->id = fsp->file_id; >@@ -816,16 +820,70 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck, > > bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) > { >+ struct share_mode_data *d = lck->data; > struct share_mode_entry *e; >+ uint16_t op_type; >+ uint32_t lease_idx; >+ uint32_t i; > > e = find_share_mode_entry(lck, fsp); > if (e == NULL) { > return False; > } > >+ op_type = e->op_type; > e->op_type = NO_OPLOCK; >- lck->data->modified = True; >- return True; >+ >+ d->modified = True; >+ >+ if (op_type != LEASE_OPLOCK) { >+ return true; >+ } >+ >+ /* >+ * This used to reference a lease. If there's no other one referencing >+ * it, remove it. >+ */ >+ >+ lease_idx = e->lease_idx; >+ e->lease_idx = UINT32_MAX; >+ >+ for (i=0; i<d->num_share_modes; i++) { >+ if (d->share_modes[i].lease_idx == lease_idx) { >+ break; >+ } >+ } >+ if (i < d->num_share_modes) { >+ /* >+ * Found another one >+ */ >+ return true; >+ } >+ >+ d->num_leases -= 1; >+ d->leases[lease_idx] = d->leases[d->num_leases]; >+ >+ /* >+ * We changed the lease array. Fix all references to it. >+ */ >+ for (i=0; i<d->num_share_modes; i++) { >+ if (d->share_modes[i].lease_idx == d->num_leases) { >+ d->share_modes[i].lease_idx = lease_idx; >+ } >+ } >+ >+ { >+ NTSTATUS status; >+ >+ status = leases_db_del( >+ &fsp->conn->sconn->client->connections->smb2.client.guid, >+ &fsp->lease->lease.lease_key); >+ >+ DEBUG(10, ("%s: leases_db_del returned %s\n", __func__, >+ nt_errstr(status))); >+ } >+ >+ return true; > } > > /******************************************************************* >@@ -846,6 +904,83 @@ bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp) > return True; > } > >+/* >+ * "key" has been broken, it is referenced somewhere in "lck". If "fsp" >+ * applies to it, reset fsp->sent_oplock_break. >+ */ >+ >+bool fsp_lease_broken(struct share_mode_lock *lck, >+ struct file_id lck_id, >+ const struct smb2_lease_key *key, >+ files_struct *fsp, uint32_t new_lease_state) >+{ >+ struct share_mode_data *d = lck->data; >+ struct share_mode_entry *e; >+ >+ if (!file_id_equal(&fsp->file_id, &lck_id)) { >+ return false; >+ } >+ if (fsp->oplock_type != LEASE_OPLOCK) { >+ return false; >+ } >+ >+ e = find_share_mode_entry(lck, fsp); >+ if (e == NULL) { >+ DEBUG(1, ("downgrade_lease_fsps: Could not find share mode " >+ "entry\n")); >+ return false; >+ } >+ >+ if (!smb2_lease_key_equal(key, &d->leases[e->lease_idx].lease_key)) { >+ return false; >+ } >+ fsp->sent_oplock_break = NO_BREAK_SENT; >+ TALLOC_FREE(fsp->oplock_timeout); >+ fsp->lease->lease.lease_state = new_lease_state; >+ return true; >+} >+ >+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn, >+ struct share_mode_lock *lck, >+ const struct smb2_lease_key *key, >+ uint32_t new_lease_state) >+{ >+ struct share_mode_data *d = lck->data; >+ struct share_mode_oplock *l; >+ uint32_t i; >+ >+ for (i=0; i<d->num_leases; i++) { >+ if (smb2_lease_key_equal(key, &d->leases[i].lease_key)) { >+ break; >+ } >+ } >+ if (i == d->num_leases) { >+ DEBUG(10, ("lease not found\n")); >+ return NT_STATUS_INVALID_PARAMETER; >+ } >+ >+ l = &d->leases[i]; >+ >+ /* >+ * Can't upgrade anything: l->current_state must be a strict bitwise >+ * superset of new_lease_state >+ */ >+ >+ if ((new_lease_state & l->current_state) != new_lease_state) { >+ DEBUG(10, ("Attempt to upgrade from %d to %d\n", >+ (int)l->current_state, (int)new_lease_state)); >+ return NT_STATUS_REQUEST_NOT_ACCEPTED; >+ } >+ >+ l->current_state = new_lease_state; >+ l->breaking_to_state = 0; >+ l->breaking = 0; >+ >+ d->modified = true; >+ >+ return NT_STATUS_OK; >+} >+ > /**************************************************************************** > Adds a delete on close token. > ****************************************************************************/ >diff --git a/source3/locking/proto.h b/source3/locking/proto.h >index 44f3ba1..94e9b8f 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, >+ uint32_t lease_idx); > void remove_stale_share_mode_entries(struct share_mode_data *d); > bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp); > bool mark_share_mode_disconnected(struct share_mode_lock *lck, > struct files_struct *fsp); > bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp); > bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp); >+bool fsp_lease_broken(struct share_mode_lock *lck, >+ struct file_id lck_id, >+ const struct smb2_lease_key *key, >+ files_struct *fsp, uint32_t new_lease_state); >+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn, >+ struct share_mode_lock *lck, >+ const struct smb2_lease_key *key, >+ uint32_t new_lease_state); > bool get_delete_on_close_token(struct share_mode_lock *lck, > uint32_t name_hash, > const struct security_token **pp_nt_tok, >diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c >index 9489cf1..660865d 100644 >--- a/source3/smbd/durable.c >+++ b/source3/smbd/durable.c >@@ -168,7 +168,7 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, > return NT_STATUS_INVALID_PARAMETER; > } > >- if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) { >+ if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) { > return NT_STATUS_NOT_SUPPORTED; > } > >@@ -724,6 +724,32 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, > fsp->aio_write_behind = false; > fsp->oplock_type = e->op_type; > >+ if (fsp->oplock_type == LEASE_OPLOCK) { >+ struct share_mode_oplock *o = &lck->data->leases[e->lease_idx]; >+ struct smb2_lease_key key; >+ >+ key.data[0] = o->lease_key.data[0]; >+ key.data[1] = o->lease_key.data[1]; >+ >+ fsp->lease = find_fsp_lease(fsp, &key); >+ >+ if (fsp->lease != NULL) { >+ fsp->lease->ref_count += 1; >+ } else { >+ fsp->lease = talloc_zero(fsp->conn->sconn, >+ struct fsp_lease); >+ if (fsp->lease == NULL) { >+ TALLOC_FREE(lck); >+ fsp_free(fsp); >+ return NT_STATUS_NO_MEMORY; >+ } >+ fsp->lease->ref_count = 1; >+ fsp->lease->lease.lease_key = key; >+ fsp->lease->lease.lease_state = o->current_state; >+ fsp->lease->lease.lease_epoch = o->epoch; >+ } >+ } >+ > fsp->initial_allocation_size = cookie.initial_allocation_size; > fsp->fh->position_information = cookie.position_information; > fsp->update_write_time_triggered = cookie.update_write_time_triggered; >diff --git a/source3/smbd/files.c b/source3/smbd/files.c >index a9e8357..13d1138 100644 >--- a/source3/smbd/files.c >+++ b/source3/smbd/files.c >@@ -387,6 +387,24 @@ files_struct *file_find_di_next(files_struct *start_fsp) > return NULL; > } > >+struct files_struct *file_find_one_fsp_from_lease_key( >+ struct smbd_server_connection *sconn, >+ const struct smb2_lease_key *lease_key) >+{ >+ struct files_struct *fsp; >+ >+ for (fsp = sconn->files; fsp; fsp=fsp->next) { >+ if ((fsp->lease != NULL) && >+ (fsp->lease->lease.lease_key.data[0] == >+ lease_key->data[0]) && >+ (fsp->lease->lease.lease_key.data[1] == >+ lease_key->data[1])) { >+ return fsp; >+ } >+ } >+ return NULL; >+} >+ > /**************************************************************************** > Find any fsp open with a pathname below that of an already open path. > ****************************************************************************/ >@@ -472,6 +490,14 @@ void fsp_free(files_struct *fsp) > fsp->fh->ref_count--; > } > >+ if (fsp->lease != NULL) { >+ if (fsp->lease->ref_count == 1) { >+ TALLOC_FREE(fsp->lease); >+ } else { >+ fsp->lease->ref_count--; >+ } >+ } >+ > fsp->conn->num_files_open--; > > /* this is paranoia, just in case someone tries to reuse the >@@ -740,3 +766,11 @@ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp, > smb_fname_str_dbg(fsp->fsp_name), > &fsp->name_hash); > } >+ >+uint32_t fsp_lease_type(struct files_struct *fsp) >+{ >+ if (fsp->oplock_type == LEASE_OPLOCK) { >+ return fsp->lease->lease.lease_state; >+ } >+ return map_oplock_to_lease_type(fsp->oplock_type); >+} >diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h >index 36e7f0f..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..9795625 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,321 @@ 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; > >- /* 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, >+ uint32_t *p_lease_idx, >+ uint32_t granted) >+{ >+ const struct GUID *client_guid; >+ struct share_mode_oplock *o; >+ struct share_mode_oplock *tmp; >+ NTSTATUS status; >+ int idx; >+ >+ >+ /* >+ * TODO: in future we can have multiple connections... >+ */ >+ client_guid = &fsp->conn->sconn->client->connections->smb2.client.guid; >+ >+ idx = find_share_mode_oplock(d, client_guid, &lease->lease_key); >+ >+ if (idx != -1) { >+ >+ bool do_upgrade; >+ uint32_t existing, requested; >+ >+ fsp->lease = find_fsp_lease(fsp, &lease->lease_key); >+ if (fsp->lease == NULL) { >+ DEBUG(1, ("Did not find existing lease for file %s\n", >+ fsp_str_dbg(fsp))); >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ fsp->lease->ref_count += 1; >+ >+ *p_lease_idx = idx; >+ o = &d->leases[idx]; >+ >+ /* >+ * Upgrade only if the requested lease is a strict upgrade. >+ */ >+ existing = o->current_state; >+ requested = lease->lease_state; >+ >+ /* >+ * Tricky: This test makes sure that "requested" is a >+ * strict bitwise superset of "existing". >+ */ >+ do_upgrade = ((existing & requested) == existing); >+ >+ /* >+ * Upgrade only if other leases don't prevent what was asked >+ * for. >+ */ >+ do_upgrade &= (granted == requested); >+ >+ DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", " >+ "granted=%"PRIu32", do_upgrade=%d\n", >+ existing, requested, granted, (int)do_upgrade)); >+ >+ if (do_upgrade) { >+ o->current_state = granted; >+ } >+ fsp->lease->lease.lease_state = o->current_state; >+ return NT_STATUS_OK; >+ } >+ >+ /* >+ * Create new lease >+ */ >+ >+ tmp = talloc_realloc(d, d->leases, struct share_mode_oplock, >+ d->num_leases+1); >+ if (tmp == NULL) { >+ /* >+ * See [MS-SMB2] >+ */ >+ return NT_STATUS_INSUFFICIENT_RESOURCES; >+ } >+ d->leases = tmp; >+ >+ fsp->lease = talloc(fsp->conn->sconn, struct fsp_lease); >+ if (fsp->lease == NULL) { >+ return NT_STATUS_INSUFFICIENT_RESOURCES; >+ } >+ fsp->lease->ref_count = 1; >+ fsp->lease->lease = *lease; >+ fsp->lease->lease.lease_state = granted; >+ fsp->lease->lease.lease_epoch += 1; >+ >+ *p_lease_idx = d->num_leases; >+ >+ 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; >+ uint32_t lease_idx = UINT32_MAX; >+ NTSTATUS status; >+ bool ret; > > if (oplock_request & INTERNAL_OPEN_ONLY) { > /* No oplocks on internal open. */ >- fsp->oplock_type = NO_OPLOCK; >+ 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; >+ >+ for (i=0; i<d->num_share_modes; i++) { >+ struct share_mode_entry *e = &d->share_modes[i]; >+ uint32_t e_lease_type; > >- got_level2_oplock = false; >- got_a_none_oplock = false; >+ e_lease_type = get_lease_type(d, e); > >- for (i=0; i<lck->data->num_share_modes; i++) { >- int op_type = lck->data->share_modes[i].op_type; >+ if ((granted & SMB2_LEASE_WRITE) && >+ !is_same_lease(d, e, lease) && >+ !share_mode_stale_pid(d, i)) { >+ /* >+ * Can grant only one writer >+ */ >+ granted &= ~SMB2_LEASE_WRITE; >+ } > >- if (LEVEL_II_OPLOCK_TYPE(op_type)) { >- got_level2_oplock = true; >+ if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease && >+ !share_mode_stale_pid(d, i)) { >+ got_handle_lease = true; > } >- if (op_type == NO_OPLOCK) { >- got_a_none_oplock = true; >+ >+ if ((e->op_type != LEASE_OPLOCK) && !got_oplock && >+ !share_mode_stale_pid(d, i)) { >+ got_oplock = true; > } > } > >- /* >- * Match what was requested (fsp->oplock_type) with >- * what was found in the existing share modes. >- */ >+ if (oplock_request == LEASE_OPLOCK) { > >- if (got_level2_oplock || got_a_none_oplock) { >- if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { >- fsp->oplock_type = LEVEL_II_OPLOCK; >+ fsp->oplock_type = LEASE_OPLOCK; >+ >+ if (got_oplock) { >+ granted &= SMB2_LEASE_READ; > } >- } > >- /* >- * 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; >- } >+ status = grant_fsp_lease(fsp, lck->data, lease, &lease_idx, >+ granted); >+ if (!NT_STATUS_IS_OK(status)) { >+ return status; > >- if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) { >- /* >- * We're the first level2 oplock. Indicate that in brlock.tdb. >- */ >- struct byte_range_lock *brl; >+ } >+ 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 +2729,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 +2853,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 +3004,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, > } > } > >- grant_fsp_oplock_type(fsp, lck, oplock_request); >- > /* > * We have the share entry *locked*..... > */ >@@ -2799,9 +3063,22 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, > } > > if (file_existed) { >- /* stat opens on existing files don't get oplocks. */ >+ /* >+ * stat opens on existing files don't get oplocks or leases. >+ * >+ * Note that we check for stat open on the *open_access_mask*, >+ * i.e. the access mask we actually used to do the open, >+ * not the one the client asked for (which is in >+ * fsp->access_mask). This is due to the fact that >+ * FILE_OVERWRITE and FILE_OVERWRITE_IF add in O_TRUNC, >+ * which adds FILE_WRITE_DATA to open_access_mask. >+ */ > if (is_stat_open(open_access_mask)) { >- fsp->oplock_type = NO_OPLOCK; >+ if (lease) { >+ lease->lease_state = SMB2_LEASE_NONE; >+ } else { >+ oplock_request = NO_OPLOCK; >+ } > } > } > >@@ -2824,20 +3101,12 @@ 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)) { > TALLOC_FREE(lck); > fd_close(fsp); >- return NT_STATUS_NO_MEMORY; >+ return status; > } > > /* Handle strange delete on close create semantics. */ >@@ -3331,7 +3600,7 @@ static NTSTATUS open_directory(connection_struct *conn, > } > > if (!set_share_mode(lck, fsp, get_current_uid(conn), >- req ? req->mid : 0, NO_OPLOCK)) { >+ req ? req->mid : 0, NO_OPLOCK, UINT32_MAX)) { > TALLOC_FREE(lck); > fd_close(fsp); > file_free(req, fsp); >@@ -3816,6 +4085,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 +4182,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..c55fb3b 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,14 +781,28 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp, > tevent_schedule_immediate(im, sconn->ev_ctx, do_break_to_none, state); > } > >+static void send_break_to_none(struct messaging_context *msg_ctx, >+ const struct share_mode_entry *e) >+{ >+ char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; >+ >+ share_mode_entry_to_message(msg, e); >+ /* Overload entry->op_type */ >+ SSVAL(msg, OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK); >+ >+ messaging_send_buf(msg_ctx, e->pid, MSG_SMB_BREAK_REQUEST, >+ (uint8 *)msg, sizeof(msg)); >+} >+ > static void do_break_to_none(struct tevent_context *ctx, > struct tevent_immediate *im, > void *private_data) > { > struct break_to_none_state *state = talloc_get_type_abort( > private_data, struct break_to_none_state); >- int i; >+ uint32_t i; > struct share_mode_lock *lck; >+ struct share_mode_data *d; > > lck = get_existing_share_mode_lock(talloc_tos(), state->id); > if (lck == NULL) { >@@ -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 936e87b7..bde21e4 100644 >--- a/source3/utils/status.c >+++ b/source3/utils/status.c >@@ -177,6 +177,8 @@ static int print_share_mode(const struct share_mode_entry *e, > d_printf("BATCH "); > } else if (e->op_type & LEVEL_II_OPLOCK) { > d_printf("LEVEL_II "); >+ } else if (e->op_type == LEASE_OPLOCK) { >+ d_printf("LEASE "); > } else { > d_printf("NONE "); > } >diff --git a/source3/wscript_build b/source3/wscript_build >index 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 901e265b6a3ac6291030b409e5ea80e8403b54ca Mon Sep 17 00:00:00 2001 >From: Michael Adam <obnox@samba.org> >Date: Sun, 21 Sep 2014 09:32:25 +0200 >Subject: [PATCH 04/20] smbd: use smbXsrv_connection as argument to > smbd_smb2_send_lease_break() > >Signed-off-by: Michael Adam <obnox@samba.org> >Reviewed-by: Jeremy Allison <jra@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 ed6837e75f62f786b2d7d36ccb628a8c59d5ef51 Mon Sep 17 00:00:00 2001 >From: Michael Adam <obnox@samba.org> >Date: Sun, 21 Sep 2014 09:46:30 +0200 >Subject: [PATCH 05/20] selftest: samba3.smb2.leases.v2_request_parent succeeds > >Signed-off-by: Michael Adam <obnox@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >--- > selftest/knownfail | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/selftest/knownfail b/selftest/knownfail >index 6b720f0..3f13ee0 100644 >--- a/selftest/knownfail >+++ b/selftest/knownfail >@@ -194,7 +194,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 ebcfeb7666caa4fa3ea79da8f39e46776eb90e7a Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 23 Sep 2014 02:28:38 +0200 >Subject: [PATCH 06/20] s3: leases - epoch handling > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@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 9795625..79616d2 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 c55fb3b..f80a3999 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 25a6136468e9aad4f5cd5b4c852ad64bd1a44e86 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 23 Sep 2014 18:49:46 +0200 >Subject: [PATCH 07/20] First test for NT_STATUS_INVALID_OPLOCK_PROTOCOL, then > for in_oplock_level being reasonable > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@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 d0a3c45036503844d5f139fae482096045bfa1c5 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 23 Sep 2014 22:56:41 +0200 >Subject: [PATCH 08/20] s3: leases - Only increment epoch for V2 leases > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@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 79616d2..e8da1d7 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 f80a3999..d42058c 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 941b33dd8310581874d47f8707c0d4c44686833b Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 23 Sep 2014 23:34:14 +0200 >Subject: [PATCH 09/20] s3: leases - break to none with FILE_OVERWRITE > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@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 e8da1d7..dba3552 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 0fd92bcd9d949328e34fe86ba0deac969e9a351b Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Fri, 10 Oct 2014 14:32:19 -0700 >Subject: [PATCH 10/20] s3: smbd: Leases - ensure all share mode removal > functions go through a common lease refcount manager. > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > source3/locking/locking.c | 136 ++++++++++++++++++++++++++++------------------ > 1 file changed, 82 insertions(+), 54 deletions(-) > >diff --git a/source3/locking/locking.c b/source3/locking/locking.c >index 170fa1d..82bc3b0 100644 >--- a/source3/locking/locking.c >+++ b/source3/locking/locking.c >@@ -618,6 +618,84 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e) > } > > /* >+ * See if we need to remove a lease being referred to by a >+ * share mode that is being marked stale or deleted. >+ */ >+ >+static void remove_share_mode_lease(struct share_mode_data *d, >+ struct share_mode_entry *e) >+{ >+ struct GUID client_guid; >+ struct smb2_lease_key lease_key; >+ uint16_t op_type; >+ uint32_t lease_idx; >+ uint32_t i; >+ >+ op_type = e->op_type; >+ e->op_type = NO_OPLOCK; >+ >+ d->modified = true; >+ >+ if (op_type != LEASE_OPLOCK) { >+ return; >+ } >+ >+ /* >+ * This used to reference a lease. If there's no other one referencing >+ * it, remove it. >+ */ >+ >+ lease_idx = e->lease_idx; >+ e->lease_idx = UINT32_MAX; >+ >+ for (i=0; i<d->num_share_modes; i++) { >+ if (d->share_modes[i].stale) { >+ continue; >+ } >+ if (e == &d->share_modes[i]) { >+ /* Not ourselves. */ >+ continue; >+ } >+ if (d->share_modes[i].lease_idx == lease_idx) { >+ break; >+ } >+ } >+ if (i < d->num_share_modes) { >+ /* >+ * Found another one >+ */ >+ return; >+ } >+ >+ memcpy(&client_guid, >+ &d->leases[lease_idx].client_guid, >+ sizeof(client_guid)); >+ lease_key = d->leases[lease_idx].lease_key; >+ >+ d->num_leases -= 1; >+ d->leases[lease_idx] = d->leases[d->num_leases]; >+ >+ /* >+ * We changed the lease array. Fix all references to it. >+ */ >+ for (i=0; i<d->num_share_modes; i++) { >+ if (d->share_modes[i].lease_idx == d->num_leases) { >+ d->share_modes[i].lease_idx = lease_idx; >+ } >+ } >+ >+ { >+ NTSTATUS status; >+ >+ status = leases_db_del(&client_guid, >+ &lease_key); >+ >+ 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 +753,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 +853,7 @@ bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp) > if (e == NULL) { > return False; > } >+ remove_share_mode_lease(lck->data, e); > *e = lck->data->share_modes[lck->data->num_share_modes-1]; > lck->data->num_share_modes -= 1; > lck->data->modified = True; >@@ -822,67 +903,14 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) > { > struct share_mode_data *d = lck->data; > struct share_mode_entry *e; >- uint16_t op_type; >- uint32_t lease_idx; >- uint32_t i; > > e = find_share_mode_entry(lck, fsp); > if (e == NULL) { > return False; > } > >- op_type = e->op_type; >- e->op_type = NO_OPLOCK; >- >+ remove_share_mode_lease(d, e); > d->modified = True; >- >- if (op_type != LEASE_OPLOCK) { >- return true; >- } >- >- /* >- * This used to reference a lease. If there's no other one referencing >- * it, remove it. >- */ >- >- lease_idx = e->lease_idx; >- e->lease_idx = UINT32_MAX; >- >- for (i=0; i<d->num_share_modes; i++) { >- if (d->share_modes[i].lease_idx == lease_idx) { >- break; >- } >- } >- if (i < d->num_share_modes) { >- /* >- * Found another one >- */ >- return true; >- } >- >- d->num_leases -= 1; >- d->leases[lease_idx] = d->leases[d->num_leases]; >- >- /* >- * We changed the lease array. Fix all references to it. >- */ >- for (i=0; i<d->num_share_modes; i++) { >- if (d->share_modes[i].lease_idx == d->num_leases) { >- d->share_modes[i].lease_idx = lease_idx; >- } >- } >- >- { >- NTSTATUS status; >- >- status = leases_db_del( >- &fsp->conn->sconn->client->connections->smb2.client.guid, >- &fsp->lease->lease.lease_key); >- >- DEBUG(10, ("%s: leases_db_del returned %s\n", __func__, >- nt_errstr(status))); >- } >- > return true; > } > >-- >2.1.0.rc2.206.gedb03e5 > > >From 87a1111438d1ad52e03892e3e65560b1e5b04ade Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Fri, 10 Oct 2014 16:36:54 -0700 >Subject: [PATCH 11/20] 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@samba.org> >--- > source3/librpc/idl/leases_db.idl | 24 +++ > source3/locking/leases_db.c | 368 +++++++++++++++++++++++++++++++++++++++ > source3/locking/leases_db.h | 46 +++++ > source3/locking/locking.c | 3 +- > 4 files changed, 440 insertions(+), 1 deletion(-) > create mode 100644 source3/librpc/idl/leases_db.idl > create mode 100644 source3/locking/leases_db.c > create mode 100644 source3/locking/leases_db.h > >diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl >new file mode 100644 >index 0000000..28ec479 >--- /dev/null >+++ b/source3/librpc/idl/leases_db.idl >@@ -0,0 +1,24 @@ >+#include "idl_types.h" >+ >+import "misc.idl"; >+import "smb2_lease_struct.idl"; >+import "file_id.idl"; >+ >+[ >+ pointer_default(unique) >+] >+ >+interface leases_db >+{ >+ typedef [public] struct { >+ GUID client_guid; >+ smb2_lease_key lease_key; >+ } leases_db_key; >+ >+ typedef [public] struct { >+ uint32 num_file_ids; >+ [size_is(num_file_ids)] file_id ids[]; >+ [string,charset(UTF8)] char *filename; >+ [string,charset(UTF8)] char *stream_name; >+ } leases_db_value; >+} >diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c >new file mode 100644 >index 0000000..29b8e35 >--- /dev/null >+++ b/source3/locking/leases_db.c >@@ -0,0 +1,368 @@ >+/* >+ Unix SMB/CIFS implementation. >+ Map lease keys to file ids >+ Copyright (C) Volker Lendecke 2013 >+ >+ This program is free software; you can redistribute it and/or modify >+ it under the terms of the GNU General Public License as published by >+ the Free Software Foundation; either version 3 of the License, or >+ (at your option) any later version. >+ >+ This program is distributed in the hope that it will be useful, >+ but WITHOUT ANY WARRANTY; without even the implied warranty of >+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ GNU General Public License for more details. >+ >+ You should have received a copy of the GNU General Public License >+ along with this program. If not, see <http://www.gnu.org/licenses/>. >+ >+*/ >+ >+#include "includes.h" >+#include "system/filesys.h" >+#include "locking/leases_db.h" >+#include "dbwrap/dbwrap.h" >+#include "dbwrap/dbwrap_open.h" >+#include "util_tdb.h" >+#include "ndr.h" >+#include "librpc/gen_ndr/ndr_leases_db.h" >+ >+#undef DBGC_CLASS >+#define DBGC_CLASS DBGC_LOCKING >+ >+/* the leases database handle */ >+static struct db_context *leases_db; >+ >+bool leases_db_init(bool read_only) >+{ >+ if (leases_db) { >+ return true; >+ } >+ >+ leases_db = db_open(NULL, lock_path("leases.tdb"), 0, >+ TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST| >+ TDB_INCOMPATIBLE_HASH, >+ read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644, >+ DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE); >+ >+ if (leases_db == NULL) { >+ DEBUG(1, ("ERROR: Failed to initialise leases database\n")); >+ return false; >+ } >+ >+ return true; >+} >+ >+static bool leases_db_key(TALLOC_CTX *mem_ctx, >+ const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ TDB_DATA *key) >+{ >+ struct leases_db_key db_key = { >+ .client_guid = *client_guid, >+ .lease_key = *lease_key }; >+ DATA_BLOB blob; >+ enum ndr_err_code ndr_err; >+ >+ if (DEBUGLEVEL >= 10) { >+ DEBUG(10, ("%s:\n", __func__)); >+ NDR_PRINT_DEBUG(leases_db_key, &db_key); >+ } >+ >+ ndr_err = ndr_push_struct_blob( >+ &blob, mem_ctx, &db_key, >+ (ndr_push_flags_fn_t)ndr_push_leases_db_key); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ return false; >+ } >+ >+ *key = make_tdb_data(blob.data, blob.length); >+ return true; >+} >+ >+NTSTATUS leases_db_add(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id, >+ const char *filename, >+ const char *stream_name) >+{ >+ TDB_DATA db_key, db_value; >+ DATA_BLOB blob; >+ struct db_record *rec; >+ NTSTATUS status; >+ bool ok; >+ struct leases_db_value new_value; >+ struct leases_db_value *value = NULL; >+ enum ndr_err_code ndr_err; >+ >+ if (!leases_db_init(false)) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key); >+ if (!ok) { >+ DEBUG(10, ("%s: leases_db_key failed\n", __func__)); >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key); >+ TALLOC_FREE(db_key.dptr); >+ if (rec == NULL) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ db_value = dbwrap_record_get_value(rec); >+ if (db_value.dsize != 0) { >+ uint32_t i; >+ >+ DEBUG(10, ("%s: record exists\n", __func__)); >+ >+ value = talloc(talloc_tos(), struct leases_db_value); >+ if (value == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto out; >+ } >+ >+ blob.data = db_value.dptr; >+ blob.length = db_value.dsize; >+ >+ ndr_err = ndr_pull_struct_blob_all( >+ &blob, value, value, >+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ /* id must be unique. */ >+ for (i = 0; i < value->num_file_ids; i++) { >+ if (file_id_equal(id, &value->ids[i])) { >+ status = NT_STATUS_OBJECT_NAME_COLLISION; >+ goto out; >+ } >+ } >+ >+ value->ids = talloc_realloc(value, value->ids, struct file_id, >+ value->num_file_ids + 1); >+ if (value->ids == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto out; >+ } >+ value->ids[value->num_file_ids] = *id; >+ value->num_file_ids += 1; >+ >+ } else { >+ new_value = (struct leases_db_value) { >+ .num_file_ids = 1, >+ .ids = id, >+ .filename = filename, >+ .stream_name = stream_name, >+ }; >+ value = &new_value; >+ } >+ >+ ndr_err = ndr_push_struct_blob( >+ &blob, talloc_tos(), value, >+ (ndr_push_flags_fn_t)ndr_push_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ db_value = make_tdb_data(blob.data, blob.length); >+ >+ status = dbwrap_record_store(rec, db_value, 0); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n", >+ __func__, nt_errstr(status))); >+ } >+ >+ out: >+ >+ if (value != &new_value) { >+ TALLOC_FREE(value); >+ } >+ TALLOC_FREE(rec); >+ return status; >+} >+ >+NTSTATUS leases_db_del(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id) >+{ >+ TDB_DATA db_key, db_value; >+ struct db_record *rec; >+ NTSTATUS status; >+ struct leases_db_value *value; >+ enum ndr_err_code ndr_err; >+ DATA_BLOB blob; >+ uint32_t i; >+ bool ok; >+ >+ if (!leases_db_init(false)) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key); >+ if (!ok) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key); >+ TALLOC_FREE(db_key.dptr); >+ if (rec == NULL) { >+ return NT_STATUS_NOT_FOUND; >+ } >+ db_value = dbwrap_record_get_value(rec); >+ if (db_value.dsize == 0) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto out; >+ } >+ >+ value = talloc(talloc_tos(), struct leases_db_value); >+ if (value == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto out; >+ } >+ >+ blob.data = db_value.dptr; >+ blob.length = db_value.dsize; >+ >+ ndr_err = ndr_pull_struct_blob_all( >+ &blob, value, value, >+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ /* id must exist. */ >+ for (i = 0; i < value->num_file_ids; i++) { >+ if (file_id_equal(id, &value->ids[i])) { >+ break; >+ } >+ } >+ >+ if (i == value->num_file_ids) { >+ status = NT_STATUS_NOT_FOUND; >+ goto out; >+ } >+ >+ value->ids[i] = value->ids[value->num_file_ids-1]; >+ value->num_file_ids -= 1; >+ >+ if (value->num_file_ids == 0) { >+ status = dbwrap_record_delete(rec); >+ } else { >+ ndr_err = ndr_push_struct_blob( >+ &blob, talloc_tos(), value, >+ (ndr_push_flags_fn_t)ndr_push_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ db_value = make_tdb_data(blob.data, blob.length); >+ >+ status = dbwrap_record_store(rec, db_value, 0); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n", >+ __func__, nt_errstr(status))); >+ } >+ } >+ >+ out: >+ >+ TALLOC_FREE(value); >+ TALLOC_FREE(rec); >+ return status; >+} >+ >+struct leases_db_fetch_state { >+ void (*parser)(uint32_t num_file_ids, >+ struct file_id *ids, const char *filename, >+ const char *stream_name, void *private_data); >+ void *private_data; >+ NTSTATUS status; >+}; >+ >+static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data) >+{ >+ struct leases_db_fetch_state *state = >+ (struct leases_db_fetch_state *)private_data; >+ DATA_BLOB blob = { .data = data.dptr, .length = data.dsize }; >+ enum ndr_err_code ndr_err; >+ struct leases_db_value *value; >+ >+ value = talloc(talloc_tos(), struct leases_db_value); >+ if (value == NULL) { >+ state->status = NT_STATUS_NO_MEMORY; >+ return; >+ } >+ >+ ndr_err = ndr_pull_struct_blob_all( >+ &blob, value, value, >+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ TALLOC_FREE(value); >+ state->status = ndr_map_error2ntstatus(ndr_err); >+ return; >+ } >+ >+ state->parser(value->num_file_ids, >+ value->ids, value->filename, value->stream_name, >+ state->private_data); >+ >+ TALLOC_FREE(value); >+ state->status = NT_STATUS_OK; >+} >+ >+NTSTATUS leases_db_parse(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ void (*parser)(uint32_t num_file_ids, >+ struct file_id *ids, >+ const char *filename, >+ const char *stream_name, >+ void *private_data), >+ void *private_data) >+{ >+ TDB_DATA db_key; >+ struct leases_db_fetch_state state; >+ NTSTATUS status; >+ bool ok; >+ >+ if (!leases_db_init(true)) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key); >+ if (!ok) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ state = (struct leases_db_fetch_state) { >+ .parser = parser, >+ .private_data = private_data, >+ .status = NT_STATUS_OK >+ }; >+ >+ status = dbwrap_parse_record(leases_db, db_key, leases_db_parser, >+ &state); >+ TALLOC_FREE(db_key.dptr); >+ if (!NT_STATUS_IS_OK(status)) { >+ return status; >+ } >+ return state.status; >+} >diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h >new file mode 100644 >index 0000000..f570356 >--- /dev/null >+++ b/source3/locking/leases_db.h >@@ -0,0 +1,46 @@ >+/* >+ * Unix SMB/CIFS implementation. >+ * leases.tdb functions >+ * >+ * Copyright (C) Volker Lendecke 2014 >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 3 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program. If not, see <http://www.gnu.org/licenses/>. >+ */ >+ >+#ifndef _LEASES_DB_H_ >+#define _LEASES_DB_H_ >+ >+struct GUID; >+struct smb2_lease_key; >+struct file_id; >+ >+bool leases_db_init(bool read_only); >+NTSTATUS leases_db_add(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id, >+ const char *filename, >+ const char *stream_name); >+NTSTATUS leases_db_del(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id); >+NTSTATUS leases_db_parse(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ void (*parser)(uint32_t num_file_ids, >+ struct file_id *ids, >+ const char *filename, >+ const char *stream_name, >+ void *private_data), >+ void *private_data); >+ >+#endif /* _LEASES_DB_H_ */ >diff --git a/source3/locking/locking.c b/source3/locking/locking.c >index 82bc3b0..8132c71 100644 >--- a/source3/locking/locking.c >+++ b/source3/locking/locking.c >@@ -688,7 +688,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))); >-- >2.1.0.rc2.206.gedb03e5 > > >From d4ca501f342173603d19eae0a4f11381b6ae9dd4 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Fri, 10 Oct 2014 17:05:46 -0700 >Subject: [PATCH 12/20] s3: smbd: leases - Fix the dynamic share file case > [homes]. > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > source3/smbd/open.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++------ > 1 file changed, 135 insertions(+), 16 deletions(-) > >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index dba3552..b6009e1 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -4094,39 +4094,148 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) > */ > > struct lease_fname_match_state { >+ /* Input parameters. */ > const struct smb_filename *fname; >- bool match; >+ bool file_existed; >+ struct file_id id; >+ /* Return parameters. */ >+ uint32_t num_file_ids; >+ struct file_id *ids; >+ NTSTATUS match_status; > }; > > static void lease_fname_match_parser( >- struct file_id id, const char *filename, const char *stream_name, >+ uint32_t num_file_ids, >+ struct file_id *ids, const char *filename, const char *stream_name, > void *private_data) > { > struct lease_fname_match_state *state = > (struct lease_fname_match_state *)private_data; > >- state->match = >- strequal(filename, state->fname->base_name) && >- strequal(stream_name, state->fname->stream_name); >+ if (!strequal(filename, state->fname->base_name) || >+ !strequal(stream_name, state->fname->stream_name)) { >+ /* Names don't match lease key. */ >+ state->match_status = NT_STATUS_INVALID_PARAMETER; >+ return; >+ } >+ >+ if (!state->file_existed) { >+ /* New file. */ >+ state->match_status = NT_STATUS_OK; >+ return; >+ } >+ >+ if (num_file_ids == 1 && file_id_equal(&ids[0],&state->id)) { >+ /* Common case - non-dynamic share. We're ok.. */ >+ state->match_status = NT_STATUS_OK; >+ return; >+ } >+ >+ /* More than one file id, or not equal. Don't allow leases. */ >+ state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; >+ state->num_file_ids = num_file_ids; >+ state->ids = talloc_memdup(talloc_tos(), >+ ids, >+ num_file_ids * sizeof(struct file_id)); >+ if (state->ids == NULL) { >+ state->match_status = NT_STATUS_NO_MEMORY; >+ } > } > >-static bool lease_fname_match(struct smbd_server_connection *sconn, >- struct smb2_lease_key *lease_key, >- const struct smb_filename *fname) >+static NTSTATUS lease_match(connection_struct *conn, >+ struct smb_request *req, >+ struct smb2_lease_key *lease_key, >+ const struct smb_filename *fname) > { >- struct lease_fname_match_state state = >- { .fname = fname, .match = true }; >+ struct smbd_server_connection *sconn = req->sconn; >+ struct lease_fname_match_state state = { >+ .fname = fname, >+ .match_status = NT_STATUS_OK >+ }; >+ uint32_t i; > NTSTATUS status; > >+ state.file_existed = VALID_STAT(fname->st); >+ if (state.file_existed) { >+ state.id = vfs_file_id_from_sbuf(conn, &fname->st); >+ } >+ > status = leases_db_parse(&sconn->client->connections->smb2.client.guid, > lease_key, lease_fname_match_parser, &state); > if (!NT_STATUS_IS_OK(status)) { > /* > * Not found or error means okay: We can make the lease pass > */ >- return true; >+ return NT_STATUS_OK; > } >- return state.match; >+ if (!NT_STATUS_EQUAL(state.match_status, NT_STATUS_OPLOCK_NOT_GRANTED)) { >+ /* >+ * Anything but NT_STATUS_OPLOCK_NOT_GRANTED, let the caller >+ * deal with it. >+ */ >+ return state.match_status; >+ } >+ >+ /* We have to break all existing leases. */ >+ for (i = 0; i < state.num_file_ids; i++) { >+ struct share_mode_lock *lck; >+ struct share_mode_data *d; >+ uint32_t j; >+ >+ if (file_id_equal(&state.ids[i], &state.id)) { >+ /* Don't need to break our own file. */ >+ continue; >+ } >+ lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]); >+ if (lck == NULL) { >+ /* Race condition - file already closed. */ >+ continue; >+ } >+ d = lck->data; >+ for (j=0; j<d->num_share_modes; j++) { >+ struct share_mode_entry *e = &d->share_modes[j]; >+ uint32_t e_lease_type = get_lease_type(d, e); >+ >+ if (e_lease_type == SMB2_LEASE_NONE) { >+ continue; >+ } >+ if (share_mode_stale_pid(d, j)) { >+ continue; >+ } >+ >+ send_break_message(conn->sconn->msg_ctx, e, >+ SMB2_LEASE_NONE); >+ >+ /* >+ * Windows 7 and 8 lease clients >+ * are broken in that they will not >+ * respond to lease break requests >+ * whilst waiting for an outstanding >+ * open request on that lease handle >+ * on the same TCP connection, due >+ * to holding an internal inode lock. >+ * >+ * This means we can't reschedule >+ * ourselves here, but must return >+ * from the create. >+ * >+ * Work around: >+ * >+ * Send the breaks and then return >+ * SMB2_LEASE_NONE in the lease handle >+ * to cause them to acknowledge the >+ * lease break. Consulatation with >+ * Microsoft engineering confirmed >+ * this approach is safe. >+ */ >+ } >+ TALLOC_FREE(lck); >+ } >+ /* >+ * Ensure we don't grant anything more so we >+ * never upgrade. >+ */ >+ return NT_STATUS_OPLOCK_NOT_GRANTED; > } > > /* >@@ -4185,10 +4294,20 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, > oplock_request |= INTERNAL_OPEN_ONLY; > } > >- if ((lease != NULL) && >- !lease_fname_match(req->sconn, &lease->lease_key, smb_fname)) { >- status = NT_STATUS_INVALID_PARAMETER; >- goto fail; >+ if (lease != NULL) { >+ status = lease_match(conn, >+ req, >+ &lease->lease_key, >+ smb_fname); >+ if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) { >+ /* Dynamic share file. No leases and update epoch... */ >+ lease->lease_state = SMB2_LEASE_NONE; >+ if (lease->lease_version > 1) { >+ lease->lease_epoch += 1; >+ } >+ } else if (!NT_STATUS_IS_OK(status)) { >+ goto fail; >+ } > } > > if ((conn->fs_capabilities & FILE_NAMED_STREAMS) >-- >2.1.0.rc2.206.gedb03e5 > > >From 5892a7cd9176b9251aa9a08a633b07a0e87676db Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 14 Oct 2014 10:24:31 -0700 >Subject: [PATCH 13/20] 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@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 b6009e1..20b25a5 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 6429ca25f866c2cf2fb6274353603157067ef511 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Wed, 22 Oct 2014 17:53:01 -0700 >Subject: [PATCH 14/20] 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@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 20b25a5..71a8eaf 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -2679,9 +2679,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 93536b0039e9277dd6b5da6fe7002dc72296fb39 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 4 Nov 2014 21:44:45 -0800 >Subject: [PATCH 15/20] s3: leases : Add smb2_lease_equal() which compares > client_guids and keys. > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > libcli/smb/smb2_lease.c | 9 +++++++++ > libcli/smb/smb2_lease.h | 5 +++++ > 2 files changed, 14 insertions(+) > >diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c >index 41eafc9..9b448ac 100644 >--- a/libcli/smb/smb2_lease.c >+++ b/libcli/smb/smb2_lease.c >@@ -93,3 +93,12 @@ bool smb2_lease_key_equal(const struct smb2_lease_key *k1, > { > return ((k1->data[0] == k2->data[0]) && (k1->data[1] == k2->data[1])); > } >+ >+bool smb2_lease_equal(const struct GUID *g1, >+ const struct smb2_lease_key *k1, >+ const struct GUID *g2, >+ const struct smb2_lease_key *k2) >+{ >+ return GUID_equal(g1, g2) && >+ smb2_lease_key_equal(k1, k2); >+} >diff --git a/libcli/smb/smb2_lease.h b/libcli/smb/smb2_lease.h >index 9db239d..2e6faf7 100644 >--- a/libcli/smb/smb2_lease.h >+++ b/libcli/smb/smb2_lease.h >@@ -23,6 +23,7 @@ > #ifndef _LIBCLI_SMB_SMB2_LEASE_H_ > #define _LIBCLI_SMB_SMB2_LEASE_H_ > >+#include "librpc/gen_ndr/ndr_misc.h" > #include "librpc/gen_ndr/smb2_lease_struct.h" > > /* >@@ -34,5 +35,9 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len, > 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); >+bool smb2_lease_equal(const struct GUID *g1, >+ const struct smb2_lease_key *k1, >+ const struct GUID *g2, >+ const struct smb2_lease_key *k2); > > #endif /* _LIBCLI_SMB_SMB2_LEASE_H_ */ >-- >2.1.0.rc2.206.gedb03e5 > > >From 01eeaf30bc64c11fc72693c5d80a66e0b27fef12 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 4 Nov 2014 21:46:14 -0800 >Subject: [PATCH 16/20] s3: leases: Add fsp_client_guid() utility function to > return the connected client guid. > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > source3/smbd/files.c | 5 +++++ > source3/smbd/proto.h | 1 + > 2 files changed, 6 insertions(+) > >diff --git a/source3/smbd/files.c b/source3/smbd/files.c >index 13d1138..6a987c4 100644 >--- a/source3/smbd/files.c >+++ b/source3/smbd/files.c >@@ -774,3 +774,8 @@ uint32_t fsp_lease_type(struct files_struct *fsp) > } > return map_oplock_to_lease_type(fsp->oplock_type); > } >+ >+const struct GUID *fsp_client_guid(const files_struct *fsp) >+{ >+ return &fsp->conn->sconn->client->connections->smb2.client.guid; >+} >diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h >index 22b1a82..ecf89a7 100644 >--- a/source3/smbd/proto.h >+++ b/source3/smbd/proto.h >@@ -391,6 +391,7 @@ NTSTATUS file_name_hash(connection_struct *conn, > NTSTATUS fsp_set_smb_fname(struct files_struct *fsp, > const struct smb_filename *smb_fname_in); > uint32_t fsp_lease_type(struct files_struct *fsp); >+const struct GUID *fsp_client_guid(const files_struct *fsp); > > /* The following definitions come from smbd/ipc.c */ > >-- >2.1.0.rc2.206.gedb03e5 > > >From c3cda66839f645d3731bdf62078a2169f2f795a5 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 4 Nov 2014 21:47:14 -0800 >Subject: [PATCH 17/20] s3: leases: Ensure we check client_guid as well as > lease key to ensure lease identity. > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > source3/locking/locking.c | 10 ++++++++-- > source3/smbd/open.c | 39 ++++++++++++++++++++------------------- > 2 files changed, 28 insertions(+), 21 deletions(-) > >diff --git a/source3/locking/locking.c b/source3/locking/locking.c >index 8132c71..006d6d3 100644 >--- a/source3/locking/locking.c >+++ b/source3/locking/locking.c >@@ -960,7 +960,10 @@ bool fsp_lease_broken(struct share_mode_lock *lck, > return false; > } > >- if (!smb2_lease_key_equal(key, &d->leases[e->lease_idx].lease_key)) { >+ if (!smb2_lease_equal(&fsp->conn->sconn->client->connections->smb2.client.guid, >+ key, >+ &d->leases[e->lease_idx].client_guid, >+ &d->leases[e->lease_idx].lease_key)) { > return false; > } > fsp->sent_oplock_break = NO_BREAK_SENT; >@@ -979,7 +982,10 @@ NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn, > uint32_t i; > > for (i=0; i<d->num_leases; i++) { >- if (smb2_lease_key_equal(key, &d->leases[i].lease_key)) { >+ if (smb2_lease_equal(&sconn->client->connections->smb2.client.guid, >+ key, >+ &d->leases[i].client_guid, >+ &d->leases[i].lease_key)) { > break; > } > } >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index 71a8eaf..4052a7e 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -37,11 +37,11 @@ > #include "source3/lib/dbwrap/dbwrap_watch.h" > #include "locking/leases_db.h" > >-static bool is_same_lease(const struct share_mode_data *d, >+static bool is_same_lease(const files_struct *fsp, >+ const struct share_mode_data *d, > const struct share_mode_entry *e, > const struct smb2_lease *lease); > >- > extern const struct generic_mapping file_generic_mapping; > > struct deferred_open_record { >@@ -1424,7 +1424,7 @@ static bool delay_for_oplock(files_struct *fsp, > if (!(e_lease_type & SMB2_LEASE_HANDLE)) { > continue; > } >- if (is_same_lease(d, e, lease)) { >+ if (is_same_lease(fsp, d, e, lease)) { > continue; > } > if (share_mode_stale_pid(d, i)) { >@@ -1470,9 +1470,10 @@ static bool delay_for_oplock(files_struct *fsp, > } > if ((e->op_type == LEASE_OPLOCK) && > (lease != NULL) && >- smb2_lease_key_equal( >- &lease->lease_key, >- &d->leases[e->lease_idx].lease_key)) { >+ smb2_lease_equal(fsp_client_guid(fsp), >+ &lease->lease_key, >+ &d->leases[e->lease_idx].client_guid, >+ &d->leases[e->lease_idx].lease_key)) { > return false; > } > >@@ -1529,15 +1530,18 @@ int find_share_mode_oplock(struct share_mode_data *d, > > for (i=0; i<d->num_leases; i++) { > struct share_mode_oplock *l = &d->leases[i]; >- if (GUID_equal(client_guid, &l->client_guid) && >- smb2_lease_key_equal(key, &l->lease_key)) { >+ if (smb2_lease_equal(client_guid, >+ key, >+ &l->client_guid, >+ &l->lease_key)) { > return i; > } > } > return -1; > } > >-static bool is_same_lease(const struct share_mode_data *d, >+static bool is_same_lease(const files_struct *fsp, >+ const struct share_mode_data *d, > const struct share_mode_entry *e, > const struct smb2_lease *lease) > { >@@ -1547,8 +1551,11 @@ static bool is_same_lease(const struct share_mode_data *d, > if (lease == NULL) { > return false; > } >- return smb2_lease_key_equal(&d->leases[e->lease_idx].lease_key, >- &lease->lease_key); >+ >+ return smb2_lease_equal(fsp_client_guid(fsp), >+ &lease->lease_key, >+ &d->leases[e->lease_idx].client_guid, >+ &d->leases[e->lease_idx].lease_key); > } > > struct fsp_lease *find_fsp_lease(files_struct *new_fsp, >@@ -1595,17 +1602,11 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d, > uint32_t *p_lease_idx, > uint32_t granted) > { >- const struct GUID *client_guid; >+ const struct GUID *client_guid = fsp_client_guid(fsp); > struct share_mode_oplock *tmp; > NTSTATUS status; > int idx; > >- >- /* >- * TODO: in future we can have multiple connections... >- */ >- client_guid = &fsp->conn->sconn->client->connections->smb2.client.guid; >- > idx = find_share_mode_oplock(d, client_guid, &lease->lease_key); > > if (idx != -1) { >@@ -1759,7 +1760,7 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp > e_lease_type = get_lease_type(d, e); > > if ((granted & SMB2_LEASE_WRITE) && >- !is_same_lease(d, e, lease) && >+ !is_same_lease(fsp, d, e, lease) && > !share_mode_stale_pid(d, i)) { > /* > * Can grant only one writer >-- >2.1.0.rc2.206.gedb03e5 > > >From 5223b6f0a4591be207ab7e968fec1812c0f05e6c Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 4 Nov 2014 21:47:51 -0800 >Subject: [PATCH 18/20] s3: leases: Ensure the client guids match when doing a > durable reconnect. > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > source3/smbd/durable.c | 11 +++++++++++ > 1 file changed, 11 insertions(+) > >diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c >index 73bcd58..6e668ce 100644 >--- a/source3/smbd/durable.c >+++ b/source3/smbd/durable.c >@@ -737,6 +737,17 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, > fsp_free(fsp); > return NT_STATUS_NO_MEMORY; > } >+ >+ /* >+ * Ensure the existing client guid matches the >+ * stored one in the share_mode_oplock. >+ */ >+ if (!GUID_equal(fsp_client_guid(fsp), >+ &o->client_guid)) { >+ TALLOC_FREE(lck); >+ fsp_free(fsp); >+ return NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } > } > > fsp->initial_allocation_size = cookie.initial_allocation_size; >-- >2.1.0.rc2.206.gedb03e5 > > >From 0363be4c17083f57dc9cba2d23fdddd02a9fba3d Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 14 Oct 2014 10:34:53 -0700 >Subject: [PATCH 19/20] s3: smbd: Add "smb2 leases" parameter. Default "false". > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > docs-xml/smbdotconf/locking/smb2leases.xml | 19 +++++++++++++++++++ > lib/param/param_table.c | 9 +++++++++ > selftest/target/Samba3.pm | 1 + > source3/param/loadparm.c | 1 + > source3/smbd/smb2_negprot.c | 2 +- > 5 files changed, 31 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 7a13e6f..b6ff571 100644 >--- a/lib/param/param_table.c >+++ b/lib/param/param_table.c >@@ -3010,6 +3010,15 @@ struct parm_struct parm_table[] = { > .flags = FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL, > }, > { >+ .label = "smb2 leases", >+ .type = P_BOOL, >+ .p_class = P_GLOBAL, >+ .offset = GLOBAL_VAR(smb2_leases), >+ .special = NULL, >+ .enum_list = NULL, >+ .flags = FLAG_ADVANCED | FLAG_SHARE, >+ }, >+ { > .label = "locking", > .type = P_BOOL, > .p_class = P_LOCAL, >diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm >index 48b8164..ae0a3a6 100755 >--- a/selftest/target/Samba3.pm >+++ b/selftest/target/Samba3.pm >@@ -1051,6 +1051,7 @@ sub provision($$$$$$) > > kernel oplocks = no > kernel change notify = no >+ smb2 leases = yes > > syslog = no > printing = bsd >diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c >index d6ba8fb..4a85045 100644 >--- a/source3/param/loadparm.c >+++ b/source3/param/loadparm.c >@@ -854,6 +854,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals) > Globals.smb2_max_write = DEFAULT_SMB2_MAX_WRITE; > Globals.smb2_max_trans = DEFAULT_SMB2_MAX_TRANSACT; > Globals.ismb2_max_credits = DEFAULT_SMB2_MAX_CREDITS; >+ Globals.smb2_leases = false; > > string_set(Globals.ctx, &Globals.ncalrpc_dir, get_dyn_NCALRPCDIR()); > >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 dd8df95cea8a86455f60049a6f50d0f1a323045c Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Wed, 5 Nov 2014 10:12:20 -0800 >Subject: [PATCH 20/20] s3: leases : Add smb2.oplock test batch9a and > raw.oplock test batch9a > >Shows attribute(stat) access open can create a file, >and subsequent attribute(stat) opens don't break oplocks. > >Can be extended to explore more varients. > >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > selftest/knownfail | 2 + > source4/torture/raw/oplock.c | 121 +++++++++++++++++++++++++++++++++++++++ > source4/torture/smb2/oplock.c | 128 ++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 251 insertions(+) > >diff --git a/selftest/knownfail b/selftest/knownfail >index 3f13ee0..310c5d9 100644 >--- a/selftest/knownfail >+++ b/selftest/knownfail >@@ -130,6 +130,7 @@ > ^samba4.raw.lock.*.async # bug 6960 > ^samba4.smb2.lock.*.multiple-unlock # bug 6959 > ^samba4.raw.sfileinfo.*.end-of-file\(.*\)$ # bug 6962 >+^samba4.raw.oplock.*.batch9a > ^samba4.raw.oplock.*.batch22 # bug 6963 > ^samba4.raw.oplock.*.doc1 > ^samba4.raw.oplock.*.exclusive5 >@@ -167,6 +168,7 @@ > ^samba4.smb2.oplock.batch1\(.*\)$ # samba 4 oplocks are a mess > ^samba4.smb2.oplock.batch6\(.*\)$ # samba 4 oplocks are a mess > ^samba4.smb2.oplock.batch9\(.*\)$ # samba 4 oplocks are a mess >+^samba4.smb2.oplock.batch9a\(.*\)$ # samba 4 oplocks are a mess > ^samba4.smb2.oplock.batch10\(.*\)$ # samba 4 oplocks are a mess > ^samba4.smb2.oplock.batch20\(.*\)$ # samba 4 oplocks are a mess > ^samba4.smb2.oplock.batch26\(.*\)$ >diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c >index a4f6a05..1d7522f 100644 >--- a/source4/torture/raw/oplock.c >+++ b/source4/torture/raw/oplock.c >@@ -1873,6 +1873,126 @@ done: > return ret; > } > >+static bool test_raw_oplock_batch9a(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) >+{ >+ const char *fname = BASEDIR "\\test_batch9a.dat"; >+ NTSTATUS status; >+ bool ret = true; >+ union smb_open io; >+ uint16_t fnum=0, fnum2=0; >+ char c = 0; >+ >+ if (!torture_setup_dir(cli1, BASEDIR)) { >+ return false; >+ } >+ >+ /* cleanup */ >+ smbcli_unlink(cli1->tree, fname); >+ >+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); >+ >+ /* >+ base ntcreatex parms >+ */ >+ io.generic.level = RAW_OPEN_NTCREATEX; >+ io.ntcreatex.in.root_fid.fnum = 0; >+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; >+ io.ntcreatex.in.alloc_size = 0; >+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; >+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; >+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; >+ io.ntcreatex.in.create_options = 0; >+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; >+ io.ntcreatex.in.security_flags = 0; >+ io.ntcreatex.in.fname = fname; >+ >+ torture_comment(tctx, "BATCH9: open with attributes only can create file\n"); >+ >+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | >+ NTCREATEX_FLAGS_REQUEST_OPLOCK | >+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; >+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; >+ status = smb_raw_open(cli1->tree, tctx, &io); >+ CHECK_STATUS(tctx, status, NT_STATUS_OK); >+ fnum = io.ntcreatex.out.file.fnum; >+ CHECK_VAL(io.ntcreatex.out.create_action, FILE_WAS_CREATED); >+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); >+ >+ torture_comment(tctx, "Subsequent attributes open should not break\n"); >+ >+ ZERO_STRUCT(break_info); >+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); >+ >+ status = smb_raw_open(cli2->tree, tctx, &io); >+ CHECK_STATUS(tctx, status, NT_STATUS_OK); >+ fnum2 = io.ntcreatex.out.file.fnum; >+ torture_wait_for_oplock_break(tctx); >+ CHECK_VAL(break_info.count, 0); >+ CHECK_VAL(io.ntcreatex.out.create_action, FILE_WAS_OPENED); >+ CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); >+ smbcli_close(cli2->tree, fnum2); >+ >+ torture_comment(tctx, "Subsequent normal open should break oplock on attribute only open to level II\n"); >+ >+ ZERO_STRUCT(break_info); >+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); >+ >+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | >+ NTCREATEX_FLAGS_REQUEST_OPLOCK | >+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; >+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; >+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; >+ status = smb_raw_open(cli2->tree, tctx, &io); >+ CHECK_STATUS(tctx, status, NT_STATUS_OK); >+ fnum2 = io.ntcreatex.out.file.fnum; >+ torture_wait_for_oplock_break(tctx); >+ CHECK_VAL(break_info.count, 1); >+ CHECK_VAL(break_info.fnum, fnum); >+ CHECK_VAL(break_info.failures, 0); >+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); >+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); >+ smbcli_close(cli2->tree, fnum2); >+ >+ torture_comment(tctx, "third oplocked open should grant level2 without break\n"); >+ ZERO_STRUCT(break_info); >+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); >+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); >+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | >+ NTCREATEX_FLAGS_REQUEST_OPLOCK | >+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; >+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; >+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; >+ status = smb_raw_open(cli2->tree, tctx, &io); >+ CHECK_STATUS(tctx, status, NT_STATUS_OK); >+ fnum2 = io.ntcreatex.out.file.fnum; >+ torture_wait_for_oplock_break(tctx); >+ CHECK_VAL(break_info.count, 0); >+ CHECK_VAL(break_info.failures, 0); >+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); >+ >+ ZERO_STRUCT(break_info); >+ >+ torture_comment(tctx, "write should trigger a break to none on both\n"); >+ smbcli_write(cli2->tree, fnum2, 0, &c, 0, 1); >+ >+ /* We expect two breaks */ >+ torture_wait_for_oplock_break(tctx); >+ torture_wait_for_oplock_break(tctx); >+ >+ CHECK_VAL(break_info.count, 2); >+ CHECK_VAL(break_info.level, 0); >+ CHECK_VAL(break_info.failures, 0); >+ >+ smbcli_close(cli1->tree, fnum); >+ smbcli_close(cli2->tree, fnum2); >+ >+done: >+ smb_raw_exit(cli1->session); >+ smb_raw_exit(cli2->session); >+ smbcli_deltree(cli1->tree, BASEDIR); >+ return ret; >+} >+ > static bool test_raw_oplock_batch10(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) > { > const char *fname = BASEDIR "\\test_batch10.dat"; >@@ -4311,6 +4431,7 @@ struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx) > torture_suite_add_2smb_test(suite, "batch7", test_raw_oplock_batch7); > torture_suite_add_2smb_test(suite, "batch8", test_raw_oplock_batch8); > torture_suite_add_2smb_test(suite, "batch9", test_raw_oplock_batch9); >+ torture_suite_add_2smb_test(suite, "batch9a", test_raw_oplock_batch9a); > torture_suite_add_2smb_test(suite, "batch10", test_raw_oplock_batch10); > torture_suite_add_2smb_test(suite, "batch11", test_raw_oplock_batch11); > torture_suite_add_2smb_test(suite, "batch12", test_raw_oplock_batch12); >diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c >index d2a2832..be1c5eb 100644 >--- a/source4/torture/smb2/oplock.c >+++ b/source4/torture/smb2/oplock.c >@@ -1611,6 +1611,133 @@ static bool test_smb2_oplock_batch9(struct torture_context *tctx, > return ret; > } > >+static bool test_smb2_oplock_batch9a(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ const char *fname = BASEDIR "\\test_batch9a.dat"; >+ NTSTATUS status; >+ bool ret = true; >+ union smb_open io; >+ struct smb2_handle h, h1, h2, h3; >+ char c = 0; >+ >+ status = torture_smb2_testdir(tree1, BASEDIR, &h); >+ torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); >+ >+ /* cleanup */ >+ smb2_util_unlink(tree1, fname); >+ >+ tree1->session->transport->oplock.handler = torture_oplock_handler; >+ tree1->session->transport->oplock.private_data = tree1; >+ >+ /* >+ base ntcreatex parms >+ */ >+ ZERO_STRUCT(io.smb2); >+ io.generic.level = RAW_OPEN_SMB2; >+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; >+ io.smb2.in.alloc_size = 0; >+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; >+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; >+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; >+ io.smb2.in.create_options = 0; >+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; >+ io.smb2.in.security_flags = 0; >+ io.smb2.in.fname = fname; >+ >+ torture_comment(tctx, "BATCH9: open with attributes only can create " >+ "file\n"); >+ >+ io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; >+ io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; >+ io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | >+ SEC_FILE_WRITE_ATTRIBUTE | >+ SEC_STD_SYNCHRONIZE; >+ status = smb2_create(tree1, tctx, &(io.smb2)); >+ torture_assert_ntstatus_ok(tctx, status, "Error creating the file"); >+ h1 = io.smb2.out.file.handle; >+ CHECK_VAL(io.smb2.out.create_action, FILE_WAS_CREATED); >+ CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); >+ >+ torture_comment(tctx, "Subsequent attributes open should not break\n"); >+ >+ ZERO_STRUCT(break_info); >+ >+ status = smb2_create(tree2, tctx, &(io.smb2)); >+ torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); >+ h3 = io.smb2.out.file.handle; >+ torture_wait_for_oplock_break(tctx); >+ CHECK_VAL(break_info.count, 0); >+ CHECK_VAL(io.smb2.out.create_action, FILE_WAS_OPENED); >+ CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); >+ smb2_util_close(tree2, h3); >+ >+ torture_comment(tctx, "Subsequent normal open should break oplock on " >+ "attribute only open to level II\n"); >+ >+ ZERO_STRUCT(break_info); >+ >+ io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; >+ io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; >+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; >+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; >+ status = smb2_create(tree2, tctx, &(io.smb2)); >+ torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); >+ h2 = io.smb2.out.file.handle; >+ torture_wait_for_oplock_break(tctx); >+ CHECK_VAL(break_info.count, 1); >+ CHECK_VAL(break_info.handle.data[0], h1.data[0]); >+ CHECK_VAL(break_info.failures, 0); >+ CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); >+ CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); >+ smb2_util_close(tree2, h2); >+ >+ torture_comment(tctx, "third oplocked open should grant level2 without " >+ "break\n"); >+ ZERO_STRUCT(break_info); >+ >+ tree2->session->transport->oplock.handler = torture_oplock_handler; >+ tree2->session->transport->oplock.private_data = tree2; >+ >+ io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; >+ io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; >+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; >+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; >+ status = smb2_create(tree2, tctx, &(io.smb2)); >+ torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); >+ h2 = io.smb2.out.file.handle; >+ torture_wait_for_oplock_break(tctx); >+ CHECK_VAL(break_info.count, 0); >+ CHECK_VAL(break_info.failures, 0); >+ CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); >+ >+ ZERO_STRUCT(break_info); >+ >+ torture_comment(tctx, "write should trigger a break to none on both\n"); >+ tree1->session->transport->oplock.handler = >+ torture_oplock_handler_level2_to_none; >+ tree2->session->transport->oplock.handler = >+ torture_oplock_handler_level2_to_none; >+ smb2_util_write(tree2, h2, &c, 0, 1); >+ >+ /* We expect two breaks */ >+ torture_wait_for_oplock_break(tctx); >+ torture_wait_for_oplock_break(tctx); >+ >+ CHECK_VAL(break_info.count, 2); >+ CHECK_VAL(break_info.level, 0); >+ CHECK_VAL(break_info.failures, 0); >+ >+ smb2_util_close(tree1, h1); >+ smb2_util_close(tree2, h2); >+ smb2_util_close(tree1, h); >+ >+ smb2_deltree(tree1, BASEDIR); >+ return ret; >+} >+ >+ > static bool test_smb2_oplock_batch10(struct torture_context *tctx, > struct smb2_tree *tree1, > struct smb2_tree *tree2) >@@ -3836,6 +3963,7 @@ struct torture_suite *torture_smb2_oplocks_init(void) > torture_suite_add_2smb2_test(suite, "batch7", test_smb2_oplock_batch7); > torture_suite_add_2smb2_test(suite, "batch8", test_smb2_oplock_batch8); > torture_suite_add_2smb2_test(suite, "batch9", test_smb2_oplock_batch9); >+ torture_suite_add_2smb2_test(suite, "batch9a", test_smb2_oplock_batch9a); > torture_suite_add_2smb2_test(suite, "batch10", test_smb2_oplock_batch10); > torture_suite_add_2smb2_test(suite, "batch11", test_smb2_oplock_batch11); > torture_suite_add_2smb2_test(suite, "batch12", test_smb2_oplock_batch12); >-- >2.1.0.rc2.206.gedb03e5 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 10911
:
10394
|
10396
|
10397
|
10399
|
10400
|
10401
|
10406
|
10407
|
10411
|
10412
|
10415
|
10416
|
10417
|
10418
|
10421
|
10478
|
10479
|
10481
|
10484
|
10486
|
10488
|
10492
|
10493
|
10507
|
10546