Fix T95480: Crash when relocating libraries in some cases.

While code deleting old (relocated to new ones) IDs would work fine in
typical cases, it would fail badly in others, when e.g. drivers would
create 'reversed' dependency from the obdata ID to the object ID.

This commit uses a less efficient, but much safer method. It also
ensures no relocated old IDs is left over in the file (previous version
could easily leave some old IDs from the old library until a full
save/reload cycle happened).
This commit is contained in:
Bastien Montagne 2022-04-29 16:52:59 +02:00
parent 4d464a946a
commit 3c7a6718dd
Notes: blender-bot 2023-02-14 06:47:29 +01:00
Referenced by commit f7ce20e340, Cleanup: Remove two files committed by mistake.
Referenced by issue #95480, Blender crashes with relocating libraries in some cases.
4 changed files with 235 additions and 6 deletions

View File

@ -197,6 +197,9 @@ void BKE_blendfile_link(struct BlendfileLinkAppendContext *lapp_context,
* - Add all IDs to search for to `lapp_context`.
* - Mark which libraries should be considered for each ID.
* - Call this function.
*
* NOTE: content of `lapp_context` after execution of that function should not be assumed valid
* anymore, and should immediately be freed.
*/
void BKE_blendfile_library_relocate(struct BlendfileLinkAppendContext *lapp_context,
struct ReportList *reports,

View File

@ -0,0 +1,85 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
#pragma once
/** \file
* \ingroup bke
*
* API to manage principle properties in data-blocks.
*
* Principle properties are properties that are defined as the ones most user will need to
* edit when using this data-block.
*
* They current main usage in is library overrides.
*
* \note `BKE_lib_` files are for operations over data-blocks themselves, although they might
* alter Main as well (when creating/renaming/deleting an ID e.g.).
*
* \section Function Names
*
* - `BKE_lib_principleprop_` should be used for function affecting a single ID.
* - `BKE_lib_principleprop_main_` should be used for function affecting the whole collection
* of IDs in a given Main data-base.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct ID;
struct IDPrincipleProperties;
struct IDPrincipleProperty;
struct PointerRNA;
struct PropertyRNA;
struct ReportList;
/**
* Initialize empty list of principle properties for \a id.
*/
struct IDPrincipleProperties *BKE_lib_principleprop_init(struct ID *id);
#if 0
/**
* Shallow or deep copy of a whole princple properties from \a src_id to \a dst_id.
*/
void BKE_lib_principleprop_copy(struct ID *dst_id, const struct ID *src_id, bool do_full_copy);
#endif
/**
* Clear any principle properties data from given \a override.
*/
void BKE_lib_principleprop_clear(struct IDPrincipleProperties *principle_props, bool do_id_user);
/**
* Free given \a principle_props.
*/
void BKE_lib_principleprop_free(struct IDPrincipleProperties **principle_props, bool do_id_user);
/**
* Find principle property from given RNA path, if it exists.
*/
struct IDPrincipleProperty *BKE_lib_principleprop_find(
struct IDPrincipleProperties *principle_props, const char *rna_path);
/**
* Find principle property from given RNA path, or create it if it does not exist.
*/
struct IDPrincipleProperty *BKE_lib_principleprop_get(
struct IDPrincipleProperties *principle_props, const char *rna_path, bool *r_created);
/**
* Remove and free given \a principle_prop from given ID \a principle_props.
*/
void BKE_lib_principleprop_delete(struct IDPrincipleProperties *principle_props,
struct IDPrincipleProperty *principle_prop);
/**
* Get the RNA-property matching the \a principle_prop principle property. Used for UI to query
* additional data about the principle property (e.g. UI name).
*
* \param idpoin: RNA Pointer of the ID.
* \param principle_prop: The principle property to find the matching RNA property for.
*/
bool BKE_lib_principleprop_rna_property_find(struct PointerRNA *idpoin,
const struct IDPrincipleProperty *principle_prop,
struct PointerRNA *r_principle_poin,
struct PropertyRNA **r_principle_prop);
#ifdef __cplusplus
}
#endif

View File

@ -1541,14 +1541,49 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
BKE_main_unlock(bmain);
for (item_idx = 0, itemlink = lapp_context->items.list; itemlink;
item_idx++, itemlink = itemlink->next) {
BlendfileLinkAppendContextItem *item = itemlink->link;
ID *old_id = item->userdata;
/* Delete all no more used old IDs. */
/* NOTE: While this looping over until we are sure we deleted everything is very far from
* efficient, doing otherwise would require a much more complex handling of indirectly linked IDs
* in steps above. Currently, in case of relocation, those are skipped in remapping phase, though
* in some cases (essentially internal links between IDs from the same library) remapping should
* happen. But getting this to work reliably would be very difficult, so since this is not a
* performance-critical code, better to go with the (relatively) simpler, brute-force approach
* here in 'removal of old IDs' step. */
bool keep_looping = true;
while (keep_looping) {
keep_looping = false;
if (old_id->us == 0) {
BKE_id_free(bmain, old_id);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
for (item_idx = 0, itemlink = lapp_context->items.list; itemlink;
item_idx++, itemlink = itemlink->next) {
BlendfileLinkAppendContextItem *item = itemlink->link;
ID *old_id = item->userdata;
if (old_id == NULL) {
continue;
}
if (GS(old_id->name) == ID_KE) {
/* Shape Keys are handled as part of their owning obdata (see below). This implies thar
* there is no way to know when the old pointer gets invalid, so just clear it immediately.
*/
item->userdata = NULL;
continue;
}
if (old_id->us == 0) {
old_id->tag |= LIB_TAG_DOIT;
item->userdata = NULL;
keep_looping = true;
Key *old_key = BKE_key_from_id(old_id);
if (old_key != NULL) {
old_key->id.tag |= LIB_TAG_DOIT;
}
}
}
BKE_id_multi_tagged_delete(bmain);
/* Should not be needed, all tagged IDs should have been deleted above, just 'in case'. */
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
/* Some datablocks can get reloaded/replaced 'silently' because they are not linkable

View File

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
/** \file
* \ingroup bke
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "CLG_log.h"
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
#include "BKE_lib_id.h"
#include "BKE_lib_principle_properties.h"
#include "BKE_report.h"
#include "BLO_readfile.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "RNA_types.h"
static CLG_LogRef LOG = {"bke.idprincipleprops"};
IDPrincipleProperties *BKE_lib_principleprop_init(ID *id)
{
BLI_assert(id->principle_properties == NULL);
/* Else, generate new empty override. */
id->principle_properties = MEM_callocN(sizeof(*id->principle_properties), __func__);
return id->principle_properties;
}
void BKE_lib_principleprop_clear(IDPrincipleProperties *principle_props, bool UNUSED(do_id_user))
{
LISTBASE_FOREACH_MUTABLE (IDPrincipleProperty *, pprop, &principle_props->properties) {
BLI_assert(pprop->rna_path != NULL);
MEM_freeN(pprop->rna_path);
MEM_freeN(pprop);
}
BLI_listbase_clear(&principle_props->properties);
principle_props->flag = 0;
}
void BKE_lib_principleprop_free(IDPrincipleProperties **principle_props, bool do_id_user)
{
BLI_assert(*principle_props != NULL);
BKE_lib_principleprop_clear(*principle_props, do_id_user);
MEM_freeN(*principle_props);
*principle_props = NULL;
}
IDPrincipleProperty *BKE_lib_principleprop_find(IDPrincipleProperties *principle_props,
const char *rna_path)
{
return BLI_findstring_ptr(
&principle_props->properties, rna_path, offsetof(IDPrincipleProperty, rna_path));
}
IDPrincipleProperty *BKE_lib_principleprop_get(IDPrincipleProperties *principle_props,
const char *rna_path,
bool *r_created)
{
IDPrincipleProperty *pprop = BKE_lib_principleprop_find(principle_props, rna_path);
if (pprop == NULL) {
pprop = MEM_callocN(sizeof(*pprop), __func__);
pprop->rna_path = BLI_strdup(rna_path);
BLI_addtail(&principle_props->properties, pprop);
if (r_created) {
*r_created = true;
}
}
else if (r_created) {
*r_created = false;
}
return pprop;
}
void BKE_lib_principleprop_delete(IDPrincipleProperties *principle_props,
IDPrincipleProperty *principle_prop)
{
BLI_remlink(&principle_props->properties, principle_prop);
}
bool BKE_lib_principleprop_rna_property_find(struct PointerRNA *idpoin,
const struct IDPrincipleProperty *principle_prop,
struct PointerRNA *r_principle_poin,
struct PropertyRNA **r_principle_prop)
{
BLI_assert(RNA_struct_is_ID(idpoin->type));
return RNA_path_resolve_property(
idpoin, principle_prop->rna_path, r_principle_poin, r_principle_prop);
}