From 8dbd406ea0f495b3d404a7433a32b4781953e55a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 16 Dec 2021 16:17:28 +1100 Subject: WM: various changes to file writing behavior Saving with only a filename (from Python) wasn't being prevented, while it would successfully write the file to the working-directory, path remapping and setting relative paths wouldn't work afterwards as `Main.filepath` would have no directory component. Disallow this since it's a corner case which only ever occurs when path names without any directories are used from Python, the overhead of expanding the working-directory for all data saving operations isn't worthwhile. The following changes have been made: - bpy.ops.wm.save_mainfile() without a filepath argument fails & reports and error when the file hasn't been saved. Previously it would write to "untitled.blend" and set the `G.main->filepath` to this as well. - bpy.ops.wm.save_mainfile(filepath="untitled.blend") fails & reports and error as the filename has no directory component. - `BLI_path_is_abs_from_cwd` was added to check if the path would attempt to expand to the CWD. --- source/blender/blenlib/BLI_path_util.h | 8 ++++++- source/blender/blenlib/intern/path_util.c | 30 +++++++++++++++----------- source/blender/blenloader/intern/writefile.c | 1 + source/blender/windowmanager/intern/wm_files.c | 26 +++++++++++++++++++--- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 9e2970f6013..3cffecae187 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -323,9 +323,15 @@ void BLI_path_frame_strip(char *path, char *ext) ATTR_NONNULL(); * Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range */ bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(); +/** + * Checks for a relative path (ignoring Blender's "//") prefix + * (unlike `!BLI_path_is_rel(path)`). + * When false, #BLI_path_abs_from_cwd would expand the absolute path. + */ +bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(); /** * Checks for relative path, expanding them relative to the current working directory. - * Returns true if the expansion was performed. + * \returns true if the expansion was performed. * * \note Should only be called with command line paths. * This is _not_ something Blender's internal paths support, instead they use the "//" prefix. diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index c40eed711f5..de71188abfe 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1002,25 +1002,30 @@ bool BLI_path_abs(char *path, const char *basepath) return wasrelative; } -bool BLI_path_abs_from_cwd(char *path, const size_t maxlen) +bool BLI_path_is_abs_from_cwd(const char *path) { -#ifdef DEBUG_STRSIZE - memset(path, 0xff, sizeof(*path) * maxlen); -#endif - bool wasrelative = true; - const int filelen = strlen(path); + bool is_abs = false; + const int path_len_clamp = BLI_strnlen(path, 3); #ifdef WIN32 - if ((filelen >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) { - wasrelative = false; + if ((ppath_len_clamp >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) { + is_abs = true; } #else - if (filelen >= 2 && path[0] == '/') { - wasrelative = false; + if (path_len_clamp >= 2 && path[0] == '/') { + is_abs = true; } #endif + return is_abs; +} - if (wasrelative) { +bool BLI_path_abs_from_cwd(char *path, const size_t maxlen) +{ +#ifdef DEBUG_STRSIZE + memset(path, 0xff, sizeof(*path) * maxlen); +#endif + + if (!BLI_path_is_abs_from_cwd(path)) { char cwd[FILE_MAX]; /* in case the full path to the blend isn't used */ if (BLI_current_working_dir(cwd, sizeof(cwd))) { @@ -1031,9 +1036,10 @@ bool BLI_path_abs_from_cwd(char *path, const size_t maxlen) else { printf("Could not get the current working directory - $PWD for an unknown reason.\n"); } + return true; } - return wasrelative; + return false; } #ifdef _WIN32 diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index e0ff70d4b49..aa3eef4b475 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1319,6 +1319,7 @@ bool BLO_write_file(Main *mainvar, ReportList *reports) { BLI_assert(!BLI_path_is_rel(filepath)); + BLI_assert(BLI_path_is_abs_from_cwd(filepath)); char tempname[FILE_MAX + 1]; WriteWrap ww; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 7ac9395e16c..1d3fa158ac1 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -3067,12 +3067,32 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) BLO_WRITE_PATH_REMAP_NONE; save_set_compress(op); - if (RNA_struct_property_is_set(op->ptr, "filepath")) { + const bool is_filepath_set = RNA_struct_property_is_set(op->ptr, "filepath"); + if (is_filepath_set) { RNA_string_get(op->ptr, "filepath", path); } else { - BLI_strncpy(path, BKE_main_blendfile_path(bmain), FILE_MAX); - wm_filepath_default(bmain, path); + STRNCPY(path, BKE_main_blendfile_path(bmain)); + } + + if (path[0] == '\0') { + BKE_report(op->reports, + RPT_ERROR, + "Unable to save an unsaved file with an empty or unset \"filepath\" property"); + return OPERATOR_CANCELLED; + } + + /* NOTE(@campbellbarton): only check this for file-path properties so saving an already + * saved file never fails with an error. + * Even though this should never happen, there may be some corner case where a malformed + * path is stored in `G.main->filepath`: when the file path is initialized from recovering + * a blend file - for example, so in this case failing to save isn't ideal. */ + if (is_filepath_set && !BLI_path_is_abs_from_cwd(path)) { + BKE_reportf(op->reports, + RPT_ERROR, + "The \"filepath\" property was not an absolute path: \"%s\"", + path); + return OPERATOR_CANCELLED; } const int fileflags_orig = G.fileflags; -- cgit v1.2.3