diff --git a/gfx/drivers_shader/glslang_util.cpp b/gfx/drivers_shader/glslang_util.cpp index f90c9f6e73..e9253f175d 100644 --- a/gfx/drivers_shader/glslang_util.cpp +++ b/gfx/drivers_shader/glslang_util.cpp @@ -53,7 +53,7 @@ bool glslang_read_shader_file(const char *path, vector *output, bool roo include_path[0] = tmp[0] = '\0'; strlcpy(tmp_path, path, path_size); - path_resolve_realpath(tmp_path, path_size); + path_resolve_realpath(tmp_path, path_size, false); if (!path_is_valid(tmp_path)) strlcpy(tmp_path, path, path_size); diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index 4b0bb2047f..9734835cc1 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -756,7 +756,7 @@ void video_shader_write_conf_preset(config_file_t *conf, strlcpy(tmp_base, preset_path, tmp_size); /* ensure we use a clean base like the shader passes and texture paths do */ - path_resolve_realpath(tmp_base, tmp_size); + path_resolve_realpath(tmp_base, tmp_size, false); path_basedir(tmp_base); } diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index 09cea5708e..f7d995f62f 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -756,35 +756,135 @@ bool path_is_absolute(const char *path) /** * path_resolve_realpath: - * @buf : buffer for path + * @buf : input and output buffer for path * @size : size of buffer + * @resolve_symlinks : whether to resolve symlinks or not * - * Turns relative paths into absolute paths and - * resolves use of "." and ".." in absolute paths. - * If relative, rebases on current working dir. + * Resolves use of ".", "..", multiple slashes etc in absolute paths. + * + * Relative paths are rebased on the current working dir. + * + * Returns: @buf if successful, NULL otherwise. + * Note: Not implemented on consoles + * Note: Symlinks are only resolved on Unix-likes + * Note: The current working dir might not be what you expect, + * e.g. on Android it is "/" + * Use of fill_pathname_resolve_relative() should be prefered **/ -void path_resolve_realpath(char *buf, size_t size) +char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks) { #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) char tmp[PATH_MAX_LENGTH]; - - tmp[0] = '\0'; - - strlcpy(tmp, buf, sizeof(tmp)); - #ifdef _WIN32 + strlcpy(tmp, buf, sizeof(tmp)); if (!_fullpath(buf, tmp, size)) + { strlcpy(buf, tmp, size); + return NULL; + } + return buf; #else + size_t t; + char *p; + const char *next; + const char *buf_end; - /* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf. - * Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways. - * POSIX 2008 can automatically allocate for you, - * but don't rely on that. */ - if (!realpath(tmp, buf)) - strlcpy(buf, tmp, size); + if (resolve_symlinks) + { + strlcpy(tmp, buf, sizeof(tmp)); + + /* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf. + * Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways. + * POSIX 2008 can automatically allocate for you, + * but don't rely on that. */ + if (!realpath(tmp, buf)) + { + strlcpy(buf, tmp, size); + return NULL; + } + + return buf; + } + + t = 0; /* length of output */ + buf_end = buf + strlen(buf); + + if (!path_is_absolute(buf)) + { + size_t len; + /* rebase on working directory */ + if (!getcwd(tmp, PATH_MAX_LENGTH-1)) + return NULL; + + len = strlen(tmp); + t += len; + + if (tmp[len-1] != '/') + tmp[t++] = '/'; + + if (string_is_empty(buf)) + goto end; + + p = buf; + } + else + { + /* UNIX paths can start with multiple '/', copy those */ + for (p = buf; *p == '/'; p++) + tmp[t++] = '/'; + } + + /* p points to just after a slash while 'next' points to the next slash + * if there are no slashes, they point relative to where one would be */ + do + { + next = strchr(p, '/'); + if (!next) + next = buf_end; + + if ((next - p == 2 && p[0] == '.' && p[1] == '.')) + { + p += 3; + + /* fail for illegal /.., //.. etc */ + if (t == 1 || tmp[t-2] == '/') + return NULL; + + /* delete previous segment in tmp by adjusting size t + * tmp[t-1] == '/', find '/' before that */ + t = t-2; + while (tmp[t] != '/') + t--; + t++; + } + else if (next - p == 1 && p[0] == '.') + { + p += 2; + } + else if (next - p == 0) + { + p += 1; + } + else + { + /* fail when truncating */ + if (t + next-p+1 > PATH_MAX_LENGTH-1) + return NULL; + + while (p <= next) + tmp[t++] = *p++; + } + + } + while (next < buf_end); + +end: + tmp[t] = '\0'; + strlcpy(buf, tmp, size); + return buf; #endif #endif + return NULL; } /** @@ -855,7 +955,7 @@ void fill_pathname_resolve_relative(char *out_path, fill_pathname_basedir(out_path, in_refpath, size); strlcat(out_path, in_path, size); - path_resolve_realpath(out_path, size); + path_resolve_realpath(out_path, size, false); } /** diff --git a/libretro-common/include/file/file_path.h b/libretro-common/include/file/file_path.h index 31b3ce8aef..8d365a9c8e 100644 --- a/libretro-common/include/file/file_path.h +++ b/libretro-common/include/file/file_path.h @@ -147,14 +147,22 @@ void path_parent_dir(char *path); /** * path_resolve_realpath: - * @buf : buffer for path + * @buf : input and output buffer for path * @size : size of buffer + * @resolve_symlinks : whether to resolve symlinks or not * - * Turns relative paths into absolute paths and - * resolves use of "." and ".." in absolute paths. - * If relative, rebases on current working dir. + * Resolves use of ".", "..", multiple slashes etc in absolute paths. + * + * Relative paths are rebased on the current working dir. + * + * Returns: @buf if successful, NULL otherwise. + * Note: Not implemented on consoles + * Note: Symlinks are only resolved on Unix-likes + * Note: The current working dir might not be what you expect, + * e.g. on Android it is "/" + * Use of fill_pathname_resolve_relative() should be prefered **/ -void path_resolve_realpath(char *buf, size_t size); +char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks); /** * path_relative_to: diff --git a/playlist.c b/playlist.c index c72e258453..ad8bce2960 100644 --- a/playlist.c +++ b/playlist.c @@ -104,7 +104,7 @@ static bool playlist_path_equal(const char *real_path, const char *entry_path) /* Get entry 'real' path */ strlcpy(entry_real_path, entry_path, sizeof(entry_real_path)); - path_resolve_realpath(entry_real_path, sizeof(entry_real_path)); + path_resolve_realpath(entry_real_path, sizeof(entry_real_path), true); if (string_is_empty(entry_real_path)) return false; @@ -193,7 +193,7 @@ static bool playlist_core_path_equal(const char *real_core_path, const char *ent /* Get entry 'real' core path */ strlcpy(entry_real_core_path, entry_core_path, sizeof(entry_real_core_path)); if (!string_is_equal(entry_real_core_path, file_path_str(FILE_PATH_DETECT))) - path_resolve_realpath(entry_real_core_path, sizeof(entry_real_core_path)); + path_resolve_realpath(entry_real_core_path, sizeof(entry_real_core_path), true); if (string_is_empty(entry_real_core_path)) return false; @@ -279,7 +279,7 @@ void playlist_get_index_by_path(playlist_t *playlist, /* Get 'real' search path */ strlcpy(real_search_path, search_path, sizeof(real_search_path)); - path_resolve_realpath(real_search_path, sizeof(real_search_path)); + path_resolve_realpath(real_search_path, sizeof(real_search_path), true); for (i = 0; i < playlist->size; i++) { @@ -306,7 +306,7 @@ bool playlist_entry_exists(playlist_t *playlist, /* Get 'real' search path */ strlcpy(real_search_path, path, sizeof(real_search_path)); - path_resolve_realpath(real_search_path, sizeof(real_search_path)); + path_resolve_realpath(real_search_path, sizeof(real_search_path), true); for (i = 0; i < playlist->size; i++) if (playlist_path_equal(real_search_path, playlist->entries[i].path)) @@ -539,13 +539,13 @@ bool playlist_push_runtime(playlist_t *playlist, if (!string_is_empty(entry->path)) { strlcpy(real_path, entry->path, sizeof(real_path)); - path_resolve_realpath(real_path, sizeof(real_path)); + path_resolve_realpath(real_path, sizeof(real_path), true); } /* Get 'real' core path */ strlcpy(real_core_path, entry->core_path, sizeof(real_core_path)); if (!string_is_equal(real_core_path, file_path_str(FILE_PATH_DETECT))) - path_resolve_realpath(real_core_path, sizeof(real_core_path)); + path_resolve_realpath(real_core_path, sizeof(real_core_path), true); if (string_is_empty(real_core_path)) { @@ -628,7 +628,7 @@ success: /** * playlist_resolve_path: * @mode : PLAYLIST_LOAD or PLAYLIST_SAVE - * @path : The path to be modified + * @path : The path to be modified * * Resolves the path of an item, such as the content path or path to the core, to a format * appropriate for saving or loading depending on the @mode parameter @@ -640,32 +640,26 @@ success: void playlist_resolve_path(enum playlist_file_mode mode, char *path, size_t size) { - char tmp[PATH_MAX_LENGTH]; - tmp[0] = '\0'; #ifdef HAVE_COCOATOUCH - char resolved_path[PATH_MAX_LENGTH] = {0}; - strlcpy(tmp, path, sizeof(tmp)); - if ( mode == PLAYLIST_LOAD ) - { - strlcpy(resolved_path, tmp, sizeof(resolved_path)); - fill_pathname_expand_special(tmp, resolved_path, sizeof(tmp)); - } - else - { - /* iOS needs to call realpath here since the call - * above fails due to possibly buffer related issues. */ - realpath(tmp, resolved_path); - fill_pathname_abbreviate_special(tmp, resolved_path, sizeof(tmp)); - } - strlcpy(path, tmp, size); - return; -#else - if ( mode == PLAYLIST_LOAD) - return; + char tmp[PATH_MAX_LENGTH]; - strlcpy(tmp, path, sizeof(tmp)); - path_resolve_realpath(tmp, sizeof(tmp)); - strlcpy(path, tmp, size); + if (mode == PLAYLIST_LOAD) + { + fill_pathname_expand_special(tmp, path, sizeof(tmp)); + strlcpy(path, tmp, size); + } + else + { + /* iOS needs to call realpath here since the call + * above fails due to possibly buffer related issues. */ + realpath(path, tmp); + fill_pathname_abbreviate_special(path, tmp, size); + } +#else + if (mode == PLAYLIST_LOAD) + return; + + path_resolve_realpath(path, size, true); #endif } @@ -787,7 +781,7 @@ bool playlist_push(playlist_t *playlist, if (!string_is_empty(entry->subsystem_roms->elems[j].data)) { strlcpy(real_rom_path, entry->subsystem_roms->elems[j].data, sizeof(real_rom_path)); - path_resolve_realpath(real_rom_path, sizeof(real_rom_path)); + path_resolve_realpath(real_rom_path, sizeof(real_rom_path), true); } if (!playlist_path_equal(real_rom_path, roms->elems[j].data)) diff --git a/retroarch.c b/retroarch.c index d3e494a390..0005b3ace2 100644 --- a/retroarch.c +++ b/retroarch.c @@ -7529,7 +7529,7 @@ static bool load_dynamic_core(const char *path, char *buf, size_t size) /* Need to use absolute path for this setting. It can be * saved to content history, and a relative path would * break in that scenario. */ - path_resolve_realpath(buf, size); + path_resolve_realpath(buf, size, true); if ((lib_handle = dylib_load(path))) return true; return false; diff --git a/tasks/task_content.c b/tasks/task_content.c index 5bd97ba14f..6d91017eff 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -1349,7 +1349,7 @@ static void task_push_to_history_list( /* Path can be relative here. * Ensure we're pushing absolute path. */ if (!launched_from_menu && !string_is_empty(tmp)) - path_resolve_realpath(tmp, tmp_size); + path_resolve_realpath(tmp, tmp_size, true); #ifdef HAVE_MENU /* Push quick menu onto menu stack */