Fix stat() for recursive junction points on Windows.
authorThomas Munro <tmunro@postgresql.org>
Tue, 25 Oct 2022 02:24:41 +0000 (15:24 +1300)
committerAndrew Dunstan <andrew@dunslane.net>
Sat, 18 Jan 2025 14:33:16 +0000 (09:33 -0500)
Commit c5cb8f3b supposed that we'd only ever have to follow one junction
point in stat(), because we don't construct longer chains of them ourselves.
When examining a parent directory supplied by the user, we should really be
able to cope with longer chains, just in case someone has their system
set up that way.  Choose an arbitrary cap of 8, to match the minimum
acceptable value of SYMLOOP_MAX in POSIX.

Previously I'd avoided reporting ELOOP thinking Windows didn't have it,
but it turns out that it does, so we can use the proper error number.

Reviewed-by: Roman Zharkov <r.zharkov@postgrespro.ru>
Discussion: https://postgr.es/m/CA%2BhUKGJ7JDGWYFt9%3D-TyJiRRy5q9TtPfqeKkneWDr1XPU1%2Biqw%40mail.gmail.com
Discussion: https://postgr.es/m/CA%2BhUKG%2BajSQ_8eu2AogTncOnZ5me2D-Cn66iN_-wZnRjLN%2Bicg%40mail.gmail.com

Backpatched commit 4517358e as above by Thomas Munro into releases 13 thru 15

Discussion: https://postgr.es/m/CA+hUKGLbnv+pe3q1fYOVkLD3pMra7GuihfMxUN-1831YH9RYQg@mail.gmail.com

src/port/win32stat.c

index 49fbb3ce649b3d9e110480f6a2c3f49e5c715ee1..28813e3f89fff8e6f8acf4bbfdf7a0fad27bd527 100644 (file)
@@ -201,23 +201,33 @@ _pglstat64(const char *name, struct stat *buf)
 int
 _pgstat64(const char *name, struct stat *buf)
 {
+   int         loops = 0;
    int         ret;
+   char        curr[MAXPGPATH];
 
    ret = _pglstat64(name, buf);
 
+   strlcpy(curr, name, MAXPGPATH);
+
    /* Do we need to follow a symlink (junction point)? */
-   if (ret == 0 && S_ISLNK(buf->st_mode))
+   while (ret == 0 && S_ISLNK(buf->st_mode))
    {
        char        next[MAXPGPATH];
        ssize_t     size;
 
+       if (++loops > 8)
+       {
+           errno = ELOOP;
+           return -1;
+       }
+
        /*
         * _pglstat64() already called readlink() once to be able to fill in
         * st_size, and now we need to do it again to get the path to follow.
         * That could be optimized, but stat() on symlinks is probably rare
         * and this way is simple.
         */
-       size = readlink(name, next, sizeof(next));
+       size = readlink(curr, next, sizeof(next));
        if (size < 0)
        {
            if (errno == EACCES &&
@@ -236,17 +246,7 @@ _pgstat64(const char *name, struct stat *buf)
        next[size] = 0;
 
        ret = _pglstat64(next, buf);
-       if (ret == 0 && S_ISLNK(buf->st_mode))
-       {
-           /*
-            * We're only prepared to go one hop, because we only expect to
-            * deal with the simple cases that we create.  The error for too
-            * many symlinks is supposed to be ELOOP, but Windows hasn't got
-            * it.
-            */
-           errno = EIO;
-           return -1;
-       }
+       strcpy(curr, next);
    }
 
    return ret;