From 42aa69b076cb338e20b5b4656771f1873e8930d8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 17 May 2024 18:39:01 +0800 Subject: fix(path): avoid chdir() when resolving path (#28799) Use uv_fs_realpath() instead. It seems that uv_fs_realpath() has some problems on non-Linux platforms: - macOS and other BSDs: this function will fail with UV_ELOOP if more than 32 symlinks are found while resolving the given path. This limit is hardcoded and cannot be sidestepped. - Windows: while this function works in the common case, there are a number of corner cases where it doesn't: - Paths in ramdisk volumes created by tools which sidestep the Volume Manager (such as ImDisk) cannot be resolved. - Inconsistent casing when using drive letters. - Resolved path bypasses subst'd drives. Ref: https://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_realpath I don't know if the old implementation that uses uv_chdir() and uv_cwd() also suffers from the same problems. - For the ELOOP case, chdir() seems to have the same limitations. - On Windows, Vim doesn't use anything like chdir() either. It uses _wfullpath(), while libuv uses GetFinalPathNameByHandleW(). --- src/nvim/path.c | 43 +++++++++++++------------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) (limited to 'src/nvim/path.c') diff --git a/src/nvim/path.c b/src/nvim/path.c index f7e8b2b65c..96330e000b 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -2273,13 +2273,20 @@ bool match_suffix(char *fname) /// @return `FAIL` for failure, `OK` for success. int path_full_dir_name(char *directory, char *buffer, size_t len) { - int SUCCESS = 0; - int retval = OK; - if (strlen(directory) == 0) { return os_dirname(buffer, len); } + if (os_realpath(directory, buffer, len) != NULL) { + return OK; + } + + // Path does not exist (yet). For a full path fail, will use the path as-is. + if (path_is_absolute(directory)) { + return FAIL; + } + // For a relative path use the current directory and append the file name. + char old_dir[MAXPATHL]; // Get current directory name. @@ -2287,36 +2294,12 @@ int path_full_dir_name(char *directory, char *buffer, size_t len) return FAIL; } - // We have to get back to the current dir at the end, check if that works. - if (os_chdir(old_dir) != SUCCESS) { + xstrlcpy(buffer, old_dir, len); + if (append_path(buffer, directory, len) == FAIL) { return FAIL; } - if (os_chdir(directory) != SUCCESS) { - // Path does not exist (yet). For a full path fail, - // will use the path as-is. For a relative path use - // the current directory and append the file name. - if (path_is_absolute(directory)) { - // Do not return immediately since we may be in the wrong directory. - retval = FAIL; - } else { - xstrlcpy(buffer, old_dir, len); - if (append_path(buffer, directory, len) == FAIL) { - retval = FAIL; - } - } - } else if (os_dirname(buffer, len) == FAIL) { - // Do not return immediately since we are in the wrong directory. - retval = FAIL; - } - - if (os_chdir(old_dir) != SUCCESS) { - // That shouldn't happen, since we've tested if it works. - retval = FAIL; - emsg(_(e_prev_dir)); - } - - return retval; + return OK; } // Append to_append to path with a slash in between. -- cgit