From 80b96579036f39c9e3d5a42cefb5a7d30f07dfc0 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:56:03 -0800 Subject: [PATCH 1/5] s3:locking: pass servicename_new to leases_db_rename() Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 2cd9a5c3209e947a6e63ad019da869790d01d0aa) --- source3/locking/leases_db.c | 1 + source3/locking/leases_db.h | 1 + source3/locking/locking.c | 1 + 3 files changed, 3 insertions(+) diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index 7e000aa..4cb38f0 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -389,6 +389,7 @@ NTSTATUS leases_db_parse(const struct GUID *client_guid, NTSTATUS leases_db_rename(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, + const char *servicename_new, const char *filename_new, const char *stream_name_new) { diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index 906a99b..f398863 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -45,6 +45,7 @@ NTSTATUS leases_db_parse(const struct GUID *client_guid, NTSTATUS leases_db_rename(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, + const char *servicepath_new, const char *filename_new, const char *stream_name_new); #endif /* _LEASES_DB_H_ */ diff --git a/source3/locking/locking.c b/source3/locking/locking.c index dd73f68..221d6ee 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -575,6 +575,7 @@ bool rename_share_filename(struct messaging_context *msg_ctx, status = leases_db_rename(&l->client_guid, &l->lease_key, &id, + d->servicepath, d->base_name, d->stream_name); if (!NT_STATUS_IS_OK(status)) { -- 1.9.1 From 02da4d47b05e6b4fcd512d9f8d6bde1902829f2e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:47:52 -0800 Subject: [PATCH 2/5] s3:locking: prepare the data model for leases_db to cope with dynamic path renames. interface leases_db { typedef [public] struct { GUID client_guid; smb2_lease_key lease_key; } leases_db_key; typedef [public] struct { file_id id; [string,charset(UTF8)] char *servicepath; [string,charset(UTF8)] char *base_name; [string,charset(UTF8)] char *stream_name; } leases_db_file; typedef [public] struct { uint32 num_files; [size_is(num_files)] leases_db_file files[]; } leases_db_value; } As designed by metze. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 5661198d718496201ea2a6bb75d043a8b255b578) --- source3/librpc/idl/leases_db.idl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl index 2ab1591..bdb875d 100644 --- a/source3/librpc/idl/leases_db.idl +++ b/source3/librpc/idl/leases_db.idl @@ -15,6 +15,13 @@ interface leases_db } leases_db_key; typedef [public] struct { + file_id id; + [string,charset(UTF8)] char *servicepath; + [string,charset(UTF8)] char *base_name; + [string,charset(UTF8)] char *stream_name; + } leases_db_file; + + typedef [public] struct { uint32 num_file_ids; [size_is(num_file_ids)] file_id ids[]; [string,charset(UTF8)] char *filename; -- 1.9.1 From 2038bab26eaf848901bc74bdf7e248134f8834d4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:57:24 -0800 Subject: [PATCH 3/5] s3:locking: Add new utility function leases_db_copy_file_ids() Will be used by lease db parsers. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit b3b878eea4ecdd13828fc8f912ad95904e8a6c5e) --- source3/locking/leases_db.c | 20 ++++++++++++++++++++ source3/locking/leases_db.h | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index 4cb38f0..ed4f09a 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -408,3 +408,23 @@ NTSTATUS leases_db_rename(const struct GUID *client_guid, filename_new, stream_name_new); } + +NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx, + uint32_t num_files, + const struct leases_db_file *files, + struct file_id **pp_ids) +{ + uint32_t i; + struct file_id *ids = talloc_array(mem_ctx, + struct file_id, + num_files); + if (ids == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; i < num_files; i++) { + ids[i] = files[i].id; + } + *pp_ids = ids; + return NT_STATUS_OK; +} diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index f398863..0daa0ec 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -24,6 +24,7 @@ struct GUID; struct smb2_lease_key; struct file_id; +struct leases_db_file; bool leases_db_init(bool read_only); NTSTATUS leases_db_add(const struct GUID *client_guid, @@ -48,4 +49,8 @@ NTSTATUS leases_db_rename(const struct GUID *client_guid, const char *servicepath_new, const char *filename_new, const char *stream_name_new); +NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx, + uint32_t num_files, + const struct leases_db_file *files, + struct file_id **pp_ids); #endif /* _LEASES_DB_H_ */ -- 1.9.1 From f6d9c1589f0c4240bce7bc60abbddea1d246446a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:58:39 -0800 Subject: [PATCH 4/5] s3:locking: pass down servicepath to leases_db_add() Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 708f87b79dcdfc58e2219e90473160eb5a22ecb6) --- source3/locking/leases_db.c | 2 ++ source3/locking/leases_db.h | 1 + source3/smbd/open.c | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index ed4f09a..a9e2566 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -85,6 +85,7 @@ static bool leases_db_key(TALLOC_CTX *mem_ctx, NTSTATUS leases_db_add(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, + const char *servicepath, const char *filename, const char *stream_name) { @@ -405,6 +406,7 @@ NTSTATUS leases_db_rename(const struct GUID *client_guid, return leases_db_add(client_guid, lease_key, id, + servicename_new, filename_new, stream_name_new); } diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index 0daa0ec..20ec522 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -30,6 +30,7 @@ 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 *servicepath, const char *filename, const char *stream_name); NTSTATUS leases_db_del(const struct GUID *client_guid, diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 8f19a36..c1a8ee0 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1677,8 +1677,11 @@ static NTSTATUS grant_fsp_lease(struct files_struct *fsp, .epoch = fsp->lease->lease.lease_epoch, }; - status = leases_db_add(client_guid, &lease->lease_key, - &fsp->file_id, fsp->fsp_name->base_name, + status = leases_db_add(client_guid, + &lease->lease_key, + &fsp->file_id, + fsp->conn->connectpath, + 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__, -- 1.9.1 From fed7c2f1c00209466d7b413284ae6a7f378623f3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:47:52 -0800 Subject: [PATCH 5/5] s3:locking: Change the data model for leases_db to cope with dynamic path renames. interface leases_db { typedef [public] struct { GUID client_guid; smb2_lease_key lease_key; } leases_db_key; typedef [public] struct { file_id id; [string,charset(UTF8)] char *servicepath; [string,charset(UTF8)] char *base_name; [string,charset(UTF8)] char *stream_name; } leases_db_file; typedef [public] struct { uint32 num_files; [size_is(num_files)] leases_db_file files[]; } leases_db_value; } As designed by metze. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Tue Dec 9 03:44:04 CET 2014 on sn-devel-104 (cherry picked from commit 5ebb1903858b4d1aadfa4e04644ec1b2b218b914) --- source3/librpc/idl/leases_db.idl | 6 +- source3/locking/leases_db.c | 62 +++++++++-------- source3/locking/leases_db.h | 6 +- source3/smbd/open.c | 144 +++++++++++++++++++++++++++++++-------- source3/smbd/smb2_break.c | 16 ++--- 5 files changed, 161 insertions(+), 73 deletions(-) diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl index bdb875d..d021875 100644 --- a/source3/librpc/idl/leases_db.idl +++ b/source3/librpc/idl/leases_db.idl @@ -22,9 +22,7 @@ interface leases_db } leases_db_file; 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; + uint32 num_files; + [size_is(num_files)] leases_db_file files[]; } leases_db_value; } diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index a9e2566..0700ba9 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -86,7 +86,7 @@ NTSTATUS leases_db_add(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, const char *servicepath, - const char *filename, + const char *base_name, const char *stream_name) { TDB_DATA db_key, db_value; @@ -95,6 +95,7 @@ NTSTATUS leases_db_add(const struct GUID *client_guid, NTSTATUS status; bool ok; struct leases_db_value new_value; + struct leases_db_file new_file; struct leases_db_value *value = NULL; enum ndr_err_code ndr_err; @@ -140,31 +141,40 @@ NTSTATUS leases_db_add(const struct GUID *client_guid, } /* id must be unique. */ - for (i = 0; i < value->num_file_ids; i++) { - if (file_id_equal(id, &value->ids[i])) { + for (i = 0; i < value->num_files; i++) { + if (file_id_equal(id, &value->files[i].id)) { 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) { + value->files = talloc_realloc(value, value->files, + struct leases_db_file, + value->num_files + 1); + if (value->files == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } - value->ids[value->num_file_ids] = *id; - value->num_file_ids += 1; + value->files[value->num_files].id = *id; + value->files[value->num_files].servicepath = servicepath; + value->files[value->num_files].base_name = base_name; + value->files[value->num_files].stream_name = stream_name; + value->num_files += 1; } else { DEBUG(10, ("%s: new record\n", __func__)); - new_value = (struct leases_db_value) { - .num_file_ids = 1, - .ids = discard_const_p(struct file_id, id), - .filename = filename, + new_file = (struct leases_db_file) { + .id = *id, + .servicepath = servicepath, + .base_name = base_name, .stream_name = stream_name, }; + + new_value = (struct leases_db_value) { + .num_files = 1, + .files = &new_file, + }; value = &new_value; } @@ -253,21 +263,21 @@ NTSTATUS leases_db_del(const struct GUID *client_guid, } /* id must exist. */ - for (i = 0; i < value->num_file_ids; i++) { - if (file_id_equal(id, &value->ids[i])) { + for (i = 0; i < value->num_files; i++) { + if (file_id_equal(id, &value->files[i].id)) { break; } } - if (i == value->num_file_ids) { + if (i == value->num_files) { status = NT_STATUS_NOT_FOUND; goto out; } - value->ids[i] = value->ids[value->num_file_ids-1]; - value->num_file_ids -= 1; + value->files[i] = value->files[value->num_files-1]; + value->num_files -= 1; - if (value->num_file_ids == 0) { + if (value->num_files == 0) { DEBUG(10, ("%s: deleting record\n", __func__)); status = dbwrap_record_delete(rec); } else { @@ -304,9 +314,9 @@ NTSTATUS leases_db_del(const struct GUID *client_guid, } 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 (*parser)(uint32_t num_files, + const struct leases_db_file *files, + void *private_data); void *private_data; NTSTATUS status; }; @@ -341,8 +351,8 @@ static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data) NDR_PRINT_DEBUG(leases_db_value, value); } - state->parser(value->num_file_ids, - value->ids, value->filename, value->stream_name, + state->parser(value->num_files, + value->files, state->private_data); TALLOC_FREE(value); @@ -351,10 +361,8 @@ static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data) NTSTATUS leases_db_parse(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, - void (*parser)(uint32_t num_file_ids, - struct file_id *ids, - const char *filename, - const char *stream_name, + void (*parser)(uint32_t num_files, + const struct leases_db_file *files, void *private_data), void *private_data) { diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index 20ec522..383575a 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -38,10 +38,8 @@ NTSTATUS leases_db_del(const struct GUID *client_guid, 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 (*parser)(uint32_t num_files, + const struct leases_db_file *files, void *private_data), void *private_data); NTSTATUS leases_db_rename(const struct GUID *client_guid, diff --git a/source3/smbd/open.c b/source3/smbd/open.c index c1a8ee0..06770e0 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -36,6 +36,7 @@ #include "messages.h" #include "source3/lib/dbwrap/dbwrap_watch.h" #include "locking/leases_db.h" +#include "librpc/gen_ndr/ndr_leases_db.h" extern const struct generic_mapping file_generic_mapping; @@ -4127,8 +4128,10 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) * used for a different file name. */ -struct lease_fname_match_state { +struct lease_match_state { /* Input parameters. */ + TALLOC_CTX *mem_ctx; + const char *servicepath; const struct smb_filename *fname; bool file_existed; struct file_id id; @@ -4138,57 +4141,139 @@ struct lease_fname_match_state { NTSTATUS match_status; }; -static void lease_fname_match_parser( - uint32_t num_file_ids, - struct file_id *ids, const char *filename, const char *stream_name, +/************************************************************* + File doesn't exist but this lease key+guid is already in use. + + This is only allowable in the dynamic share case where the + service path must be different. + + There is a small race condition here in the multi-connection + case where a client sends two create calls on different connections, + where the file doesn't exist and one smbd creates the leases_db + entry first, but this will get fixed by the multichannel cleanup + when all identical client_guids get handled by a single smbd. +**************************************************************/ + +static void lease_match_parser_new_file( + uint32_t num_files, + const struct leases_db_file *files, + struct lease_match_state *state) +{ + uint32_t i; + + for (i = 0; i < num_files; i++) { + const struct leases_db_file *f = &files[i]; + if (strequal(state->servicepath, f->servicepath)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + return; + } + } + + /* Dynamic share case. Break leases on all other files. */ + state->match_status = leases_db_copy_file_ids(state->mem_ctx, + num_files, + files, + &state->ids); + if (!NT_STATUS_IS_OK(state->match_status)) { + return; + } + + state->num_file_ids = num_files; + state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; + return; +} + +static void lease_match_parser( + uint32_t num_files, + const struct leases_db_file *files, void *private_data) { - struct lease_fname_match_state *state = - (struct lease_fname_match_state *)private_data; + struct lease_match_state *state = + (struct lease_match_state *)private_data; + uint32_t i; - 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; + if (!state->file_existed) { + /* + * Deal with name mismatch or + * possible dynamic share case separately + * to make code clearer. + */ + lease_match_parser_new_file(num_files, + files, + state); return; } - if (state->file_existed && - 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; + /* File existed. */ + state->match_status = NT_STATUS_OK; + + for (i = 0; i < num_files; i++) { + const struct leases_db_file *f = &files[i]; + + /* Everything should be the same. */ + if (!file_id_equal(&state->id, &f->id)) { + /* This should catch all dynamic share cases. */ + state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; + break; + } + if (!strequal(f->servicepath, state->servicepath)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + break; + } + if (!strequal(f->base_name, state->fname->base_name)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + break; + } + if (!strequal(f->stream_name, state->fname->stream_name)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + break; + } + } + + if (NT_STATUS_IS_OK(state->match_status)) { + /* + * Common case - just opening another handle on a + * file on a non-dynamic share. + */ + return; + } + + if (NT_STATUS_EQUAL(state->match_status, NT_STATUS_INVALID_PARAMETER)) { + /* Mismatched path. Error back to client. */ return; } /* - * More than one file id, or not equal, or new file - * being created and there's already an existing lease - * on this (client_guid, lease id) pair. + * File id mismatch. Dynamic share case NT_STATUS_OPLOCK_NOT_GRANTED. * 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; + state->match_status = leases_db_copy_file_ids(state->mem_ctx, + num_files, + files, + &state->ids); + if (!NT_STATUS_IS_OK(state->match_status)) { + return; } + + state->num_file_ids = num_files; + state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; + return; } static NTSTATUS lease_match(connection_struct *conn, struct smb_request *req, struct smb2_lease_key *lease_key, + const char *servicepath, const struct smb_filename *fname, uint16_t *p_version, uint16_t *p_epoch) { struct smbd_server_connection *sconn = req->sconn; - struct lease_fname_match_state state = { + TALLOC_CTX *tos = talloc_tos(); + struct lease_match_state state = { + .mem_ctx = tos, + .servicepath = servicepath, .fname = fname, .match_status = NT_STATUS_OK }; @@ -4203,7 +4288,7 @@ static NTSTATUS lease_match(connection_struct *conn, } status = leases_db_parse(&sconn->client->connections->smb2.client.guid, - lease_key, lease_fname_match_parser, &state); + lease_key, lease_match_parser, &state); if (!NT_STATUS_IS_OK(status)) { /* * Not found or error means okay: We can make the lease pass @@ -4356,6 +4441,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, status = lease_match(conn, req, &lease->lease_key, + conn->connectpath, smb_fname, &version, &epoch); diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c index 126bf78..5eab0a1 100644 --- a/source3/smbd/smb2_break.c +++ b/source3/smbd/smb2_break.c @@ -340,21 +340,19 @@ struct lease_lookup_state { }; static void lease_parser( - uint32_t num_file_ids, - struct file_id *ids, const char *filename, const char *stream_name, + uint32_t num_files, + const struct leases_db_file *files, void *private_data) { struct lease_lookup_state *lls = (struct lease_lookup_state *)private_data; lls->status = NT_STATUS_OK; - lls->num_file_ids = num_file_ids; - lls->ids = talloc_memdup(lls->mem_ctx, - ids, - num_file_ids * sizeof(struct file_id)); - if (lls->ids == NULL) { - lls->status = NT_STATUS_NO_MEMORY; - } + lls->num_file_ids = num_files; + lls->status = leases_db_copy_file_ids(lls->mem_ctx, + num_files, + files, + &lls->ids); } static struct tevent_req *smbd_smb2_lease_break_send( -- 1.9.1