postgresql/src/port/dirent.c
Thomas Munro 6d306ab731 Fix get_dirent_type() for Windows junction points.
Commit 87e6ed7c8 added code that intended to report Windows "junction
points" as DT_LNK (the same way we report symlinks on Unix).  Windows
junction points are *also* directories according to the Windows
attributes API, and we were reporting them as as DT_DIR.  Change the
order we check the attribute flags, to prioritize DT_LNK.

If at some point we start using Windows' recently added real symlinks
and need to distinguish them from junction points, we may need to
rethink this, but for now this continues the tradition of wrapper
functions that treat junction points as symlinks.

Back-patch to 14, where get_dirent_type() landed.

Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
Discussion: https://postgr.es/m/CA%2BhUKGLzLK4PUPx0_AwXEWXOYAejU%3D7XpxnYE55Y%2Be7hB2N3FA%40mail.gmail.com
Discussion: https://postgr.es/m/20220721111751.x7hod2xgrd76xr5c%40alvherre.pgsql
2022-07-22 16:57:36 +12:00

138 lines
2.8 KiB
C

/*-------------------------------------------------------------------------
*
* dirent.c
* opendir/readdir/closedir for win32/msvc
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/port/dirent.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <dirent.h>
struct DIR
{
char *dirname;
struct dirent ret; /* Used to return to caller */
HANDLE handle;
};
DIR *
opendir(const char *dirname)
{
DWORD attr;
DIR *d;
/* Make sure it is a directory */
attr = GetFileAttributes(dirname);
if (attr == INVALID_FILE_ATTRIBUTES)
{
errno = ENOENT;
return NULL;
}
if ((attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
{
errno = ENOTDIR;
return NULL;
}
d = malloc(sizeof(DIR));
if (!d)
{
errno = ENOMEM;
return NULL;
}
d->dirname = malloc(strlen(dirname) + 4);
if (!d->dirname)
{
errno = ENOMEM;
free(d);
return NULL;
}
strcpy(d->dirname, dirname);
if (d->dirname[strlen(d->dirname) - 1] != '/' &&
d->dirname[strlen(d->dirname) - 1] != '\\')
strcat(d->dirname, "\\"); /* Append backslash if not already there */
strcat(d->dirname, "*"); /* Search for entries named anything */
d->handle = INVALID_HANDLE_VALUE;
d->ret.d_ino = 0; /* no inodes on win32 */
d->ret.d_reclen = 0; /* not used on win32 */
d->ret.d_type = DT_UNKNOWN;
return d;
}
struct dirent *
readdir(DIR *d)
{
WIN32_FIND_DATA fd;
if (d->handle == INVALID_HANDLE_VALUE)
{
d->handle = FindFirstFile(d->dirname, &fd);
if (d->handle == INVALID_HANDLE_VALUE)
{
/* If there are no files, force errno=0 (unlike mingw) */
if (GetLastError() == ERROR_FILE_NOT_FOUND)
errno = 0;
else
_dosmaperr(GetLastError());
return NULL;
}
}
else
{
if (!FindNextFile(d->handle, &fd))
{
/* If there are no more files, force errno=0 (like mingw) */
if (GetLastError() == ERROR_NO_MORE_FILES)
errno = 0;
else
_dosmaperr(GetLastError());
return NULL;
}
}
strcpy(d->ret.d_name, fd.cFileName); /* Both strings are MAX_PATH long */
d->ret.d_namlen = strlen(d->ret.d_name);
/*
* For reparse points dwReserved0 field will contain the ReparseTag. We
* check this first, because reparse points are also reported as
* directories.
*/
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 &&
(fd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT))
d->ret.d_type = DT_LNK;
else if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
d->ret.d_type = DT_DIR;
else
d->ret.d_type = DT_REG;
return &d->ret;
}
int
closedir(DIR *d)
{
int ret = 0;
if (d->handle != INVALID_HANDLE_VALUE)
ret = !FindClose(d->handle);
free(d->dirname);
free(d);
return ret;
}