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().
This commit is contained in:
zeertzjq
2024-05-17 18:39:01 +08:00
committed by GitHub
parent 10f9173519
commit 42aa69b076
5 changed files with 72 additions and 63 deletions

View File

@@ -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.