From bfdfde18e14e887f7a08d80330a0bc4a94b79f7b Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 1 Sep 2023 17:58:18 +0200 Subject: [PATCH] Fix invalid filedata passed to linked data for versioning. Code would not keep filedata for read libraries long enough to be able to pass along to the 'after_liblink' versioning code. Instead, it was passing the main file data, leading to all kind of potential breakage in versioning code, e.g. in case of checks on available DNA data. Fix #111776. --- source/blender/blenloader/intern/readfile.cc | 59 +++++++++++++++----- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 09206ab4d6c..96858982471 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -3226,6 +3226,8 @@ static void do_versions(FileData *fd, Library *lib, Main *main) static void do_versions_after_linking(FileData *fd, Main *main) { + BLI_assert(fd != nullptr); + CLOG_INFO(&LOG, 2, "Processing %s (%s), %d.%d", @@ -3738,7 +3740,9 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) blo_split_main(&mainlist, bfd->main); LISTBASE_FOREACH (Main *, mainvar, &mainlist) { BLI_assert(mainvar->versionfile != 0); - do_versions_after_linking(fd, mainvar); + do_versions_after_linking( + (mainvar->curlib && mainvar->curlib->filedata) ? mainvar->curlib->filedata : fd, + mainvar); } blo_join_main(&mainlist); @@ -3748,6 +3752,14 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) BKE_main_id_refcount_recompute(bfd->main, false); } + LISTBASE_FOREACH (Library *, lib, &bfd->main->libraries) { + /* Now we can clear this runtime library filedata, it is not needed anymore. */ + if (lib->filedata) { + blo_filedata_free(lib->filedata); + lib->filedata = nullptr; + } + } + if (bfd->main->is_read_invalid) { return bfd; } @@ -4269,6 +4281,9 @@ static Main *library_link_begin(Main *mainvar, /* which one do we need? */ mainl = blo_find_main(fd, filepath, BKE_main_blendfile_path(mainvar)); + if (mainl->curlib) { + mainl->curlib->filedata = fd; + } /* needed for do_version */ mainl->versionfile = short(fd->fileversion); @@ -4459,8 +4474,27 @@ void BLO_library_link_end(Main *mainl, BlendHandle **bh, const LibraryLink_Param if (!mainl->is_read_invalid) { library_link_end(mainl, &fd, params->flag); - *bh = reinterpret_cast(fd); } + + LISTBASE_FOREACH (Library *, lib, ¶ms->bmain->libraries) { + /* Now we can clear this runtime library filedata, it is not needed anymore. */ + if (lib->filedata == reinterpret_cast(*bh)) { + /* The filedata is owned and managed by caller code, only clear matching library ponter. */ + lib->filedata = nullptr; + } + else if (lib->filedata) { + /* In case other libraries had to be read as dependencies of the main linked one, they need + * to be cleared here. + * + * TODO: In the future, could be worth keeping them in case data are linked from several + * libraries at once? To avoid closing and re-opening the same file several times. Would need + * a global cleanup callback then once all linking is done, though. */ + blo_filedata_free(lib->filedata); + lib->filedata = nullptr; + } + } + + *bh = reinterpret_cast(fd); } void *BLO_library_read_struct(FileData *fd, BHead *bh, const char *blockname) @@ -4765,13 +4799,15 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) * do_versions multiple times, which would have bad consequences. */ split_main_newid(mainptr, main_newid); - /* File data can be zero with link/append. */ + /* `filedata` can be NULL when loading linked data from inexistant or invalid library + * reference. Or during linking/appending, when processing data from a library not involved + * in the current linking/appending operation. + * + * Skip versioning in these cases, since the only IDs here will be placeholders (missing + * lib), or already existing IDs (linking/appending). */ if (mainptr->curlib->filedata) { do_versions(mainptr->curlib->filedata, mainptr->curlib, main_newid); } - else { - do_versions(basefd, nullptr, main_newid); - } add_main_to_main(mainptr, main_newid); } @@ -4784,13 +4820,10 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) /* NOTE: No need to call #do_versions_after_linking() or #BKE_main_id_refcount_recompute() * here, as this function is only called for library 'subset' data handling, as part of * either full blendfile reading (#blo_read_file_internal()), or library-data linking - * (#library_link_end()). */ - - /* Free file data we no longer need. */ - if (mainptr->curlib->filedata) { - blo_filedata_free(mainptr->curlib->filedata); - } - mainptr->curlib->filedata = nullptr; + * (#library_link_end()). + * + * For this to work reliably, `mainptr->curlib->filedata` also needs to be freed after said + * versioning code has run. */ } BKE_main_free(main_newid); }