s3:locking: add leases_db infrastructure
authorVolker Lendecke <vl@samba.org>
Fri, 10 Oct 2014 23:36:54 +0000 (16:36 -0700)
committerJeremy Allison <jra@samba.org>
Thu, 4 Dec 2014 04:45:09 +0000 (05:45 +0100)
Will enable us to solve the dynamic share path problem
with leases on [homes].

We're also able to give the correct error codes when a
lease key is re-used with a different file name.

Pair-Programmed-With: Jeremy Allison <jra@samba.org>

Signed-off-by: Volker Lendecke <vl@samba.org>
Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source3/librpc/idl/leases_db.idl [new file with mode: 0644]
source3/librpc/idl/wscript_build
source3/librpc/wscript_build
source3/locking/leases_db.c [new file with mode: 0644]
source3/locking/leases_db.h [new file with mode: 0644]
source3/smbd/server.c
source3/wscript_build

diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl
new file mode 100644 (file)
index 0000000..2ab1591
--- /dev/null
@@ -0,0 +1,23 @@
+#include "idl_types.h"
+
+import "misc.idl";
+import "smb2_lease_struct.idl";
+import "file_id.idl";
+
+[
+       pointer_default(unique)
+]
+interface leases_db
+{
+       typedef [public] struct {
+               GUID client_guid;
+               smb2_lease_key lease_key;
+       } leases_db_key;
+
+       typedef [public] struct {
+               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;
+}
index c38fe7bd72b5eea7e3a5fddca0aab0ebd29cc7f3..f9b1bd7d761018c893b108aa5e9f78274cef35b5 100644 (file)
@@ -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')
index 77ae048f3af50cef122e3a61508175ac5a1f0b13..38d9a81a55f200ffcb095ff70d05af2e8a0005b9 100644 (file)
@@ -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/leases_db.c b/source3/locking/leases_db.c
new file mode 100644 (file)
index 0000000..67c93ff
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+   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 {
+               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,
+                       .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;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10, ("%s:\n", __func__));
+               NDR_PRINT_DEBUG(leases_db_value, value);
+       }
+
+       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) {
+               DEBUG(10, ("%s: deleting record\n", __func__));
+               status = dbwrap_record_delete(rec);
+       } else {
+               DEBUG(10, ("%s: updating record\n", __func__));
+               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;
+               }
+
+               if (DEBUGLEVEL >= 10) {
+                       DEBUG(10, ("%s:\n", __func__));
+                       NDR_PRINT_DEBUG(leases_db_value, value);
+               }
+
+               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;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10, ("%s:\n", __func__));
+               NDR_PRINT_DEBUG(leases_db_value, value);
+       }
+
+       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 (file)
index 0000000..f570356
--- /dev/null
@@ -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_ */
index 05104daebc7c6eb71e7fe49efa74c862fa8a5b89..8207bf1676f822c8649f5882351c81e7c860be45 100644 (file)
@@ -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;
@@ -1452,6 +1453,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);
        }
index df503babc3d92d60288d74a30d86c3f87872cb7d..469424d136d84166a67aee3cfe4dd04723487e1a 100755 (executable)
@@ -610,6 +610,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    LIBAFS
                    RPC_SERVICE
                    NDR_SMBXSRV
+                   LEASES_DB
                    LIBASYS
                    sysquotas
                    ccan-hash
@@ -627,9 +628,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',