fsrvp: add rpc server state storage back-end
authorDavid Disseldorp <ddiss@samba.org>
Thu, 13 Sep 2012 12:51:27 +0000 (14:51 +0200)
committerDavid Disseldorp <ddiss@samba.org>
Mon, 15 Apr 2013 16:15:20 +0000 (18:15 +0200)
MS-FSRVP defines that the state of the rpc server must be persistent.
Use an fss_srv tdb to preserve such state.

source3/Makefile.in
source3/rpc_server/fss/srv_fss_private.h
source3/rpc_server/fss/srv_fss_state.c [new file with mode: 0644]
source3/rpc_server/wscript_build

index eabc71ca2c37cf92ab18860e4678b316fafa3522..70ee6da33a8ca0cf0493fa479b8c31db3bc7f34c 100644 (file)
@@ -793,7 +793,8 @@ RPC_EPMAPPER_OBJ = rpc_server/epmapper/srv_epmapper.o autoconf/librpc/gen_ndr/sr
 
 RPC_SERVER_REGISTER_OBJ = rpc_server/rpc_ep_register.o $(DCE_RPC_EP_OBJ)
 
-RPC_FSS_AGENT_OBJ = rpc_server/fss/srv_fss_agent.o autoconf/librpc/gen_ndr/srv_fsrvp.o
+RPC_FSS_AGENT_OBJ = rpc_server/fss/srv_fss_agent.o rpc_server/fss/srv_fss_state.o \
+                   autoconf/librpc/gen_ndr/srv_fsrvp.o
 
 RPC_SERVER_OBJ = $(RPC_LSARPC_OBJ) $(RPC_WINREG_OBJ) $(RPC_INITSHUTDOWN_OBJ) \
                 $(RPC_DSSETUP_OBJ) $(RPC_WKSSVC_OBJ) $(RPC_SVCCTL_OBJ) \
index f77a8e3c7eed9b936cdae930e2f99b0bc6fbb09e..7802b070a65d25ac3976611266e5baef3fe21fd2 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef _SRV_FSS_PRIVATE_H_
 #define _SRV_FSS_PRIVATE_H_
 
