Fix lstat() for broken junction points on Windows.

When using junction points to emulate symlinks on Windows, one edge case
was not handled correctly by commit c5cb8f3b: if a junction point is
broken (pointing to a non-existent path), we'd report ENOENT.  This
doesn't break any known use case, but was noticed while developing a
test suite for these functions and is fixed here for completeness.

Also add translation ERROR_CANT_RESOLVE_FILENAME -> ENOENT, as that is
one of the errors Windows can report for some kinds of broken paths.

Discussion: https://postgr.es/m/CA%2BhUKG%2BajSQ_8eu2AogTncOnZ5me2D-Cn66iN_-wZnRjLN%2Bicg%40mail.gmail.com
(cherry picked from commit 387803d81d6256fcb60b9192bb5b00042442b4e3)

Author: Thomas Munro <tmunro@postgresql.org>
Author: Alexandra Wang <alexandra.wang.oss@gmail.com>
This commit is contained in:
Thomas Munro 2022-10-25 15:20:00 +13:00 committed by Andrew Dunstan
parent 895f23d9e1
commit 8a5e4982f9
2 changed files with 28 additions and 5 deletions

View File

@ -164,6 +164,12 @@ static const struct
}, },
{ {
ERROR_DELETE_PENDING, ENOENT ERROR_DELETE_PENDING, ENOENT
},
{
ERROR_INVALID_NAME, ENOENT
},
{
ERROR_CANT_RESOLVE_FILENAME, ENOENT
} }
}; };

View File

@ -127,15 +127,30 @@ _pglstat64(const char *name, struct stat *buf)
hFile = pgwin32_open_handle(name, O_RDONLY, true); hFile = pgwin32_open_handle(name, O_RDONLY, true);
if (hFile == INVALID_HANDLE_VALUE) if (hFile == INVALID_HANDLE_VALUE)
{
if (errno == ENOENT)
{
/*
* If it's a junction point pointing to a non-existent path, we'll
* have ENOENT here (because pgwin32_open_handle does not use
* FILE_FLAG_OPEN_REPARSE_POINT). In that case, we'll try again
* with readlink() below, which will distinguish true ENOENT from
* pseudo-symlink.
*/
memset(buf, 0, sizeof(*buf));
ret = 0;
}
else
return -1; return -1;
}
else
ret = fileinfo_to_stat(hFile, buf); ret = fileinfo_to_stat(hFile, buf);
/* /*
* Junction points appear as directories to fileinfo_to_stat(), so we'll * Junction points appear as directories to fileinfo_to_stat(), so we'll
* need to do a bit more work to distinguish them. * need to do a bit more work to distinguish them.
*/ */
if (ret == 0 && S_ISDIR(buf->st_mode)) if ((ret == 0 && S_ISDIR(buf->st_mode)) || hFile == INVALID_HANDLE_VALUE)
{ {
char next[MAXPGPATH]; char next[MAXPGPATH];
ssize_t size; ssize_t size;
@ -171,9 +186,11 @@ _pglstat64(const char *name, struct stat *buf)
buf->st_mode &= ~S_IFDIR; buf->st_mode &= ~S_IFDIR;
buf->st_mode |= S_IFLNK; buf->st_mode |= S_IFLNK;
buf->st_size = size; buf->st_size = size;
ret = 0;
} }
} }
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile); CloseHandle(hFile);
return ret; return ret;
} }