fsrvp: prune shadow copies if associated path doesn't exist
authorNoel Power <noel.power@suse.com>
Thu, 13 Nov 2014 11:13:35 +0000 (11:13 +0000)
committerJeremy Allison <jra@samba.org>
Tue, 31 Mar 2015 16:40:25 +0000 (18:40 +0200)
This patch implements some simple FSRVP server housekeeping. On startup
the server scans the cached entries, any entries where the underlying
system paths associated with shadow copies no longer exist are removed
from the cache and from the registry.

This behaviour is disabled by default, but can be enabled via the new
"fss: prune stale" smb.conf parameter.

Signed-off-by: Noel Power <noel.power@suse.com>
Signed-off-by: David Disseldorp <ddiss@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/rpc_server/fss/srv_fss_agent.c
source3/rpc_server/fss/srv_fss_agent.h
source3/rpc_server/fssd.c

index 67b12f9b8226ff2559466168938d72fbdf6055ac..46d9a01f6e440fc663a9b4c2b97999514ff05598 100644 (file)
@@ -131,6 +131,163 @@ static NTSTATUS fss_unc_parse(TALLOC_CTX *mem_ctx,
        return NT_STATUS_OK;
 }
 
+static NTSTATUS fss_vfs_conn_create(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct messaging_context *msg_ctx,
+                                   struct auth_session_info *session_info,
+                                   int snum,
+                                   struct connection_struct **conn_out);
+static void fss_vfs_conn_destroy(struct connection_struct *conn);
+
+/* test if system path exists */
+static bool snap_path_exists(TALLOC_CTX *ctx, struct messaging_context *msg_ctx,
+                            struct fss_sc *sc)
+{
+       SMB_STRUCT_STAT st;
+       struct connection_struct *conn = NULL;
+       struct smb_filename *smb_fname = NULL;
+       char *service = NULL;
+       char *share;
+       int snum;
+       int ret;
+       NTSTATUS status;
+       bool result = false;
+
+       ZERO_STRUCT(st);
+
+       if ((sc->smaps_count == 0) || (sc->sc_path == NULL)) {
+               goto out;
+       }
+
+       share = sc->smaps->share_name;
+       snum = find_service(ctx, share, &service);
+
+       if ((snum == -1) || (service == NULL)) {
+               goto out;
+       }
+
+       status = fss_vfs_conn_create(ctx, server_event_context(),
+                                    msg_ctx, NULL, snum, &conn);
+
+       if(!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+
+       smb_fname = synthetic_smb_fname(service, sc->sc_path, NULL, NULL);
+       if (smb_fname == NULL) {
+               goto out;
+       }
+
+       ret = SMB_VFS_STAT(conn, smb_fname);
+       if ((ret == -1) && (errno == ENOENT)) {
+               goto out;
+       }
+       result = true;
+out:
+       if (conn) {
+               fss_vfs_conn_destroy(conn);
+       }
+       TALLOC_FREE(service);
+       return result;
+}
+
+static NTSTATUS sc_smap_unexpose(struct messaging_context *msg_ctx,
+                                struct fss_sc_smap *sc_smap, bool delete_all);
+
+static NTSTATUS fss_prune_stale(struct messaging_context *msg_ctx,
+                               const char *db_path)
+{
+       struct fss_sc_set *sc_sets;
+       uint32_t sc_sets_count = 0;
+       struct fss_sc_set *sc_set;
+       struct fss_sc_smap *prunable_sc_smaps = NULL;
+       bool is_modified = false;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX *ctx = talloc_new(NULL);
+
+       if (!ctx) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* work with temporary state for simple cleanup on failure */
+       become_root();
+       status = fss_state_retrieve(ctx, &sc_sets, &sc_sets_count, db_path);
+       unbecome_root();
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("failed to retrieve fss server state: %s\n",
+                         nt_errstr(status)));
+               goto out;
+       }
+
+       /* walk the cache and pick up any entries to be deleted */
+       sc_set = sc_sets;
+       DEBUG(10, ("pruning shared shadow copies\n"));
+       while (sc_set) {
+               struct fss_sc *sc;
+               struct fss_sc_set *sc_set_next = sc_set->next;
+               char *set_id = GUID_string(ctx, &sc_set->id);
+               if (set_id == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+               DEBUGADD(10, ("\tprocessing shadow set id %s\n", set_id));
+               sc = sc_set->scs;
+               while (sc) {
+                       struct fss_sc_smap *sc_smap;
+                       struct fss_sc *sc_next = sc->next;
+                       DEBUGADD(10, ("\tprocessing shadow copy path %s\n",
+                                sc->sc_path));
+                       if (snap_path_exists(ctx, msg_ctx, sc)) {
+                               sc = sc_next;
+                               continue;
+                       }
+
+                       /* move missing snapshot state to purge list */
+                       sc_smap = sc->smaps;
+                       while (sc_smap != NULL) {
+                               struct fss_sc_smap *smap_next = sc_smap->next;
+                               DLIST_REMOVE(sc->smaps, sc_smap);
+                               DLIST_ADD_END(prunable_sc_smaps, sc_smap,
+                                             struct fss_sc_smap *);
+                               sc->smaps_count--;
+                               sc_smap = smap_next;
+                       }
+
+                       DLIST_REMOVE(sc_set->scs, sc);
+                       sc_set->scs_count--;
+                       is_modified = true;
+                       sc = sc_next;
+               }
+               if (sc_set->scs_count == 0) {
+                       DLIST_REMOVE(sc_sets, sc_set);
+                       sc_sets_count--;
+               }
+               sc_set = sc_set_next;
+       }
+
+       if (is_modified) {
+               /* unexpose all shares in a single transaction */
+               status = sc_smap_unexpose(msg_ctx, prunable_sc_smaps, true);
+               if (!NT_STATUS_IS_OK(status)) {
+                       /* exit without storing updated state */
+                       goto out;
+               }
+
+               become_root();
+               status = fss_state_store(ctx, sc_sets, sc_sets_count, db_path);
+               unbecome_root();
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("pruning failed to store fss server state: %s\n",
+                                 nt_errstr(status)));
+                       goto out;
+               }
+       }
+       status = NT_STATUS_OK;
+out:
+       TALLOC_FREE(ctx);
+       return status;
+}
+
 static NTSTATUS fss_vfs_conn_create(TALLOC_CTX *mem_ctx,
                                    struct tevent_context *ev,
                                    struct messaging_context *msg_ctx,
@@ -246,10 +403,9 @@ void srv_fssa_cleanup(void)
        ZERO_STRUCT(fss_global);
 }
 