+#define FSS_DB_NAME "srv_fss.tdb"
+
 struct fss_sc_smap {
        struct fss_sc_smap *next, *prev;
        int snum;
@@ -75,4 +77,12 @@ struct fss_global {
        uint32_t sc_sets_count;
 };
 
+NTSTATUS fss_state_store(TALLOC_CTX *mem_ctx,
+                        struct fss_sc_set *sc_sets,
+                        uint32_t sc_sets_count);
+
+NTSTATUS fss_state_retrieve(TALLOC_CTX *mem_ctx,
+                           struct fss_sc_set **sc_sets,
+                           uint32_t *sc_sets_count);
+
 #endif /*_SRV_FSS_PRIVATE_H_ */
diff --git a/source3/rpc_server/fss/srv_fss_state.c b/source3/rpc_server/fss/srv_fss_state.c
new file mode 100644 (file)
index 0000000..32543a1
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * File Server Shadow-Copy service for the FSRVP pipe
+ *
+ * Copyright (C) David Disseldorp      2012
+ *
+ * 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 <fcntl.h>
+#include "source3/include/includes.h"
+#include "source3/include/util_tdb.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "librpc/ndr/libndr.h"
+#include "srv_fss_private.h"
+
+#define FSS_DB_KEY_VERSION "db_version"
+#define FSS_DB_KEY_CONTEXT "context"
+#define FSS_DB_KEY_SC_SET_COUNT "sc_set_count"
+#define FSS_DB_KEY_PFX_SC_SET "sc_set/"
+#define FSS_DB_KEY_PFX_SC "sc/"
+#define FSS_DB_KEY_PFX_SMAP "smap/"
+
+#define FSS_DB_FMT_SC_SET "Pddd"
+#define FSS_DB_FMT_SC "PPPBd"
+#define FSS_DB_FMT_SMAP "dPPPd"
+
+/* database format version, NOT the fsrvp rpc version */
+#define FSS_DB_VAL_VERSION 0
+
+static NTSTATUS fss_state_smap_store(TALLOC_CTX *mem_ctx,
+                                    struct db_context *db,
+                                    const char *sc_key_str,
+                                    struct fss_sc_smap *smap)
+{
+       NTSTATUS status;
+       size_t len;
+       TDB_DATA val;
+       const char *smap_key_str;
+
+       /* becomes sc_set/@sc_set_id/sc/@sc_id/smap/@sc_share_name */
+       smap_key_str = talloc_asprintf(mem_ctx, "%s/%s%s", sc_key_str,
+                                      FSS_DB_KEY_PFX_SMAP,
+                                      smap->sc_share_name);
+       if (smap_key_str == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* @smap->sc_share_comment may be null if not exposed */
+       len = tdb_pack(NULL, 0, FSS_DB_FMT_SMAP, smap->snum,
+                      smap->share_name, smap->sc_share_name,
+                      (smap->sc_share_comment ? smap->sc_share_comment : ""),
+                      (int)smap->is_exposed);
+
+       val.dptr = talloc_size(mem_ctx, len);
+       if (val.dptr == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       val.dsize = len;
+
+       tdb_pack(val.dptr, val.dsize, FSS_DB_FMT_SMAP, smap->snum,
+                smap->share_name, smap->sc_share_name,
+                (smap->sc_share_comment ? smap->sc_share_comment : ""),
+                (int)smap->is_exposed);
+
+       status = dbwrap_store(db, string_term_tdb_data(smap_key_str), val, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fss_state_sc_store(TALLOC_CTX *mem_ctx,
+                                  struct db_context *db,
+                                  const char *sc_set_key_str,
+                                  struct fss_sc *sc)
+{
+       NTSTATUS status;
+       size_t len;
+       TDB_DATA val;
+       const char *sc_key_str;
+       struct fss_sc_smap *smap;
+
+       /* becomes sc_set/@sc_set.id/sc/@sc_id */
+       sc_key_str = talloc_asprintf(mem_ctx, "%s/%s%s", sc_set_key_str,
+                                    FSS_DB_KEY_PFX_SC, sc->id_str);
+       if (sc_key_str == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* @sc->sc_path may be null if not committed, store empty str */
+       len = tdb_pack(NULL, 0, FSS_DB_FMT_SC, sc->id_str,
+                      sc->volume_name,
+                      (sc->sc_path ? sc->sc_path : ""),
+                      sizeof(sc->create_ts), &sc->create_ts,
+                      sc->smaps_count);
+
+       val.dptr = talloc_size(mem_ctx, len);
+       if (val.dptr == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       val.dsize = len;
+
+       tdb_pack(val.dptr, val.dsize, FSS_DB_FMT_SC, sc->id_str,
+                sc->volume_name,
+                (sc->sc_path ? sc->sc_path : ""),
+                sizeof(sc->create_ts), &sc->create_ts,
+                sc->smaps_count);
+
+       status = dbwrap_store(db, string_term_tdb_data(sc_key_str), val, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       for (smap = sc->smaps; smap; smap = smap->next) {
+               status = fss_state_smap_store(mem_ctx, db, sc_key_str, smap);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fss_state_sc_set_store(TALLOC_CTX *mem_ctx,
+                                      struct db_context *db,
+                                      struct fss_sc_set *sc_set)
+{
+       NTSTATUS status;
+       size_t len;
+       TDB_DATA val;
+       const char *sc_set_key_str;
+       struct fss_sc *sc;
+
+       sc_set_key_str = talloc_asprintf(mem_ctx, "%s%s",
+                                        FSS_DB_KEY_PFX_SC_SET,
+                                        sc_set->id_str);
+       if (sc_set_key_str == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       len = tdb_pack(NULL, 0, FSS_DB_FMT_SC_SET, sc_set->id_str,
+                      sc_set->state, sc_set->context, sc_set->scs_count);
+
+       val.dptr = talloc_size(mem_ctx, len);
+       if (val.dptr == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       val.dsize = len;
+
+       tdb_pack(val.dptr, val.dsize, FSS_DB_FMT_SC_SET, sc_set->id_str,
+                sc_set->state, sc_set->context, sc_set->scs_count);
+
+       status = dbwrap_store(db, string_term_tdb_data(sc_set_key_str), val, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       for (sc = sc_set->scs; sc; sc = sc->next) {
+               status = fss_state_sc_store(mem_ctx, db, sc_set_key_str, sc);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
+
+       return NT_STATUS_OK;
+}
+
+/*
+ * write out the current fsrvp server state to a TDB. This clears any content
+ * currently written to the TDB.
+ */
+NTSTATUS fss_state_store(TALLOC_CTX *mem_ctx,
+                        struct fss_sc_set *sc_sets,
+                        uint32_t sc_sets_count)
+{
+       TALLOC_CTX *tmp_ctx;
+       struct db_context *db;
+       NTSTATUS status;
+       int ret;
+       struct fss_sc_set *sc_set;
+       char *db_path;
+
+       tmp_ctx = talloc_new(mem_ctx);
+       if (tmp_ctx == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       db_path = lock_path(FSS_DB_NAME);
+       if (db_path == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_ctx_free;
+       }
+
+       db = db_open(tmp_ctx, db_path, 0, TDB_DEFAULT,  O_RDWR | O_CREAT,
+                    0600, DBWRAP_LOCK_ORDER_1);
+       talloc_free(db_path);
+       if (db == NULL) {
+               DEBUG(0, ("Failed to open fss state TDB\n"));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto err_ctx_free;
+       }
+
+       ret = dbwrap_wipe(db);
+       if (ret != 0) {
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_db_free;
+       }
+
+       status = dbwrap_store_int32_bystring(db, FSS_DB_KEY_VERSION,
+                                            FSS_DB_VAL_VERSION);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto err_db_free;
+       }
+
+       ret = dbwrap_transaction_start(db);
+       if (ret != 0) {
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_db_free;
+       }
+
+       status = dbwrap_store_int32_bystring(db, FSS_DB_KEY_SC_SET_COUNT,
+                                            sc_sets_count);
+       if (!NT_STATUS_IS_OK(status)) {
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_trans_cancel;
+       }
+
+       for (sc_set = sc_sets; sc_set; sc_set = sc_set->next) {
+               status = fss_state_sc_set_store(tmp_ctx, db, sc_set);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto err_trans_cancel;
+               }
+       }
+
+       ret = dbwrap_transaction_commit(db);
+       if (ret != 0) {
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_trans_cancel;
+       }
+
+       talloc_free(db);
+       talloc_free(tmp_ctx);
+       return NT_STATUS_OK;
+
+err_trans_cancel:
+       dbwrap_transaction_cancel(db);
+err_db_free:
+       talloc_free(db);
+err_ctx_free:
+       talloc_free(tmp_ctx);
+       return status;
+}
+
+static NTSTATUS fss_state_smap_retrieve(TALLOC_CTX *mem_ctx,
+                                       TDB_DATA *key,
+                                       TDB_DATA *val,
+                                       struct fss_sc_smap **smap_out)
+{
+       NTSTATUS status;
+       struct fss_sc_smap *smap;
+       int len;
+       char *share_name;
+       char *sc_share_name;
+       char *sc_share_comment;
+
+       smap = talloc_zero(mem_ctx, struct fss_sc_smap);
+       if (smap == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       len = tdb_unpack(val->dptr, val->dsize, FSS_DB_FMT_SMAP,
+                        &smap->snum, &share_name, &sc_share_name,
+                        &sc_share_comment, (int *)&smap->is_exposed);
+       if  (len < 0) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       smap->share_name = talloc_strdup(smap, share_name);
+       if (smap->share_name == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_unpack_free;
+       }
+
+       /* store the full path so that the heirarchy can be rebuilt */
+       smap->sc_share_name = talloc_strdup(smap, (char *)key->dptr);
+       if (smap->sc_share_name == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_unpack_free;
+       }
+
+       /* sc_share_comment may be empty, keep null in such a case */
+       if (strlen(sc_share_comment) > 0) {
+               smap->sc_share_comment = talloc_strdup(smap, sc_share_comment);
+               if (smap->sc_share_comment == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto err_unpack_free;
+               }
+       }
+
+       status = NT_STATUS_OK;
+       *smap_out = smap;
+
+err_unpack_free:
+       free(share_name);
+       free(sc_share_name);
+       free(sc_share_comment);
+       return status;
+}
+
+static NTSTATUS fss_state_sc_retrieve(TALLOC_CTX *mem_ctx,
+                                     TDB_DATA *key,
+                                     TDB_DATA *val,
+                                     struct fss_sc **sc_out)
+{
+       NTSTATUS status;
+       struct fss_sc *sc;
+       int len;
+       char *id_str;
+       char *volume_name;
+       char *sc_path;
+       int ts_size;
+       time_t *create_ts;
+
+       sc = talloc_zero(mem_ctx, struct fss_sc);
+       if (sc == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       len = tdb_unpack(val->dptr, val->dsize, FSS_DB_FMT_SC,
+                        &id_str, &volume_name, &sc_path,
+                        &ts_size, &create_ts, &sc->smaps_count);
+       if  (len < 0) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       SMB_ASSERT(ts_size == sizeof(*create_ts));
+
+       /* store the full path so that the heirarchy can be rebuilt */
+       sc->id_str = talloc_strdup(sc, (char *)key->dptr);
+       if (sc->id_str == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_unpack_free;
+       }
+
+       sc->volume_name = talloc_strdup(sc, volume_name);
+       if (sc->volume_name == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_unpack_free;
+       }
+
+       /* sc_path may be empty, keep null in such a case */
+       if (strlen(sc_path) > 0) {
+               sc->sc_path = talloc_strdup(sc, sc_path);
+               if (sc->sc_path == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto err_unpack_free;
+               }
+       }
+
+       sc->create_ts = *create_ts;
+       status = NT_STATUS_OK;
+       *sc_out = sc;
+
+err_unpack_free:
+       free(id_str);
+       free(volume_name);
+       free(sc_path);
+       free(create_ts);
+       return status;
+}
+
+static NTSTATUS fss_state_sc_set_retrieve(TALLOC_CTX *mem_ctx,
+                                         TDB_DATA *key,
+                                         TDB_DATA *val,
+                                         struct fss_sc_set **sc_set_out)
+{
+       NTSTATUS status;
+       struct fss_sc_set *sc_set;
+       int len;
+       char *id_str;
+
+       sc_set = talloc_zero(mem_ctx, struct fss_sc_set);
+       if (sc_set == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       len = tdb_unpack(val->dptr, val->dsize, FSS_DB_FMT_SC_SET,
+                        &id_str, (int *)&sc_set->state, &sc_set->context,
+                        &sc_set->scs_count);
+       if  (len < 0) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* store the full path so that the heirarchy can be rebuilt */
+       sc_set->id_str = talloc_strdup(sc_set, (char *)key->dptr);
+       if (sc_set->id_str == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_unpack_free;
+       }
+
+       status = NT_STATUS_OK;
+       *sc_set_out = sc_set;
+
+err_unpack_free:
+       free(id_str);
+       return status;
+}
+
+struct fss_traverse_state {
+       TALLOC_CTX *mem_ctx;
+       struct fss_sc_smap *smaps;
+       uint32_t smaps_count;
+       struct fss_sc *scs;
+       uint32_t scs_count;
+       struct fss_sc_set *sc_sets;
+       uint32_t sc_sets_count;
+};
+
+static int fss_state_retrieve_traverse(struct db_record *rec,
+                                      void *private_data)
+{
+       NTSTATUS status;
+       struct fss_traverse_state *trv_state
+                       = (struct fss_traverse_state *)private_data;
+       TDB_DATA key = dbwrap_record_get_key(rec);
+       TDB_DATA val = dbwrap_record_get_value(rec);
+
+       /* order of checking is important here */
+       if (strstr((char *)key.dptr, FSS_DB_KEY_PFX_SMAP) != NULL) {
+               struct fss_sc_smap *smap;
+               status = fss_state_smap_retrieve(trv_state->mem_ctx,
+                                                &key, &val, &smap);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return -1;
+               }
+               DLIST_ADD_END(trv_state->smaps, smap, struct fss_sc_smap *);
+               trv_state->smaps_count++;
+       } else if (strstr((char *)key.dptr, FSS_DB_KEY_PFX_SC) != NULL) {
+               struct fss_sc *sc;
+               status = fss_state_sc_retrieve(trv_state->mem_ctx,
+                                              &key, &val, &sc);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return -1;
+               }
+               DLIST_ADD_END(trv_state->scs, sc, struct fss_sc *);
+               trv_state->scs_count++;
+       } else if (strstr((char *)key.dptr, FSS_DB_KEY_PFX_SC_SET) != NULL) {
+               struct fss_sc_set *sc_set;
+               status = fss_state_sc_set_retrieve(trv_state->mem_ctx,
+                                                  &key, &val, &sc_set);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return -1;
+               }
+               DLIST_ADD_END(trv_state->sc_sets, sc_set, struct fss_sc_set *);
+               trv_state->sc_sets_count++;
+       } else {
+               /* global context and db vers */
+               DEBUG(4, ("Ignoring fss srv db entry with key %s", key.dptr));
+       }
+
+       return 0;
+}
+
+static bool fss_state_smap_is_child(struct fss_sc *sc,
+                                   struct fss_sc_smap *smap)
+{
+       return (strstr(smap->sc_share_name, sc->id_str) != NULL);
+}
+
+static NTSTATUS fss_state_hierarchize_smaps(struct fss_traverse_state *trv_state,
+                                           struct fss_sc *sc)
+{
+       struct fss_sc_smap *smap;
+       struct fss_sc_smap *smap_n;
+       uint32_t smaps_moved = 0;
+
+       for (smap = trv_state->smaps; smap; smap = smap_n) {
+               smap_n = smap->next;
+               if (!fss_state_smap_is_child(sc, smap))
+                       continue;
+
+               /* smap mem should be owned by parent sc */
+               talloc_steal(sc, smap);
+               DLIST_REMOVE(trv_state->smaps, smap);
+               trv_state->smaps_count--;
+               DLIST_ADD_END(sc->smaps, smap, struct fss_sc_smap *);
+               smaps_moved++;
+
+               /* last component of the tdb key path is the sc share name */
+               SMB_ASSERT(strrchr(smap->sc_share_name, '/') != NULL);
+               smap->sc_share_name = strrchr(smap->sc_share_name, '/') + 1;
+       }
+
+       if (sc->smaps_count != smaps_moved) {
+               DEBUG(0, ("Inconsistent smaps_count, expected %u, moved %u\n",
+                         sc->smaps_count, smaps_moved));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static bool fss_state_sc_is_child(struct fss_sc_set *sc_set,
+                                 struct fss_sc *sc)
+{
+       return (strstr(sc->id_str, sc_set->id_str) != NULL);
+}
+
+static NTSTATUS fss_state_hierarchize_scs(struct fss_traverse_state *trv_state,
+                                         struct fss_sc_set *sc_set)
+{
+       NTSTATUS status;
+       struct fss_sc *sc;
+       struct fss_sc *sc_n;
+       uint32_t scs_moved = 0;
+
+       for (sc = trv_state->scs; sc; sc = sc_n) {
+               sc_n = sc->next;
+               if (!fss_state_sc_is_child(sc_set, sc))
+                       continue;
+
+               /* sc mem should be owned by parent sc_set */
+               talloc_steal(sc_set, sc);
+               DLIST_REMOVE(trv_state->scs, sc);
+               trv_state->scs_count--;
+               DLIST_ADD_END(sc_set->scs, sc, struct fss_sc *);
+               scs_moved++;
+
+               sc->sc_set = sc_set;
+
+               /* last component of the tdb key path is the sc GUID str */
+               SMB_ASSERT(strrchr(sc->id_str, '/') != NULL);
+               sc->id_str = strrchr(sc->id_str, '/') + 1;
+
+               status = GUID_from_string(sc->id_str, &sc->id);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto err_out;
+               }
+
+               status = fss_state_hierarchize_smaps(trv_state, sc);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto err_out;
+               }
+       }
+
+       if (sc_set->scs_count != scs_moved) {
+               DEBUG(0, ("Inconsistent scs_count, expected %u, moved %u\n",
+                         sc_set->scs_count, scs_moved));
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_out;
+       }
+
+       return NT_STATUS_OK;
+
+err_out:
+       return status;
+}
+
+static NTSTATUS fss_state_hierarchize(struct fss_traverse_state *trv_state,
+                                     struct fss_sc_set **sc_sets,
+                                     uint32_t *sc_sets_count)
+{
+       NTSTATUS status;
+       struct fss_sc_set *sc_set;
+       struct fss_sc_set *sc_set_n;
+       uint32_t i = 0;
+
+       *sc_sets = NULL;
+       for (sc_set = trv_state->sc_sets; sc_set; sc_set = sc_set_n) {
+               sc_set_n = sc_set->next;
+               /* sc_set mem already owned by trv_state->mem_ctx */
+               DLIST_REMOVE(trv_state->sc_sets, sc_set);
+               trv_state->sc_sets_count--;
+               DLIST_ADD_END(*sc_sets, sc_set, struct fss_sc_set *);
+               i++;
+
+               /* last component of the tdb key path is the sc_set GUID str */
+               SMB_ASSERT(strrchr(sc_set->id_str, '/') != NULL);
+               sc_set->id_str = strrchr(sc_set->id_str, '/') + 1;
+
+               status = GUID_from_string(sc_set->id_str, &sc_set->id);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto err_out;
+               }
+
+               status = fss_state_hierarchize_scs(trv_state, sc_set);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto err_out;
+               }
+       }
+       *sc_sets_count = i;
+       return NT_STATUS_OK;
+
+err_out:
+       return status;
+}
+
+NTSTATUS fss_state_retrieve(TALLOC_CTX *mem_ctx,
+                           struct fss_sc_set **sc_sets,
+                           uint32_t *sc_sets_count)
+{
+       char *db_path;
+       struct db_context *db;
+       NTSTATUS status;
+       struct fss_traverse_state trv_state;
+       int err;
+       int rec_count;
+       int vers;
+       *sc_sets = NULL;
+       *sc_sets_count = 0;
+
+       memset(&trv_state, 0, sizeof(trv_state));
+       trv_state.mem_ctx = talloc_new(mem_ctx);
+       if (trv_state.mem_ctx == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_out;
+       }
+
+       db_path = lock_path(FSS_DB_NAME);
+       if (db_path == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err_ts_free;
+       }
+
+       db = db_open(trv_state.mem_ctx, db_path, 0, TDB_DEFAULT,
+                    O_RDONLY, 0600, DBWRAP_LOCK_ORDER_1);
+       err = errno;
+       talloc_free(db_path);
+       if ((db == NULL) && (err == ENOENT)) {
+               DEBUG(4, ("fss state TDB does not exist for retrieval\n"));
+               status = NT_STATUS_OK;
+               goto err_ts_free;
+       } else if (db == NULL) {
+               DEBUG(0, ("Failed to open fss state TDB: %s\n",
+                         strerror(err)));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto err_ts_free;
+       }
+
+       status = dbwrap_fetch_int32_bystring(db, FSS_DB_KEY_VERSION,
+                                            &vers);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("failed to fetch version from fss state tdb: %s\n",
+                         nt_errstr(status)));
+               goto err_db_free;
+       } else if (vers != FSS_DB_VAL_VERSION) {
+               DEBUG(0, ("Unsupported fss tdb version %d, expected %d\n",
+                         vers, FSS_DB_VAL_VERSION));
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_db_free;
+       }
+
+       status = dbwrap_traverse_read(db,
+                                     fss_state_retrieve_traverse,
+                                     &trv_state,
+                                     &rec_count);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto err_db_free;
+       }
+
+       status = fss_state_hierarchize(&trv_state, sc_sets, sc_sets_count);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("Failed to form fss state heirarchy\n"));
+               goto err_db_free;
+       }
+
+       /* check whether anything was left without a parent */
+       if (trv_state.sc_sets_count != 0) {
+               DEBUG(0, ("%d shadow copy set orphans in %s tdb\n",
+                         trv_state.sc_sets_count, FSS_DB_NAME));
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_db_free;
+       }
+       if (trv_state.scs_count != 0) {
+               DEBUG(0, ("%d shadow copy orphans in %s tdb\n",
+                         trv_state.scs_count, FSS_DB_NAME));
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_db_free;
+       }
+       if (trv_state.smaps_count != 0) {
+               DEBUG(0, ("%d share map orphans in %s tdb\n",
+                         trv_state.smaps_count, FSS_DB_NAME));
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto err_db_free;
+       }
+       talloc_free(db);
+
+       return NT_STATUS_OK;
+
+err_db_free:
+       talloc_free(db);
+err_ts_free:
+       talloc_free(trv_state.mem_ctx);
+err_out:
+       return status;
+}
+
index 1d09da72faf0603b034c14bd41577646b9ee8604..6a075765e837a310e3f316a08ddc1ca0ab98a3be 100755 (executable)
@@ -19,7 +19,7 @@ RPC_SPOOLSS_SRC = '''spoolss/srv_spoolss_nt.c ../../librpc/gen_ndr/srv_spoolss.c
 RPC_EVENTLOG_SRC = '''eventlog/srv_eventlog_nt.c eventlog/srv_eventlog_reg.c ../../librpc/gen_ndr/srv_eventlog.c'''
 RPC_RPCECHO_SRC = '''echo/srv_echo_nt.c ../../librpc/gen_ndr/srv_echo.c'''
 RPC_EPMAPPER_SRC = '''epmapper/srv_epmapper.c ../../librpc/gen_ndr/srv_epmapper.c'''
-RPC_FSS_AGENT_SRC = '''fss/srv_fss_agent.c ../../librpc/gen_ndr/srv_fsrvp.c'''
+RPC_FSS_AGENT_SRC = '''fss/srv_fss_agent.c fss/srv_fss_state.c ../../librpc/gen_ndr/srv_fsrvp.c'''
 
 bld.SAMBA3_SUBSYSTEM('rpc',
                     source='',