v2-0006-Fix-lstat-on-broken-junction-points.patch
text/x-patch
Filename: v2-0006-Fix-lstat-on-broken-junction-points.patch
Type: text/x-patch
Part: 5
Patch
Format: format-patch
Series: patch v2-0006
Subject: Fix lstat() on broken junction points.
| File | + | − |
|---|---|---|
| src/port/t/001_filesystem.c | 15 | 8 |
| src/port/win32error.c | 3 | 0 |
| src/port/win32stat.c | 22 | 5 |
From 46ffa9002693ad468f3c569cf3cb2a891de308ae Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 16 Oct 2022 12:07:33 +1300
Subject: [PATCH v2 06/10] Fix lstat() on broken junction points.
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 testing and is
fixed here for completeness.
Also add translation ERROR_CANT_RESOLVE_FILENAME -> ENOENT, as that is
one of the errors Windows can report depending on format of the broken
path.
---
src/port/t/001_filesystem.c | 23 +++++++++++++++--------
src/port/win32error.c | 3 +++
src/port/win32stat.c | 27 ++++++++++++++++++++++-----
3 files changed, 40 insertions(+), 13 deletions(-)
diff --git a/src/port/t/001_filesystem.c b/src/port/t/001_filesystem.c
index 137af164ed..bb1c681366 100644
--- a/src/port/t/001_filesystem.c
+++ b/src/port/t/001_filesystem.c
@@ -277,6 +277,13 @@ filesystem_metadata_tests(void)
PG_EXPECT(S_ISLNK(statbuf.st_mode));
PG_EXPECT_EQ(statbuf.st_size, strlen(path2), "got expected symlink size");
+ make_path(path, "broken-symlink");
+ make_path(path2, "does-not-exist");
+ PG_EXPECT_SYS(symlink(path2, path) == 0, "make a broken symlink");
+ PG_EXPECT_SYS(lstat(path, &statbuf) == 0, "lstat broken symlink");
+ PG_EXPECT(S_ISLNK(statbuf.st_mode));
+ PG_EXPECT_SYS(unlink(path) == 0);
+
/* Tests for link() and unlink(). */
make_path(path, "does-not-exist-1");
@@ -347,7 +354,7 @@ filesystem_metadata_tests(void)
fd = open(path, O_CREAT | O_EXCL | O_RDWR | PG_BINARY, 0777);
PG_EXPECT_SYS(fd >= 0, "touch name1.txt");
- PG_EXPECT_SYS(close(fd) == 0);
+ PG_REQUIRE_SYS(close(fd) == 0);
handle = CreateFile(path, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
PG_REQUIRE(handle);
@@ -368,7 +375,7 @@ filesystem_metadata_tests(void)
make_path(path, "name1.txt");
fd = open(path, O_CREAT | O_EXCL | O_RDWR | PG_BINARY, 0777);
PG_EXPECT_SYS(fd >= 0, "touch name1.txt");
- PG_EXPECT_SYS(close(fd) == 0);
+ PG_REQUIRE_SYS(fd < 0 || close(fd) == 0);
make_path(path2, "name2.txt");
PG_EXPECT_SYS(rename(path, path2) == 0, "rename name1.txt -> name2.txt");
@@ -378,14 +385,14 @@ filesystem_metadata_tests(void)
fd = open(path, O_CREAT | O_EXCL | O_RDWR | PG_BINARY, 0777);
PG_EXPECT_SYS(fd >= 0, "touch name1.txt");
- PG_EXPECT_SYS(close(fd) == 0);
+ PG_REQUIRE_SYS(fd < 0 || close(fd) == 0);
make_path(path2, "name2.txt");
PG_EXPECT_SYS(rename(path, path2) == 0, "rename name1.txt -> name2.txt, replacing it");
fd = open(path, O_CREAT | O_EXCL | O_RDWR | PG_BINARY, 0777);
PG_EXPECT_SYS(fd >= 0, "touch name1.txt");
- PG_EXPECT_SYS(close(fd) == 0);
+ PG_REQUIRE_SYS(fd < 0 || close(fd) == 0);
fd = open(path2, O_RDWR | PG_BINARY, 0777);
PG_EXPECT_SYS(fd >= 0, "open name2.txt");
@@ -414,12 +421,12 @@ filesystem_metadata_tests(void)
make_path(path2, "name2.txt");
PG_EXPECT_SYS(rename(path, path2) == 0,
"can rename name1.txt -> name2.txt while name1.txt is open");
- PG_EXPECT_SYS(close(fd) == 0);
+ PG_REQUIRE_SYS(fd < 0 || close(fd) == 0);
make_path(path, "name1.txt");
fd = open(path, O_CREAT | O_EXCL | O_RDWR | PG_BINARY, 0777);
PG_EXPECT_SYS(fd >= 0, "touch name1.txt");
- PG_EXPECT_SYS(close(fd) == 0);
+ PG_REQUIRE_SYS(fd < 0 || close(fd) == 0);
fd = open(path2, O_RDWR | PG_BINARY, 0777);
PG_EXPECT_SYS(fd >= 0, "open name2.txt");
@@ -435,9 +442,9 @@ filesystem_metadata_tests(void)
PG_EXPECT_SYS(rename(path, path2) == -1,
"Windows non-POSIX: cannot rename name1.txt -> name2.txt while unlinked file is still open");
PG_EXPECT_EQ(errno, EACCES);
- PG_REQUIRE_SYS(unlink(path) == 0);
+ PG_EXPECT_SYS(unlink(path) == 0);
}
- PG_REQUIRE_SYS(close(fd) == 0);
+ PG_REQUIRE_SYS(fd < 0 || close(fd) == 0);
#ifdef WIN32
diff --git a/src/port/win32error.c b/src/port/win32error.c
index a78d323827..67ce805d77 100644
--- a/src/port/win32error.c
+++ b/src/port/win32error.c
@@ -167,6 +167,9 @@ static const struct
},
{
ERROR_INVALID_NAME, ENOENT
+ },
+ {
+ ERROR_CANT_RESOLVE_FILENAME, ENOENT
}
};
diff --git a/src/port/win32stat.c b/src/port/win32stat.c
index 5f3d0d22ff..ce8d87093d 100644
--- a/src/port/win32stat.c
+++ b/src/port/win32stat.c
@@ -125,15 +125,30 @@ _pglstat64(const char *name, struct stat *buf)
hFile = pgwin32_open_handle(name, O_RDONLY, true);
if (hFile == INVALID_HANDLE_VALUE)
- return -1;
-
- ret = fileinfo_to_stat(hFile, buf);
+ {
+ 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;
+ }
+ else
+ ret = fileinfo_to_stat(hFile, buf);
/*
* Junction points appear as directories to fileinfo_to_stat(), so we'll
* 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];
ssize_t size;
@@ -169,10 +184,12 @@ _pglstat64(const char *name, struct stat *buf)
buf->st_mode &= ~S_IFDIR;
buf->st_mode |= S_IFLNK;
buf->st_size = size;
+ ret = 0;
}
}
- CloseHandle(hFile);
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
return ret;
}
--
2.35.1