Add debug tool to check validity of current file *before* saving.
authorBastien Montagne <montagne29@wanadoo.fr>
Wed, 18 Apr 2018 16:22:18 +0000 (18:22 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Fri, 20 Apr 2018 08:46:59 +0000 (10:46 +0200)
It is hidden behind the --debug-io flag for now.

Idea is to try to catch broken libraries state in current Main before we
actually write the file on disk, should help catching and understanding
what happens in Spring corruption cases.

source/blender/blenloader/BLO_runtime.h
source/blender/blenloader/CMakeLists.txt
source/blender/blenloader/intern/blend_validate.c [new file with mode: 0644]
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c

index 0a5a7d3a660aa5d13f37b926a39a5e04001cee57..c119ea95f66526d1fb49b5228428425a9570cf5e 100644 (file)
@@ -38,11 +38,14 @@ extern "C" {
 #endif
 
 struct BlendFileData;
+struct Main;
 struct ReportList;
 
 int BLO_is_a_runtime(const char *file);
 struct BlendFileData *BLO_read_runtime(const char *file, struct ReportList *reports);
 
+bool BLO_main_validate_libraries(struct Main *bmain, struct ReportList *reports);
+
 #ifdef __cplusplus
 }
 #endif
index 3d2e8a306dea8738c4c10f2b486a0211cb3baba7..420f651ee7ffce118bd240761452041632062f94 100644 (file)
@@ -46,6 +46,7 @@ set(INC_SYS
 )
 
 set(SRC
+       intern/blend_validate.c
        intern/readblenentry.c
        intern/readfile.c
        intern/runtime.c
diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.c
new file mode 100644 (file)
index 0000000..d44497e
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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 2
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Bastien Montagne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file blender/blenloader/intern/blend_validate.c
+ *  \ingroup blenloader
+ *
+ * Utils to check/validate a Main is in sane state, only checks relations between datablocks and libraries for now.
+ *
+ * \note Does not *fix* anything, only reports found errors.
+ *
+ */
+
+#include <string.h> // for strrchr strncmp strstr
+
+#include "BLI_utildefines.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_linklist.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_sdna_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+
+#include "BLO_readfile.h"
+#include "BLO_runtime.h"
+
+#include "readfile.h"
+
+/* Does not fix anything, but checks that all linked data-blocks are still valid (i.e. pointing to the right library). */
+bool BLO_main_validate_libraries(struct Main *bmain, struct ReportList *reports)
+{
+       ListBase mainlist;
+       bool is_valid = true;
+
+       BKE_main_lock(bmain);
+
+       blo_split_main(&mainlist, bmain);
+
+       ListBase *lbarray[MAX_LIBARRAY];
+       int i = set_listbasepointers(bmain, lbarray);
+       while (i--) {
+               for (ID *id = lbarray[i]->first; id != NULL; id = id->next) {
+                       if (id->lib != NULL) {
+                               is_valid = false;
+                               BKE_reportf(reports, RPT_ERROR,
+                                           "ID %s is in local database while being linked from library %s!\n", id->name, id->lib->name);
+                       }
+               }
+       }
+
+       for (Main *curmain = bmain->next; curmain != NULL; curmain = curmain->next) {
+               Library *curlib = curmain->curlib;
+               if (curlib == NULL) {
+                       BKE_reportf(reports, RPT_ERROR,
+                                   "Library database with NULL library datablock!\n");
+                       continue;
+               }
+
+               BlendHandle *bh = BLO_blendhandle_from_file(curlib->name, reports);
+
+               if (bh == NULL) {
+                       continue;
+               }
+
+               i = set_listbasepointers(curmain, lbarray);
+               while (i--) {
+                       ID *id = lbarray[i]->first;
+                       if (id == NULL) {
+                               continue;
+                       }
+
+                       if (GS(id->name) == ID_LI) {
+                               is_valid = false;
+                               BKE_reportf(reports, RPT_ERROR,
+                                           "Library ID %s in library %s, this should not happen!\n", id->name, curlib->name);
+                               continue;
+                       }
+
+                       int totnames = 0;
+                       LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), &totnames);
+                       for (; id != NULL; id = id->next) {
+                               if (id->lib == NULL) {
+                                       is_valid = false;
+                                       BKE_reportf(reports, RPT_ERROR,
+                                                   "ID %s has NULL lib pointer while being in library %s!\n", id->name, curlib->name);
+                                       continue;
+                               }
+                               if (id->lib != curlib) {
+                                       is_valid = false;
+                                       BKE_reportf(reports, RPT_ERROR,
+                                                   "ID %s has mismatched lib pointer!\n", id->name);
+                                       continue;
+                               }
+
+                               LinkNode *name = names;
+                               for (; name; name = name->next) {
+                                       char *str_name = (char *)name->link;
+                                       if (id->name[2] == str_name[0] && STREQ(str_name, id->name + 2)) {
+                                               break;
+                                       }
+                               }
+
+                               if (name == NULL) {
+                                       is_valid = false;
+                                       BKE_reportf(reports, RPT_ERROR,
+                                                   "ID %s not found in library %s anymore!\n", id->name, id->lib->name);
+                                       continue;
+                               }
+                       }
+
+                       BLI_linklist_free(names, free);
+               }
+
+               BLO_blendhandle_close(bh);
+       }
+
+       blo_join_main(&mainlist);
+
+       BLI_assert(BLI_listbase_is_single(&mainlist));
+       BLI_assert(mainlist.first == (void *)bmain);
+
+       BKE_main_unlock(bmain);
+
+       return is_valid;
+}
index 1f1dc8017beb407332aa23565fd33d3758c64fc4..7d73b3050a61d38d07f3c0e6241c5e2bfcbc2eba 100644 (file)
@@ -552,6 +552,10 @@ void blo_split_main(ListBase *mainlist, Main *main)
        ListBase *lbarray[MAX_LIBARRAY];
        i = set_listbasepointers(main, lbarray);
        while (i--) {
+               ID *id = lbarray[i]->first;
+               if (id == NULL || GS(id->name) == ID_LI) {
+                       continue;  /* no ID_LI datablock should ever be linked anyway, but just in case, better be explicit. */
+               }
                split_libdata(lbarray[i], lib_main_array, lib_main_array_len);
        }
 
index 9727f6f0011206b219e54a81a4dae04aa7c17d0f..549800ae02b692cc10ef86361fe80ded7e769165 100644 (file)
 
 #include "BLO_writefile.h"
 #include "BLO_readfile.h"
+#include "BLO_runtime.h"
 #include "BLO_undofile.h"
 #include "BLO_blend_defs.h"
 
@@ -4094,6 +4095,11 @@ bool BLO_write_file(
        void     *path_list_backup = NULL;
        const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE);
 
+       if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
+               BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* save to disk.");
+               BLO_main_validate_libraries(mainvar, reports);
+       }
+
        /* open temporary file, so we preserve the original in case we crash */
        BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath);
 
@@ -4178,6 +4184,11 @@ bool BLO_write_file(
                return 0;
        }
 
+       if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
+               BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *AFTER* save to disk.");
+               BLO_main_validate_libraries(mainvar, reports);
+       }
+
        return 1;
 }