-NTSTATUS srv_fssa_start(void)
+NTSTATUS srv_fssa_start(struct messaging_context *msg_ctx)
 {
        NTSTATUS status;
-
        fss_global.mem_ctx = talloc_named_const(NULL, 0,
                                                "parent fss rpc server ctx");
        if (fss_global.mem_ctx == NULL) {
@@ -268,6 +424,9 @@ NTSTATUS srv_fssa_start(void)
         * The server MUST populate the GlobalShadowCopySetTable with the
         * ShadowCopySet entries read from the configuration store.
         */
+       if (lp_parm_bool(GLOBAL_SECTION_SNUM, "fss", "prune stale", false)) {
+               fss_prune_stale(msg_ctx, fss_global.db_path);
+       }
        become_root();
        status = fss_state_retrieve(fss_global.mem_ctx, &fss_global.sc_sets,
                                    &fss_global.sc_sets_count,
@@ -1353,11 +1512,12 @@ uint32_t _fss_GetShareMapping(struct pipes_struct *p,
 }
 
 static NTSTATUS sc_smap_unexpose(struct messaging_context *msg_ctx,
-                                struct fss_sc_smap *sc_smap)
+                                struct fss_sc_smap *sc_smap, bool delete_all)
 {
        NTSTATUS ret;
        struct smbconf_ctx *conf_ctx;
        sbcErr cerr;
+       bool is_modified = false;
        TALLOC_CTX *tmp_ctx = talloc_new(sc_smap);
        if (tmp_ctx == NULL) {
                return NT_STATUS_NO_MEMORY;
@@ -1374,12 +1534,6 @@ static NTSTATUS sc_smap_unexpose(struct messaging_context *msg_ctx,
        /* registry IO must be done as root */
        become_root();
 
-       if (!smbconf_share_exists(conf_ctx, sc_smap->sc_share_name)) {
-               DEBUG(2, ("no such share: %s\n", sc_smap->sc_share_name));
-               ret = NT_STATUS_OK;
-               goto done;
-       }
-
        cerr = smbconf_transaction_start(conf_ctx);
        if (!SBC_ERROR_IS_OK(cerr)) {
                DEBUG(0, ("error starting transaction: %s\n",
@@ -1388,26 +1542,48 @@ static NTSTATUS sc_smap_unexpose(struct messaging_context *msg_ctx,
                goto err_conf;
        }
 
-       cerr = smbconf_delete_share(conf_ctx, sc_smap->sc_share_name);
-       if (!SBC_ERROR_IS_OK(cerr)) {
-               DEBUG(0, ("error deleting share: %s\n",
-                        sbcErrorString(cerr)));
-               ret = NT_STATUS_UNSUCCESSFUL;
-               goto err_cancel;
-       }
+       while (sc_smap) {
+               struct fss_sc_smap *sc_map_next = sc_smap->next;
+               if (!smbconf_share_exists(conf_ctx, sc_smap->sc_share_name)) {
+                       DEBUG(2, ("no such share: %s\n", sc_smap->sc_share_name));
+                       if (!delete_all) {
+                               ret = NT_STATUS_OK;
+                               goto err_cancel;
+                       }
+                       sc_smap = sc_map_next;
+                       continue;
+               }
 
-       cerr = smbconf_transaction_commit(conf_ctx);
-       if (!SBC_ERROR_IS_OK(cerr)) {
-               DEBUG(0, ("error committing transaction: %s\n",
-                         sbcErrorString(cerr)));
-               ret = NT_STATUS_UNSUCCESSFUL;
+               cerr = smbconf_delete_share(conf_ctx, sc_smap->sc_share_name);
+               if (!SBC_ERROR_IS_OK(cerr)) {
+                       DEBUG(0, ("error deleting share: %s\n",
+                                sbcErrorString(cerr)));
+                       ret = NT_STATUS_UNSUCCESSFUL;
+                       goto err_cancel;
+               }
+               is_modified = true;
+               sc_smap->is_exposed = false;
+               if (delete_all) {
+                       sc_smap = sc_map_next;
+               } else {
+                       sc_smap = NULL; /* only process single sc_map entry */
+               }
+       }
+       if (is_modified) {
+               cerr = smbconf_transaction_commit(conf_ctx);
+               if (!SBC_ERROR_IS_OK(cerr)) {
+                       DEBUG(0, ("error committing transaction: %s\n",
+                                 sbcErrorString(cerr)));
+                       ret = NT_STATUS_UNSUCCESSFUL;
+                       goto err_cancel;
+               }
+               message_send_all(msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
+       } else {
+               ret = NT_STATUS_OK;
                goto err_cancel;
        }
-       message_send_all(msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
-done:
-       sc_smap->is_exposed = false;
-
        ret = NT_STATUS_OK;
+
 err_conf:
        talloc_free(conf_ctx);
        unbecome_root();
@@ -1477,7 +1653,7 @@ uint32_t _fss_DeleteShareMapping(struct pipes_struct *p,
                goto err_tmp_free;
        }
 
-       status = sc_smap_unexpose(p->msg_ctx, sc_smap);
+       status = sc_smap_unexpose(p->msg_ctx, sc_smap, false);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0, ("failed to remove share %s: %s\n",
                          sc_smap->sc_share_name, nt_errstr(status)));
index 134b9c156af45c4935b01aa6a107f4daaa5fe944..f44a91545e4539e8b0eaffec7741fce35b2e83e2 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef _SRV_FSS_AGENT_H_
 #define _SRV_FSS_AGENT_H_
 
-NTSTATUS srv_fssa_start(void);
+NTSTATUS srv_fssa_start(struct messaging_context *msg_ctx);
 void srv_fssa_cleanup(void);
 
 #endif /*_SRV_FSS_AGENT_H_ */
index 96556996c44fbf6b657a0f5e8223eab8be2a7aba..eb9b7e6aad144e5259fb8e71da7d4e25330267a2 100644 (file)
@@ -135,7 +135,10 @@ static bool fss_shutdown_cb(void *ptr)
 static bool fss_init_cb(void *ptr)
 {
        NTSTATUS status;
-       status = srv_fssa_start();
+        struct messaging_context *msg_ctx;
+
+       msg_ctx = talloc_get_type_abort(ptr, struct messaging_context);
+       status = srv_fssa_start(msg_ctx);
        return NT_STATUS_IS_OK(status);
 }
 
@@ -150,7 +153,7 @@ void start_fssd(struct tevent_context *ev_ctx,
 
        fss_cb.init = fss_init_cb;
        fss_cb.shutdown = fss_shutdown_cb;
-       fss_cb.private_data = NULL;
+       fss_cb.private_data = msg_ctx;
 
        DEBUG(1, ("Forking File Server Shadow-copy Daemon\n"));