diff options
author | galaxycrab <UgnineSirdis@ydb.tech> | 2023-11-23 11:26:33 +0300 |
---|---|---|
committer | galaxycrab <UgnineSirdis@ydb.tech> | 2023-11-23 12:01:57 +0300 |
commit | 44354d0fc55926c1d4510d1d2c9c9f6a1a5e9300 (patch) | |
tree | cb4d75cd1c6dbc3da0ed927337fd8d1b6ed9da84 /contrib/libs/libpq/src/port/dirmod.c | |
parent | 0e69bf615395fdd48ecee032faaec81bc468b0b8 (diff) | |
download | ydb-44354d0fc55926c1d4510d1d2c9c9f6a1a5e9300.tar.gz |
YQ Connector:test INNER JOIN
Diffstat (limited to 'contrib/libs/libpq/src/port/dirmod.c')
-rw-r--r-- | contrib/libs/libpq/src/port/dirmod.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/contrib/libs/libpq/src/port/dirmod.c b/contrib/libs/libpq/src/port/dirmod.c new file mode 100644 index 0000000000..07dd190cbc --- /dev/null +++ b/contrib/libs/libpq/src/port/dirmod.c @@ -0,0 +1,422 @@ +/*------------------------------------------------------------------------- + * + * dirmod.c + * directory handling functions + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * This includes replacement versions of functions that work on + * Windows. + * + * IDENTIFICATION + * src/port/dirmod.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +/* Don't modify declarations in system headers */ +#if defined(WIN32) || defined(__CYGWIN__) +#undef rename +#undef unlink +#endif + +#include <unistd.h> +#include <sys/stat.h> + +#if defined(WIN32) || defined(__CYGWIN__) +#ifndef __CYGWIN__ +#include <winioctl.h> +#else +#include <windows.h> +#include <w32api/winioctl.h> +#endif +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +#include "port/win32ntdll.h" +#endif + +#if defined(WIN32) || defined(__CYGWIN__) + +/* + * pgrename + */ +int +pgrename(const char *from, const char *to) +{ + int loops = 0; + + /* + * We need to loop because even though PostgreSQL uses flags that allow + * rename while the file is open, other applications might have the file + * open without those flags. However, we won't wait indefinitely for + * someone else to close the file, as the caller might be holding locks + * and blocking other backends. + */ +#if defined(WIN32) && !defined(__CYGWIN__) + while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING)) +#else + while (rename(from, to) < 0) +#endif + { +#if defined(WIN32) && !defined(__CYGWIN__) + DWORD err = GetLastError(); + + _dosmaperr(err); + + /* + * Modern NT-based Windows versions return ERROR_SHARING_VIOLATION if + * another process has the file open without FILE_SHARE_DELETE. + * ERROR_LOCK_VIOLATION has also been seen with some anti-virus + * software. This used to check for just ERROR_ACCESS_DENIED, so + * presumably you can get that too with some OS versions. We don't + * expect real permission errors where we currently use rename(). + */ + if (err != ERROR_ACCESS_DENIED && + err != ERROR_SHARING_VIOLATION && + err != ERROR_LOCK_VIOLATION) + return -1; +#else + if (errno != EACCES) + return -1; +#endif + + if (++loops > 100) /* time out after 10 sec */ + return -1; + pg_usleep(100000); /* us */ + } + return 0; +} + +/* + * Check if _pglstat64()'s reason for failure was STATUS_DELETE_PENDING. + * This doesn't apply to Cygwin, which has its own lstat() that would report + * the case as EACCES. +*/ +static bool +lstat_error_was_status_delete_pending(void) +{ + if (errno != ENOENT) + return false; +#if defined(WIN32) && !defined(__CYGWIN__) + if (pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING) + return true; +#endif + return false; +} + +/* + * pgunlink + */ +int +pgunlink(const char *path) +{ + bool is_lnk; + int loops = 0; + struct stat st; + + /* + * This function might be called for a regular file or for a junction + * point (which we use to emulate symlinks). The latter must be unlinked + * with rmdir() on Windows. Before we worry about any of that, let's see + * if we can unlink directly, since that's expected to be the most common + * case. + */ + if (unlink(path) == 0) + return 0; + if (errno != EACCES) + return -1; + + /* + * EACCES is reported for many reasons including unlink() of a junction + * point. Check if that's the case so we can redirect to rmdir(). + * + * Note that by checking only once, we can't cope with a path that changes + * from regular file to junction point underneath us while we're retrying + * due to sharing violations, but that seems unlikely. We could perhaps + * prevent that by holding a file handle ourselves across the lstat() and + * the retry loop, but that seems like over-engineering for now. + * + * In the special case of a STATUS_DELETE_PENDING error (file already + * unlinked, but someone still has it open), we don't want to report + * ENOENT to the caller immediately, because rmdir(parent) would probably + * fail. We want to wait until the file truly goes away so that simple + * recursive directory unlink algorithms work. + */ + if (lstat(path, &st) < 0) + { + if (lstat_error_was_status_delete_pending()) + is_lnk = false; + else + return -1; + } + else + is_lnk = S_ISLNK(st.st_mode); + + /* + * We need to loop because even though PostgreSQL uses flags that allow + * unlink while the file is open, other applications might have the file + * open without those flags. However, we won't wait indefinitely for + * someone else to close the file, as the caller might be holding locks + * and blocking other backends. + */ + while ((is_lnk ? rmdir(path) : unlink(path)) < 0) + { + if (errno != EACCES) + return -1; + if (++loops > 100) /* time out after 10 sec */ + return -1; + pg_usleep(100000); /* us */ + } + return 0; +} + +/* We undefined these above; now redefine for possible use below */ +#define rename(from, to) pgrename(from, to) +#define unlink(path) pgunlink(path) +#endif /* defined(WIN32) || defined(__CYGWIN__) */ + + +#if defined(WIN32) && !defined(__CYGWIN__) /* Cygwin has its own symlinks */ + +/* + * pgsymlink support: + * + * This struct is a replacement for REPARSE_DATA_BUFFER which is defined in VC6 winnt.h + * but omitted in later SDK functions. + * We only need the SymbolicLinkReparseBuffer part of the original struct's union. + */ +typedef struct +{ + DWORD ReparseTag; + WORD ReparseDataLength; + WORD Reserved; + /* SymbolicLinkReparseBuffer */ + WORD SubstituteNameOffset; + WORD SubstituteNameLength; + WORD PrintNameOffset; + WORD PrintNameLength; + WCHAR PathBuffer[FLEXIBLE_ARRAY_MEMBER]; +} REPARSE_JUNCTION_DATA_BUFFER; + +#define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE \ + FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset) + + +/* + * pgsymlink - uses Win32 junction points + * + * For reference: http://www.codeproject.com/KB/winsdk/junctionpoints.aspx + */ +int +pgsymlink(const char *oldpath, const char *newpath) +{ + HANDLE dirhandle; + DWORD len; + char buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)]; + char nativeTarget[MAX_PATH]; + char *p = nativeTarget; + REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer; + + CreateDirectory(newpath, 0); + dirhandle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, + 0, 0, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0); + + if (dirhandle == INVALID_HANDLE_VALUE) + { + _dosmaperr(GetLastError()); + return -1; + } + + /* make sure we have an unparsed native win32 path */ + if (memcmp("\\??\\", oldpath, 4) != 0) + snprintf(nativeTarget, sizeof(nativeTarget), "\\??\\%s", oldpath); + else + strlcpy(nativeTarget, oldpath, sizeof(nativeTarget)); + + while ((p = strchr(p, '/')) != NULL) + *p++ = '\\'; + + len = strlen(nativeTarget) * sizeof(WCHAR); + reparseBuf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + reparseBuf->ReparseDataLength = len + 12; + reparseBuf->Reserved = 0; + reparseBuf->SubstituteNameOffset = 0; + reparseBuf->SubstituteNameLength = len; + reparseBuf->PrintNameOffset = len + sizeof(WCHAR); + reparseBuf->PrintNameLength = 0; + MultiByteToWideChar(CP_ACP, 0, nativeTarget, -1, + reparseBuf->PathBuffer, MAX_PATH); + + /* + * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version; + * we use our own definition + */ + if (!DeviceIoControl(dirhandle, + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS), + reparseBuf, + reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE, + 0, 0, &len, 0)) + { + LPSTR msg; + int save_errno; + + _dosmaperr(GetLastError()); + save_errno = errno; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, GetLastError(), + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + (LPSTR) &msg, 0, NULL); +#ifndef FRONTEND + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not set junction for \"%s\": %s", + nativeTarget, msg))); +#else + fprintf(stderr, _("could not set junction for \"%s\": %s\n"), + nativeTarget, msg); +#endif + LocalFree(msg); + + CloseHandle(dirhandle); + RemoveDirectory(newpath); + + errno = save_errno; + + return -1; + } + + CloseHandle(dirhandle); + + return 0; +} + +/* + * pgreadlink - uses Win32 junction points + */ +int +pgreadlink(const char *path, char *buf, size_t size) +{ + DWORD attr; + HANDLE h; + char buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)]; + REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer; + DWORD len; + int r; + + attr = GetFileAttributes(path); + if (attr == INVALID_FILE_ATTRIBUTES) + { + _dosmaperr(GetLastError()); + return -1; + } + if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0) + { + errno = EINVAL; + return -1; + } + + h = CreateFile(path, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + 0); + if (h == INVALID_HANDLE_VALUE) + { + _dosmaperr(GetLastError()); + return -1; + } + + if (!DeviceIoControl(h, + FSCTL_GET_REPARSE_POINT, + NULL, + 0, + (LPVOID) reparseBuf, + sizeof(buffer), + &len, + NULL)) + { + LPSTR msg; + + errno = 0; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, GetLastError(), + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + (LPSTR) &msg, 0, NULL); +#ifndef FRONTEND + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not get junction for \"%s\": %s", + path, msg))); +#else + fprintf(stderr, _("could not get junction for \"%s\": %s\n"), + path, msg); +#endif + LocalFree(msg); + CloseHandle(h); + errno = EINVAL; + return -1; + } + CloseHandle(h); + + /* Got it, let's get some results from this */ + if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) + { + errno = EINVAL; + return -1; + } + + r = WideCharToMultiByte(CP_ACP, 0, + reparseBuf->PathBuffer, -1, + buf, + size, + NULL, NULL); + + if (r <= 0) + { + errno = EINVAL; + return -1; + } + + /* r includes the null terminator */ + r -= 1; + + /* + * If the path starts with "\??\" followed by a "drive absolute" path + * (known to Windows APIs as RtlPathTypeDriveAbsolute), then strip that + * prefix. This undoes some of the transformation performed by + * pgsymlink(), to get back to a format that users are used to seeing. We + * don't know how to transform other path types that might be encountered + * outside PGDATA, so we just return them directly. + */ + if (r >= 7 && + buf[0] == '\\' && + buf[1] == '?' && + buf[2] == '?' && + buf[3] == '\\' && + isalpha(buf[4]) && + buf[5] == ':' && + buf[6] == '\\') + { + memmove(buf, buf + 4, strlen(buf + 4) + 1); + r -= 4; + } + return r; +} + +#endif /* defined(WIN32) && !defined(__CYGWIN__) */ |