diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
commit | 293913568e6a7a86fd1479e1cff8e2ecb58d6568 (patch) | |
tree | fc3b469a3ec5ab71b36ea97cc7aaddb838423a0c /src/port | |
parent | Initial commit. (diff) | |
download | postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.tar.xz postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.zip |
Adding upstream version 16.2.upstream/16.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/port')
65 files changed, 11196 insertions, 0 deletions
diff --git a/src/port/.gitignore b/src/port/.gitignore new file mode 100644 index 0000000..2037b7d --- /dev/null +++ b/src/port/.gitignore @@ -0,0 +1,4 @@ +/libpgport.a +/libpgport_shlib.a +/libpgport_srv.a +/pg_config_paths.h diff --git a/src/port/Makefile b/src/port/Makefile new file mode 100644 index 0000000..711f59e --- /dev/null +++ b/src/port/Makefile @@ -0,0 +1,163 @@ +#------------------------------------------------------------------------- +# +# Makefile +# Makefile for src/port +# +# These files are used by the Postgres backend, and also by frontend +# programs. Primarily, they are meant to provide portability on systems +# with broken/missing library files. +# +# This makefile generates three outputs: +# +# libpgport.a - contains object files with FRONTEND defined, +# for use by client applications +# +# libpgport_shlib.a - contains object files with FRONTEND defined, +# built suitably for use in shared libraries; for use +# by frontend libraries +# +# libpgport_srv.a - contains object files without FRONTEND defined, +# for use only by the backend +# +# LIBOBJS is set by configure (via Makefile.global) to be the list of object +# files that are conditionally needed as determined by configure's probing. +# OBJS adds additional object files that are always compiled. +# +# IDENTIFICATION +# src/port/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/port +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global + +override CPPFLAGS := -I$(top_builddir)/src/port -DFRONTEND $(CPPFLAGS) +LIBS += $(PTHREAD_LIBS) + +# If you add objects here, see also src/tools/msvc/Mkvcbuild.pm + +OBJS = \ + $(LIBOBJS) \ + $(PG_CRC32C_OBJS) \ + bsearch_arg.o \ + chklocale.o \ + inet_net_ntop.o \ + noblock.o \ + path.o \ + pg_bitutils.o \ + pg_strong_random.o \ + pgcheckdir.o \ + pgmkdirp.o \ + pgsleep.o \ + pgstrcasecmp.o \ + pgstrsignal.o \ + pqsignal.o \ + qsort.o \ + qsort_arg.o \ + quotes.o \ + snprintf.o \ + strerror.o \ + tar.o \ + thread.o + +# libpgport.a, libpgport_shlib.a, and libpgport_srv.a contain the same files +# foo.o, foo_shlib.o, and foo_srv.o are all built from foo.c +OBJS_SHLIB = $(OBJS:%.o=%_shlib.o) +OBJS_SRV = $(OBJS:%.o=%_srv.o) + +all: libpgport.a libpgport_shlib.a libpgport_srv.a + +# libpgport is needed by some contrib +install: all installdirs + $(INSTALL_STLIB) libpgport.a '$(DESTDIR)$(libdir)/libpgport.a' + $(INSTALL_STLIB) libpgport_shlib.a '$(DESTDIR)$(libdir)/libpgport_shlib.a' + +installdirs: + $(MKDIR_P) '$(DESTDIR)$(libdir)' + +uninstall: + rm -f '$(DESTDIR)$(libdir)/libpgport.a' + rm -f '$(DESTDIR)$(libdir)/libpgport_shlib.a' + +libpgport.a: $(OBJS) + rm -f $@ + $(AR) $(AROPT) $@ $^ + +# thread.o and thread_shlib.o need PTHREAD_CFLAGS (but thread_srv.o does not) +thread.o: CFLAGS+=$(PTHREAD_CFLAGS) +thread_shlib.o: CFLAGS+=$(PTHREAD_CFLAGS) + +# all versions of pg_crc32c_sse42.o need CFLAGS_CRC +pg_crc32c_sse42.o: CFLAGS+=$(CFLAGS_CRC) +pg_crc32c_sse42_shlib.o: CFLAGS+=$(CFLAGS_CRC) +pg_crc32c_sse42_srv.o: CFLAGS+=$(CFLAGS_CRC) + +# all versions of pg_crc32c_armv8.o need CFLAGS_CRC +pg_crc32c_armv8.o: CFLAGS+=$(CFLAGS_CRC) +pg_crc32c_armv8_shlib.o: CFLAGS+=$(CFLAGS_CRC) +pg_crc32c_armv8_srv.o: CFLAGS+=$(CFLAGS_CRC) + +# +# Shared library versions of object files +# + +libpgport_shlib.a: $(OBJS_SHLIB) + rm -f $@ + $(AR) $(AROPT) $@ $^ + +# Because this uses its own compilation rule, it doesn't use the +# dependency tracking logic from Makefile.global. To make sure that +# dependency tracking works anyway for the *_shlib.o files, depend on +# their *.o siblings as well, which do have proper dependencies. It's +# a hack that might fail someday if there is a *_shlib.o without a +# corresponding *.o, but there seems little reason for that. +%_shlib.o: %.c %.o + $(CC) $(CFLAGS) $(CFLAGS_SL) $(CPPFLAGS) -c $< -o $@ + +# +# Server versions of object files +# + +libpgport_srv.a: $(OBJS_SRV) + rm -f $@ + $(AR) $(AROPT) $@ $^ + +# Because this uses its own compilation rule, it doesn't use the +# dependency tracking logic from Makefile.global. To make sure that +# dependency tracking works anyway for the *_srv.o files, depend on +# their *.o siblings as well, which do have proper dependencies. It's +# a hack that might fail someday if there is a *_srv.o without a +# corresponding *.o, but it works for now (and those would probably go +# into src/backend/port/ anyway). +%_srv.o: %.c %.o + $(CC) $(CFLAGS) $(subst -DFRONTEND,, $(CPPFLAGS)) -c $< -o $@ + +# Dependency is to ensure that path changes propagate + +path.o: path.c pg_config_paths.h + +path_shlib.o: path.c pg_config_paths.h + +path_srv.o: path.c pg_config_paths.h + +# We create a separate file rather than put these in pg_config.h +# because many of these values come from makefiles and are not +# available to configure. +pg_config_paths.h: $(top_builddir)/src/Makefile.global + echo "#define PGBINDIR \"$(bindir)\"" >$@ + echo "#define PGSHAREDIR \"$(datadir)\"" >>$@ + echo "#define SYSCONFDIR \"$(sysconfdir)\"" >>$@ + echo "#define INCLUDEDIR \"$(includedir)\"" >>$@ + echo "#define PKGINCLUDEDIR \"$(pkgincludedir)\"" >>$@ + echo "#define INCLUDEDIRSERVER \"$(includedir_server)\"" >>$@ + echo "#define LIBDIR \"$(libdir)\"" >>$@ + echo "#define PKGLIBDIR \"$(pkglibdir)\"" >>$@ + echo "#define LOCALEDIR \"$(localedir)\"" >>$@ + echo "#define DOCDIR \"$(docdir)\"" >>$@ + echo "#define HTMLDIR \"$(htmldir)\"" >>$@ + echo "#define MANDIR \"$(mandir)\"" >>$@ + +clean distclean maintainer-clean: + rm -f libpgport.a libpgport_shlib.a libpgport_srv.a + rm -f $(OBJS) $(OBJS_SHLIB) $(OBJS_SRV) pg_config_paths.h diff --git a/src/port/README b/src/port/README new file mode 100644 index 0000000..97f18a6 --- /dev/null +++ b/src/port/README @@ -0,0 +1,32 @@ +src/port/README + +libpgport +========= + +libpgport must have special behavior. It supplies functions to both +libraries and applications. However, there are two complexities: + +1) Libraries need to use object files that are compiled with exactly +the same flags as the library. libpgport might not use the same flags, +so it is necessary to recompile the object files for individual +libraries. This is done by removing -lpgport from the link line: + + # Need to recompile any libpgport object files + LIBS := $(filter-out -lpgport, $(LIBS)) + +and adding infrastructure to recompile the object files: + + OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \ + connect.o misc.o path.o exec.o \ + $(filter strlcat.o, $(LIBOBJS)) + +The problem is that there is no testing of which object files need to be +added, but missing functions usually show up when linking user +applications. + +2) For applications, we use -lpgport before -lpq, so the static files +from libpgport are linked first. This avoids having applications +dependent on symbols that are _used_ by libpq, but not intended to be +exported by libpq. libpq's libpgport usage changes over time, so such a +dependency is a problem. Windows, Linux, AIX, and macOS use an export +list to control the symbols exported by libpq. diff --git a/src/port/bsearch_arg.c b/src/port/bsearch_arg.c new file mode 100644 index 0000000..641b40c --- /dev/null +++ b/src/port/bsearch_arg.c @@ -0,0 +1,78 @@ +/* + * bsearch_arg.c: bsearch variant with a user-supplied pointer + * + * Copyright (c) 2021-2023, PostgreSQL Global Development Group + * Copyright (c) 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. [rescinded 22 July 1999] + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * src/port/bsearch_arg.c + */ + +#include "c.h" + +/* + * Perform a binary search. + * + * The code below is a bit sneaky. After a comparison fails, we + * divide the work in half by moving either left or right. If lim + * is odd, moving left simply involves halving lim: e.g., when lim + * is 5 we look at item 2, so we change lim to 2 so that we will + * look at items 0 & 1. If lim is even, the same applies. If lim + * is odd, moving right again involves halving lim, this time moving + * the base up one item past p: e.g., when lim is 5 we change base + * to item 3 and make lim 2 so that we will look at items 3 and 4. + * If lim is even, however, we have to shrink it by one before + * halving: e.g., when lim is 4, we still looked at item 2, so we + * have to make lim 3, then halve, obtaining 1, so that we will only + * look at item 3. + */ +void * +bsearch_arg(const void *key, const void *base0, + size_t nmemb, size_t size, + int (*compar) (const void *, const void *, void *), + void *arg) +{ + const char *base = (const char *) base0; + int lim, + cmp; + const void *p; + + for (lim = nmemb; lim != 0; lim >>= 1) + { + p = base + (lim >> 1) * size; + cmp = (*compar) (key, p, arg); + if (cmp == 0) + return (void *) p; + if (cmp > 0) + { /* key > p: move right */ + base = (const char *) p + size; + lim--; + } /* else move left */ + } + return (NULL); +} diff --git a/src/port/chklocale.c b/src/port/chklocale.c new file mode 100644 index 0000000..6fa6810 --- /dev/null +++ b/src/port/chklocale.c @@ -0,0 +1,433 @@ +/*------------------------------------------------------------------------- + * + * chklocale.c + * Functions for handling locale-related info + * + * + * Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/chklocale.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif + +#include "mb/pg_wchar.h" + + +/* + * This table needs to recognize all the CODESET spellings for supported + * backend encodings, as well as frontend-only encodings where possible + * (the latter case is currently only needed for initdb to recognize + * error situations). On Windows, we rely on entries for codepage + * numbers (CPnnn). + * + * Note that we search the table with pg_strcasecmp(), so variant + * capitalizations don't need their own entries. + */ +struct encoding_match +{ + enum pg_enc pg_enc_code; + const char *system_enc_name; +}; + +static const struct encoding_match encoding_match_list[] = { + {PG_EUC_JP, "EUC-JP"}, + {PG_EUC_JP, "eucJP"}, + {PG_EUC_JP, "IBM-eucJP"}, + {PG_EUC_JP, "sdeckanji"}, + {PG_EUC_JP, "CP20932"}, + + {PG_EUC_CN, "EUC-CN"}, + {PG_EUC_CN, "eucCN"}, + {PG_EUC_CN, "IBM-eucCN"}, + {PG_EUC_CN, "GB2312"}, + {PG_EUC_CN, "dechanzi"}, + {PG_EUC_CN, "CP20936"}, + + {PG_EUC_KR, "EUC-KR"}, + {PG_EUC_KR, "eucKR"}, + {PG_EUC_KR, "IBM-eucKR"}, + {PG_EUC_KR, "deckorean"}, + {PG_EUC_KR, "5601"}, + {PG_EUC_KR, "CP51949"}, + + {PG_EUC_TW, "EUC-TW"}, + {PG_EUC_TW, "eucTW"}, + {PG_EUC_TW, "IBM-eucTW"}, + {PG_EUC_TW, "cns11643"}, + /* No codepage for EUC-TW ? */ + + {PG_UTF8, "UTF-8"}, + {PG_UTF8, "utf8"}, + {PG_UTF8, "CP65001"}, + + {PG_LATIN1, "ISO-8859-1"}, + {PG_LATIN1, "ISO8859-1"}, + {PG_LATIN1, "iso88591"}, + {PG_LATIN1, "CP28591"}, + + {PG_LATIN2, "ISO-8859-2"}, + {PG_LATIN2, "ISO8859-2"}, + {PG_LATIN2, "iso88592"}, + {PG_LATIN2, "CP28592"}, + + {PG_LATIN3, "ISO-8859-3"}, + {PG_LATIN3, "ISO8859-3"}, + {PG_LATIN3, "iso88593"}, + {PG_LATIN3, "CP28593"}, + + {PG_LATIN4, "ISO-8859-4"}, + {PG_LATIN4, "ISO8859-4"}, + {PG_LATIN4, "iso88594"}, + {PG_LATIN4, "CP28594"}, + + {PG_LATIN5, "ISO-8859-9"}, + {PG_LATIN5, "ISO8859-9"}, + {PG_LATIN5, "iso88599"}, + {PG_LATIN5, "CP28599"}, + + {PG_LATIN6, "ISO-8859-10"}, + {PG_LATIN6, "ISO8859-10"}, + {PG_LATIN6, "iso885910"}, + + {PG_LATIN7, "ISO-8859-13"}, + {PG_LATIN7, "ISO8859-13"}, + {PG_LATIN7, "iso885913"}, + + {PG_LATIN8, "ISO-8859-14"}, + {PG_LATIN8, "ISO8859-14"}, + {PG_LATIN8, "iso885914"}, + + {PG_LATIN9, "ISO-8859-15"}, + {PG_LATIN9, "ISO8859-15"}, + {PG_LATIN9, "iso885915"}, + {PG_LATIN9, "CP28605"}, + + {PG_LATIN10, "ISO-8859-16"}, + {PG_LATIN10, "ISO8859-16"}, + {PG_LATIN10, "iso885916"}, + + {PG_KOI8R, "KOI8-R"}, + {PG_KOI8R, "CP20866"}, + + {PG_KOI8U, "KOI8-U"}, + {PG_KOI8U, "CP21866"}, + + {PG_WIN866, "CP866"}, + {PG_WIN874, "CP874"}, + {PG_WIN1250, "CP1250"}, + {PG_WIN1251, "CP1251"}, + {PG_WIN1251, "ansi-1251"}, + {PG_WIN1252, "CP1252"}, + {PG_WIN1253, "CP1253"}, + {PG_WIN1254, "CP1254"}, + {PG_WIN1255, "CP1255"}, + {PG_WIN1256, "CP1256"}, + {PG_WIN1257, "CP1257"}, + {PG_WIN1258, "CP1258"}, + + {PG_ISO_8859_5, "ISO-8859-5"}, + {PG_ISO_8859_5, "ISO8859-5"}, + {PG_ISO_8859_5, "iso88595"}, + {PG_ISO_8859_5, "CP28595"}, + + {PG_ISO_8859_6, "ISO-8859-6"}, + {PG_ISO_8859_6, "ISO8859-6"}, + {PG_ISO_8859_6, "iso88596"}, + {PG_ISO_8859_6, "CP28596"}, + + {PG_ISO_8859_7, "ISO-8859-7"}, + {PG_ISO_8859_7, "ISO8859-7"}, + {PG_ISO_8859_7, "iso88597"}, + {PG_ISO_8859_7, "CP28597"}, + + {PG_ISO_8859_8, "ISO-8859-8"}, + {PG_ISO_8859_8, "ISO8859-8"}, + {PG_ISO_8859_8, "iso88598"}, + {PG_ISO_8859_8, "CP28598"}, + + {PG_SJIS, "SJIS"}, + {PG_SJIS, "PCK"}, + {PG_SJIS, "CP932"}, + {PG_SJIS, "SHIFT_JIS"}, + + {PG_BIG5, "BIG5"}, + {PG_BIG5, "BIG5HKSCS"}, + {PG_BIG5, "Big5-HKSCS"}, + {PG_BIG5, "CP950"}, + + {PG_GBK, "GBK"}, + {PG_GBK, "CP936"}, + + {PG_UHC, "UHC"}, + {PG_UHC, "CP949"}, + + {PG_JOHAB, "JOHAB"}, + {PG_JOHAB, "CP1361"}, + + {PG_GB18030, "GB18030"}, + {PG_GB18030, "CP54936"}, + + {PG_SHIFT_JIS_2004, "SJIS_2004"}, + + {PG_SQL_ASCII, "US-ASCII"}, + + {PG_SQL_ASCII, NULL} /* end marker */ +}; + +#ifdef WIN32 +/* + * On Windows, use CP<code page number> instead of the nl_langinfo() result + * + * This routine uses GetLocaleInfoEx() to parse short locale names like + * "de-DE", "fr-FR", etc. If those cannot be parsed correctly process falls + * back to the pre-VS-2010 manual parsing done with using + * <Language>_<Country>.<CodePage> as a base. + * + * Returns a malloc()'d string for the caller to free. + */ +static char * +win32_langinfo(const char *ctype) +{ + char *r = NULL; + char *codepage; + +#if defined(_MSC_VER) + uint32 cp; + WCHAR wctype[LOCALE_NAME_MAX_LENGTH]; + + memset(wctype, 0, sizeof(wctype)); + MultiByteToWideChar(CP_ACP, 0, ctype, -1, wctype, LOCALE_NAME_MAX_LENGTH); + + if (GetLocaleInfoEx(wctype, + LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (LPWSTR) &cp, sizeof(cp) / sizeof(WCHAR)) > 0) + { + r = malloc(16); /* excess */ + if (r != NULL) + { + /* + * If the return value is CP_ACP that means no ANSI code page is + * available, so only Unicode can be used for the locale. + */ + if (cp == CP_ACP) + strcpy(r, "utf8"); + else + sprintf(r, "CP%u", cp); + } + } + else +#endif + { + /* + * Locale format on Win32 is <Language>_<Country>.<CodePage>. For + * example, English_United States.1252. If we see digits after the + * last dot, assume it's a codepage number. Otherwise, we might be + * dealing with a Unix-style locale string; Windows' setlocale() will + * take those even though GetLocaleInfoEx() won't, so we end up here. + * In that case, just return what's after the last dot and hope we can + * find it in our table. + */ + codepage = strrchr(ctype, '.'); + if (codepage != NULL) + { + size_t ln; + + codepage++; + ln = strlen(codepage); + r = malloc(ln + 3); + if (r != NULL) + { + if (strspn(codepage, "0123456789") == ln) + sprintf(r, "CP%s", codepage); + else + strcpy(r, codepage); + } + } + } + + return r; +} + +#ifndef FRONTEND +/* + * Given a Windows code page identifier, find the corresponding PostgreSQL + * encoding. Issue a warning and return -1 if none found. + */ +int +pg_codepage_to_encoding(UINT cp) +{ + char sys[16]; + int i; + + sprintf(sys, "CP%u", cp); + + /* Check the table */ + for (i = 0; encoding_match_list[i].system_enc_name; i++) + if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) + return encoding_match_list[i].pg_enc_code; + + ereport(WARNING, + (errmsg("could not determine encoding for codeset \"%s\"", sys))); + + return -1; +} +#endif +#endif /* WIN32 */ + +#if (defined(HAVE_LANGINFO_H) && defined(CODESET)) || defined(WIN32) + +/* + * Given a setting for LC_CTYPE, return the Postgres ID of the associated + * encoding, if we can determine it. Return -1 if we can't determine it. + * + * Pass in NULL to get the encoding for the current locale setting. + * Pass "" to get the encoding selected by the server's environment. + * + * If the result is PG_SQL_ASCII, callers should treat it as being compatible + * with any desired encoding. + * + * If running in the backend and write_message is false, this function must + * cope with the possibility that elog() and palloc() are not yet usable. + */ +int +pg_get_encoding_from_locale(const char *ctype, bool write_message) +{ + char *sys; + int i; + + /* Get the CODESET property, and also LC_CTYPE if not passed in */ + if (ctype) + { + char *save; + char *name; + + /* If locale is C or POSIX, we can allow all encodings */ + if (pg_strcasecmp(ctype, "C") == 0 || + pg_strcasecmp(ctype, "POSIX") == 0) + return PG_SQL_ASCII; + + save = setlocale(LC_CTYPE, NULL); + if (!save) + return -1; /* setlocale() broken? */ + /* must copy result, or it might change after setlocale */ + save = strdup(save); + if (!save) + return -1; /* out of memory; unlikely */ + + name = setlocale(LC_CTYPE, ctype); + if (!name) + { + free(save); + return -1; /* bogus ctype passed in? */ + } + +#ifndef WIN32 + sys = nl_langinfo(CODESET); + if (sys) + sys = strdup(sys); +#else + sys = win32_langinfo(name); +#endif + + setlocale(LC_CTYPE, save); + free(save); + } + else + { + /* much easier... */ + ctype = setlocale(LC_CTYPE, NULL); + if (!ctype) + return -1; /* setlocale() broken? */ + + /* If locale is C or POSIX, we can allow all encodings */ + if (pg_strcasecmp(ctype, "C") == 0 || + pg_strcasecmp(ctype, "POSIX") == 0) + return PG_SQL_ASCII; + +#ifndef WIN32 + sys = nl_langinfo(CODESET); + if (sys) + sys = strdup(sys); +#else + sys = win32_langinfo(ctype); +#endif + } + + if (!sys) + return -1; /* out of memory; unlikely */ + + /* Check the table */ + for (i = 0; encoding_match_list[i].system_enc_name; i++) + { + if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) + { + free(sys); + return encoding_match_list[i].pg_enc_code; + } + } + + /* Special-case kluges for particular platforms go here */ + +#ifdef __darwin__ + + /* + * Current macOS has many locales that report an empty string for CODESET, + * but they all seem to actually use UTF-8. + */ + if (strlen(sys) == 0) + { + free(sys); + return PG_UTF8; + } +#endif + + /* + * We print a warning if we got a CODESET string but couldn't recognize + * it. This means we need another entry in the table. + */ + if (write_message) + { +#ifdef FRONTEND + fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""), + ctype, sys); + /* keep newline separate so there's only one translatable string */ + fputc('\n', stderr); +#else + ereport(WARNING, + (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"", + ctype, sys))); +#endif + } + + free(sys); + return -1; +} +#else /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ + +/* + * stub if no multi-language platform support + * + * Note: we could return -1 here, but that would have the effect of + * forcing users to specify an encoding to initdb on such platforms. + * It seems better to silently default to SQL_ASCII. + */ +int +pg_get_encoding_from_locale(const char *ctype, bool write_message) +{ + return PG_SQL_ASCII; +} + +#endif /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ diff --git a/src/port/dirent.c b/src/port/dirent.c new file mode 100644 index 0000000..528b4b2 --- /dev/null +++ b/src/port/dirent.c @@ -0,0 +1,137 @@ +/*------------------------------------------------------------------------- + * + * dirent.c + * opendir/readdir/closedir for win32/msvc + * + * Portions Copyright (c) 1996-2023, 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; +} diff --git a/src/port/dirmod.c b/src/port/dirmod.c new file mode 100644 index 0000000..07dd190 --- /dev/null +++ b/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__) */ diff --git a/src/port/explicit_bzero.c b/src/port/explicit_bzero.c new file mode 100644 index 0000000..f3a5038 --- /dev/null +++ b/src/port/explicit_bzero.c @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * explicit_bzero.c + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/explicit_bzero.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#if defined(HAVE_MEMSET_S) + +void +explicit_bzero(void *buf, size_t len) +{ + (void) memset_s(buf, len, 0, len); +} + +#elif defined(WIN32) + +void +explicit_bzero(void *buf, size_t len) +{ + (void) SecureZeroMemory(buf, len); +} + +#else + +/* + * Indirect call through a volatile pointer to hopefully avoid dead-store + * optimisation eliminating the call. (Idea taken from OpenSSH.) We can't + * assume bzero() is present either, so for simplicity we define our own. + */ + +static void +bzero2(void *buf, size_t len) +{ + memset(buf, 0, len); +} + +static void (*volatile bzero_p) (void *, size_t) = bzero2; + +void +explicit_bzero(void *buf, size_t len) +{ + bzero_p(buf, len); +} + +#endif diff --git a/src/port/getopt.c b/src/port/getopt.c new file mode 100644 index 0000000..207c283 --- /dev/null +++ b/src/port/getopt.c @@ -0,0 +1,136 @@ +/* src/port/getopt.c */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + */ + +#include "c.h" + +#include "pg_getopt.h" + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif /* LIBC_SCCS and not lint */ + + +/* + * On OpenBSD and some versions of Solaris, opterr and friends are defined in + * core libc rather than in a separate getopt module. Define these variables + * only if configure found they aren't there by default; otherwise, this + * module and its callers will just use libc's variables. (We assume that + * testing opterr is sufficient for all of these.) + */ +#ifndef HAVE_INT_OPTERR + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char *optarg; /* argument associated with option */ + +#endif + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt + * Parse argc/argv argument vector. + * + * This implementation does not use optreset. Instead, we guarantee that + * it can be restarted on a new argv array after a previous call returned -1, + * if the caller resets optind to 1 before the first call of the new series. + * (Internally, this means we must be sure to reset "place" to EMSG before + * returning -1.) + */ +int +getopt(int nargc, char *const *nargv, const char *ostr) +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (!*place) + { /* update scanning pointer */ + if (optind >= nargc || *(place = nargv[optind]) != '-') + { + place = EMSG; + return -1; + } + if (place[1] && *++place == '-' && place[1] == '\0') + { /* found "--" */ + ++optind; + place = EMSG; + return -1; + } + } /* option letter okay? */ + if ((optopt = (int) *place++) == (int) ':' || + !(oli = strchr(ostr, optopt))) + { + /* + * if the user didn't specify '-' as an option, assume it means -1. + */ + if (optopt == (int) '-') + { + place = EMSG; + return -1; + } + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void) fprintf(stderr, + "illegal option -- %c\n", optopt); + return BADCH; + } + if (*++oli != ':') + { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else + { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) + { /* no arg */ + place = EMSG; + if (*ostr == ':') + return BADARG; + if (opterr) + (void) fprintf(stderr, + "option requires an argument -- %c\n", + optopt); + return BADCH; + } + else + /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return optopt; /* dump back option letter */ +} diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c new file mode 100644 index 0000000..c989276 --- /dev/null +++ b/src/port/getopt_long.c @@ -0,0 +1,218 @@ +/* + * getopt_long() -- long options parser + * + * Portions Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Portions Copyright (c) 2003 + * PostgreSQL Global Development Group + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * src/port/getopt_long.c + */ + +#include "c.h" + +#include "getopt_long.h" + +#define BADCH '?' +#define BADARG ':' +#define EMSG "" + + +/* + * getopt_long + * Parse argc/argv argument vector, with long options. + * + * This implementation does not use optreset. Instead, we guarantee that + * it can be restarted on a new argv array after a previous call returned -1, + * if the caller resets optind to 1 before the first call of the new series. + * (Internally, this means we must be sure to reset "place" to EMSG before + * returning -1.) + */ +int +getopt_long(int argc, char *const argv[], + const char *optstring, + const struct option *longopts, int *longindex) +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (!*place) + { /* update scanning pointer */ + if (optind >= argc) + { + place = EMSG; + return -1; + } + + place = argv[optind]; + + if (place[0] != '-') + { + place = EMSG; + return -1; + } + + place++; + + if (!*place) + { + /* treat "-" as not being an option */ + place = EMSG; + return -1; + } + + if (place[0] == '-' && place[1] == '\0') + { + /* found "--", treat it as end of options */ + ++optind; + place = EMSG; + return -1; + } + + if (place[0] == '-' && place[1]) + { + /* long option */ + size_t namelen; + int i; + + place++; + + namelen = strcspn(place, "="); + for (i = 0; longopts[i].name != NULL; i++) + { + if (strlen(longopts[i].name) == namelen + && strncmp(place, longopts[i].name, namelen) == 0) + { + int has_arg = longopts[i].has_arg; + + if (has_arg != no_argument) + { + if (place[namelen] == '=') + optarg = place + namelen + 1; + else if (optind < argc - 1 && + has_arg == required_argument) + { + optind++; + optarg = argv[optind]; + } + else + { + if (optstring[0] == ':') + return BADARG; + + if (opterr && has_arg == required_argument) + fprintf(stderr, + "%s: option requires an argument -- %s\n", + argv[0], place); + + place = EMSG; + optind++; + + if (has_arg == required_argument) + return BADCH; + optarg = NULL; + } + } + else + { + optarg = NULL; + if (place[namelen] != 0) + { + /* XXX error? */ + } + } + + optind++; + + if (longindex) + *longindex = i; + + place = EMSG; + + if (longopts[i].flag == NULL) + return longopts[i].val; + else + { + *longopts[i].flag = longopts[i].val; + return 0; + } + } + } + + if (opterr && optstring[0] != ':') + fprintf(stderr, + "%s: illegal option -- %s\n", argv[0], place); + place = EMSG; + optind++; + return BADCH; + } + } + + /* short option */ + optopt = (int) *place++; + + oli = strchr(optstring, optopt); + if (!oli) + { + if (!*place) + ++optind; + if (opterr && *optstring != ':') + fprintf(stderr, + "%s: illegal option -- %c\n", argv[0], optopt); + return BADCH; + } + + if (oli[1] != ':') + { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else + { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (argc <= ++optind) + { /* no arg */ + place = EMSG; + if (*optstring == ':') + return BADARG; + if (opterr) + fprintf(stderr, + "%s: option requires an argument -- %c\n", + argv[0], optopt); + return BADCH; + } + else + /* white space */ + optarg = argv[optind]; + place = EMSG; + ++optind; + } + return optopt; +} diff --git a/src/port/getpeereid.c b/src/port/getpeereid.c new file mode 100644 index 0000000..3b040e0 --- /dev/null +++ b/src/port/getpeereid.c @@ -0,0 +1,78 @@ +/*------------------------------------------------------------------------- + * + * getpeereid.c + * get peer userid for UNIX-domain socket connection + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/getpeereid.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#ifdef HAVE_UCRED_H +#include <ucred.h> +#endif +#ifdef HAVE_SYS_UCRED_H +#include <sys/ucred.h> +#endif + + +/* + * BSD-style getpeereid() for platforms that lack it. + */ +int +getpeereid(int sock, uid_t *uid, gid_t *gid) +{ +#if defined(SO_PEERCRED) + /* Linux: use getsockopt(SO_PEERCRED) */ + struct ucred peercred; + socklen_t so_len = sizeof(peercred); + + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 || + so_len != sizeof(peercred)) + return -1; + *uid = peercred.uid; + *gid = peercred.gid; + return 0; +#elif defined(LOCAL_PEERCRED) + /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */ + struct xucred peercred; + socklen_t so_len = sizeof(peercred); + + if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 || + so_len != sizeof(peercred) || + peercred.cr_version != XUCRED_VERSION) + return -1; + *uid = peercred.cr_uid; + *gid = peercred.cr_gid; + return 0; +#elif defined(HAVE_GETPEERUCRED) + /* Solaris: use getpeerucred() */ + ucred_t *ucred; + + ucred = NULL; /* must be initialized to NULL */ + if (getpeerucred(sock, &ucred) == -1) + return -1; + + *uid = ucred_geteuid(ucred); + *gid = ucred_getegid(ucred); + ucred_free(ucred); + + if (*uid == (uid_t) (-1) || *gid == (gid_t) (-1)) + return -1; + return 0; +#else + /* No implementation available on this platform */ + errno = ENOSYS; + return -1; +#endif +} diff --git a/src/port/inet_aton.c b/src/port/inet_aton.c new file mode 100644 index 0000000..adaf18a --- /dev/null +++ b/src/port/inet_aton.c @@ -0,0 +1,149 @@ +/* src/port/inet_aton.c + * + * This inet_aton() function was taken from the GNU C library and + * incorporated into Postgres for those systems which do not have this + * routine in their standard C libraries. + * + * The function was been extracted whole from the file inet_aton.c in + * Release 5.3.12 of the Linux C library, which is derived from the + * GNU C library, by Bryan Henderson in October 1996. The copyright + * notice from that file is below. + */ + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include "c.h" + +#include <netinet/in.h> +#include <ctype.h> + +#include "port/pg_bswap.h" + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(const char *cp, struct in_addr *addr) +{ + unsigned int val; + int base, + n; + char c; + u_int parts[4]; + u_int *pp = parts; + + for (;;) + { + /* + * Collect number up to ``.''. Values are specified as for C: 0x=hex, + * 0=octal, other=decimal. + */ + val = 0; + base = 10; + if (*cp == '0') + { + if (*++cp == 'x' || *cp == 'X') + base = 16, cp++; + else + base = 8; + } + while ((c = *cp) != '\0') + { + if (isdigit((unsigned char) c)) + { + val = (val * base) + (c - '0'); + cp++; + continue; + } + if (base == 16 && isxdigit((unsigned char) c)) + { + val = (val << 4) + + (c + 10 - (islower((unsigned char) c) ? 'a' : 'A')); + cp++; + continue; + } + break; + } + if (*cp == '.') + { + /* + * Internet format: a.b.c.d a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xff) + return 0; + *pp++ = val, cp++; + } + else + break; + } + + /* + * Check for trailing junk. + */ + while (*cp) + if (!isspace((unsigned char) *cp++)) + return 0; + + /* + * Concoct the address according to the number of parts specified. + */ + n = pp - parts + 1; + switch (n) + { + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return 0; + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = pg_hton32(val); + return 1; +} diff --git a/src/port/inet_net_ntop.c b/src/port/inet_net_ntop.c new file mode 100644 index 0000000..500c66d --- /dev/null +++ b/src/port/inet_net_ntop.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * src/port/inet_net_ntop.c + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $"; +#endif + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifndef FRONTEND +#include "utils/inet.h" +#else +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that pg_inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) +#endif + + +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 + +#ifdef SPRINTF_CHAR +#define SPRINTF(x) strlen(sprintf/**/x) +#else +#define SPRINTF(x) ((size_t)sprintf x) +#endif + +static char *inet_net_ntop_ipv4(const u_char *src, int bits, + char *dst, size_t size); +static char *inet_net_ntop_ipv6(const u_char *src, int bits, + char *dst, size_t size); + + +/* + * char * + * pg_inet_net_ntop(af, src, bits, dst, size) + * convert host/network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * 192.5.5.1/28 has a nonzero host part, which means it isn't a network + * as called for by pg_inet_net_pton() but it can be a host address with + * an included netmask. + * author: + * Paul Vixie (ISC), October 1998 + */ +char * +pg_inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) +{ + /* + * We need to cover both the address family constants used by the PG inet + * type (PGSQL_AF_INET and PGSQL_AF_INET6) and those used by the system + * libraries (AF_INET and AF_INET6). We can safely assume PGSQL_AF_INET + * == AF_INET, but the INET6 constants are very likely to be different. + */ + switch (af) + { + case PGSQL_AF_INET: + return (inet_net_ntop_ipv4(src, bits, dst, size)); + case PGSQL_AF_INET6: +#if AF_INET6 != PGSQL_AF_INET6 + case AF_INET6: +#endif + return (inet_net_ntop_ipv6(src, bits, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } +} + +/* + * static char * + * inet_net_ntop_ipv4(src, bits, dst, size) + * convert IPv4 network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), October 1998 + */ +static char * +inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) +{ + char *odst = dst; + char *t; + int len = 4; + int b; + + if (bits < 0 || bits > 32) + { + errno = EINVAL; + return (NULL); + } + + /* Always format all four octets, regardless of mask length. */ + for (b = len; b > 0; b--) + { + if (size <= sizeof ".255") + goto emsgsize; + t = dst; + if (dst != odst) + *dst++ = '.'; + dst += SPRINTF((dst, "%u", *src++)); + size -= (size_t) (dst - t); + } + + /* don't print masklen if 32 bits */ + if (bits != 32) + { + if (size <= sizeof "/32") + goto emsgsize; + dst += SPRINTF((dst, "/%u", bits)); + } + + return (odst); + +emsgsize: + errno = EMSGSIZE; + return (NULL); +} + +static int +decoct(const u_char *src, int bytes, char *dst, size_t size) +{ + char *odst = dst; + char *t; + int b; + + for (b = 1; b <= bytes; b++) + { + if (size <= sizeof "255.") + return (0); + t = dst; + dst += SPRINTF((dst, "%u", *src++)); + if (b != bytes) + { + *dst++ = '.'; + *dst = '\0'; + } + size -= (size_t) (dst - t); + } + return (dst - odst); +} + +static char * +inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough to + * contain a value of the specified size. On some systems, like Crays, + * there is no such thing as an integer variable with 16 bits. Keep this + * in mind if you think this function should have been coded to use + * pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"]; + char *tp; + struct + { + int base, + len; + } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + if ((bits < -1) || (bits > 128)) + { + errno = EINVAL; + return (NULL); + } + + /* + * Preprocess: Copy the input (bytewise) array into a wordwise array. Find + * the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + best.len = 0; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } + else + { + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) + { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) + { + int n; + + n = decoct(src + 12, 4, tp, sizeof tmp - (tp - tmp)); + if (n == 0) + { + errno = EMSGSIZE; + return (NULL); + } + tp += strlen(tp); + break; + } + tp += SPRINTF((tp, "%x", words[i])); + } + + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp = '\0'; + + if (bits != -1 && bits != 128) + tp += SPRINTF((tp, "/%u", bits)); + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t) (tp - tmp) > size) + { + errno = EMSGSIZE; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} diff --git a/src/port/kill.c b/src/port/kill.c new file mode 100644 index 0000000..2cf7b92 --- /dev/null +++ b/src/port/kill.c @@ -0,0 +1,97 @@ +/*------------------------------------------------------------------------- + * + * kill.c + * kill() + * + * Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * This is a replacement version of kill for Win32 which sends + * signals that the backend can recognize. + * + * IDENTIFICATION + * src/port/kill.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#ifdef WIN32 +/* signal sending */ +int +pgkill(int pid, int sig) +{ + char pipename[128]; + BYTE sigData = sig; + BYTE sigRet = 0; + DWORD bytes; + + /* we allow signal 0 here, but it will be ignored in pg_queue_signal */ + if (sig >= PG_SIGNAL_COUNT || sig < 0) + { + errno = EINVAL; + return -1; + } + if (pid <= 0) + { + /* No support for process groups */ + errno = EINVAL; + return -1; + } + + /* special case for SIGKILL: just ask the system to terminate the target */ + if (sig == SIGKILL) + { + HANDLE prochandle; + + if ((prochandle = OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) pid)) == NULL) + { + errno = ESRCH; + return -1; + } + if (!TerminateProcess(prochandle, 255)) + { + _dosmaperr(GetLastError()); + CloseHandle(prochandle); + return -1; + } + CloseHandle(prochandle); + return 0; + } + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\pgsignal_%u", pid); + + if (CallNamedPipe(pipename, &sigData, 1, &sigRet, 1, &bytes, 1000)) + { + if (bytes != 1 || sigRet != sig) + { + errno = ESRCH; + return -1; + } + return 0; + } + + switch (GetLastError()) + { + case ERROR_BROKEN_PIPE: + case ERROR_BAD_PIPE: + + /* + * These arise transiently as a process is exiting. Treat them + * like POSIX treats a zombie process, reporting success. + */ + return 0; + + case ERROR_FILE_NOT_FOUND: + /* pipe fully gone, so treat the process as gone */ + errno = ESRCH; + return -1; + case ERROR_ACCESS_DENIED: + errno = EPERM; + return -1; + default: + errno = EINVAL; /* unexpected */ + return -1; + } +} + +#endif diff --git a/src/port/meson.build b/src/port/meson.build new file mode 100644 index 0000000..0a16f1c --- /dev/null +++ b/src/port/meson.build @@ -0,0 +1,197 @@ +# Copyright (c) 2022-2023, PostgreSQL Global Development Group + +pgport_sources = [ + 'bsearch_arg.c', + 'chklocale.c', + 'inet_net_ntop.c', + 'noblock.c', + 'path.c', + 'pg_bitutils.c', + 'pg_strong_random.c', + 'pgcheckdir.c', + 'pgmkdirp.c', + 'pgsleep.c', + 'pgstrcasecmp.c', + 'pgstrsignal.c', + 'pqsignal.c', + 'qsort.c', + 'qsort_arg.c', + 'quotes.c', + 'snprintf.c', + 'strerror.c', + 'tar.c', + 'thread.c', +] + +if host_system == 'windows' + pgport_sources += files( + 'dirmod.c', + 'kill.c', + 'open.c', + 'system.c', + 'win32common.c', + 'win32dlopen.c', + 'win32env.c', + 'win32error.c', + 'win32fdatasync.c', + 'win32fseek.c', + 'win32getrusage.c', + 'win32link.c', + 'win32ntdll.c', + 'win32pread.c', + 'win32pwrite.c', + 'win32security.c', + 'win32setlocale.c', + 'win32stat.c', + ) +elif host_system == 'cygwin' + pgport_sources += files( + 'dirmod.c', + ) +endif + +if cc.get_id() == 'msvc' + pgport_sources += files( + 'dirent.c', + 'win32gettimeofday.c', + ) +endif + +# Replacement functionality to be built if corresponding configure symbol +# is false +replace_funcs_neg = [ + ['explicit_bzero'], + ['getopt'], + ['getopt_long'], + ['getpeereid'], + ['inet_aton'], + ['mkdtemp'], + ['preadv', 'HAVE_DECL_PREADV'], + ['pwritev', 'HAVE_DECL_PWRITEV'], + ['strlcat'], + ['strlcpy'], + ['strnlen'], +] + +if host_system != 'windows' + replace_funcs_neg += [['pthread_barrier_wait']] +endif + +# Replacement functionality to be built if corresponding configure symbol +# is true +replace_funcs_pos = [ + # x86/x64 + ['pg_crc32c_sse42', 'USE_SSE42_CRC32C'], + ['pg_crc32c_sse42', 'USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 'crc'], + ['pg_crc32c_sse42_choose', 'USE_SSE42_CRC32C_WITH_RUNTIME_CHECK'], + ['pg_crc32c_sb8', 'USE_SSE42_CRC32C_WITH_RUNTIME_CHECK'], + + # arm / aarch64 + ['pg_crc32c_armv8', 'USE_ARMV8_CRC32C'], + ['pg_crc32c_armv8', 'USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 'crc'], + ['pg_crc32c_armv8_choose', 'USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK'], + ['pg_crc32c_sb8', 'USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK'], + + # generic fallback + ['pg_crc32c_sb8', 'USE_SLICING_BY_8_CRC32C'], +] + +pgport_cflags = {'crc': cflags_crc} +pgport_sources_cflags = {'crc': []} + +foreach f : replace_funcs_neg + func = f.get(0) + varname = f.get(1, 'HAVE_@0@'.format(func.to_upper())) + filename = '@0@.c'.format(func) + + val = '@0@'.format(cdata.get(varname, 'false')) + if val == 'false' or val == '0' + pgport_sources += files(filename) + endif +endforeach + +foreach f : replace_funcs_pos + func = f.get(0) + varname = f.get(1, 'HAVE_@0@'.format(func.to_upper())) + filename = '@0@.c'.format(func) + + val = '@0@'.format(cdata.get(varname, 'false')) + if val == 'true' or val == '1' + src = files(filename) + if f.length() > 2 + pgport_sources_cflags += {f[2]: pgport_sources_cflags[f[2]] + src} + else + pgport_sources += src + endif + endif +endforeach + + +if (host_system == 'windows' or host_system == 'cygwin') and \ + (cc.get_id() != 'msvc' or cc.version().version_compare('<14.0')) + + # Cygwin and (apparently, based on test results) Mingw both + # have a broken strtof(), so substitute its implementation. + # That's not a perfect fix, since it doesn't avoid double-rounding, + # but we have no better options. + pgport_sources += files('strtof.c') + message('On @0@ with compiler @1@ @2@ we will use our strtof wrapper.'.format( + host_system, cc.get_id(), cc.version())) +endif + + + +# Build pgport once for backend, once for use in frontend binaries, and once +# for use in shared libraries +pgport = {} +pgport_variants = { + '_srv': internal_lib_args + { + 'dependencies': [backend_port_code], + }, + '': default_lib_args + { + 'dependencies': [frontend_port_code], + }, + '_shlib': default_lib_args + { + 'pic': true, + 'dependencies': [frontend_port_code], + }, +} + +foreach name, opts : pgport_variants + + # Build internal static libraries for sets of files that need to be built + # with different cflags + cflag_libs = [] + foreach cflagname, sources : pgport_sources_cflags + if sources.length() == 0 + continue + endif + c_args = opts.get('c_args', []) + pgport_cflags[cflagname] + cflag_libs += static_library('libpgport@0@_@1@'.format(name, cflagname), + sources, + c_pch: pch_c_h, + kwargs: opts + { + 'c_args': c_args, + 'build_by_default': false, + 'install': false, + }, + ) + endforeach + + lib = static_library('libpgport@0@'.format(name), + pgport_sources, + link_with: cflag_libs, + c_pch: pch_c_h, + kwargs: opts + { + 'dependencies': opts['dependencies'] + [ssl], + } + ) + pgport += {name: lib} +endforeach + +pgport_srv = pgport['_srv'] +pgport_static = pgport[''] +pgport_shlib = pgport['_shlib'] + +# autoconf generates the file there, ensure we get a conflict +generated_sources_ac += {'src/port': ['pg_config_paths.h']} diff --git a/src/port/mkdtemp.c b/src/port/mkdtemp.c new file mode 100644 index 0000000..4578e83 --- /dev/null +++ b/src/port/mkdtemp.c @@ -0,0 +1,293 @@ +/*------------------------------------------------------------------------- + * + * mkdtemp.c + * create a mode-0700 temporary directory + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/mkdtemp.c + * + * This code was taken from NetBSD to provide an implementation for platforms + * that lack it. (Among compatibly-licensed implementations, the OpenBSD + * version better resists denial-of-service attacks. However, it has a + * cryptographic dependency.) The NetBSD copyright terms follow. + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#define _DIAGASSERT(x) do {} while (0) + + +/* $NetBSD: gettemp.c,v 1.17 2014/01/21 19:09:48 seanb Exp $ */ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#if !HAVE_NBTOOL_CONFIG_H || !HAVE_MKSTEMP || !HAVE_MKDTEMP + +#ifdef NOT_POSTGRESQL +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: gettemp.c,v 1.17 2014/01/21 19:09:48 seanb Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#ifdef NOT_POSTGRESQL +#if HAVE_NBTOOL_CONFIG_H +#define GETTEMP __nbcompat_gettemp +#else +#include "reentrant.h" +#include "local.h" +#define GETTEMP __gettemp +#endif +#endif + +static int +GETTEMP(char *path, int *doopen, int domkdir) +{ + char *start, + *trv; + struct stat sbuf; + u_int pid; + + /* + * To guarantee multiple calls generate unique names even if the file is + * not created. 676 different possibilities with 7 or more X's, 26 with 6 + * or less. + */ + static char xtra[2] = "aa"; + int xcnt = 0; + + _DIAGASSERT(path != NULL); + /* doopen may be NULL */ + + pid = getpid(); + + /* Move to end of path and count trailing X's. */ + for (trv = path; *trv; ++trv) + if (*trv == 'X') + xcnt++; + else + xcnt = 0; + + /* Use at least one from xtra. Use 2 if more than 6 X's. */ + if (xcnt > 0) + { + *--trv = xtra[0]; + xcnt--; + } + if (xcnt > 5) + { + *--trv = xtra[1]; + xcnt--; + } + + /* Set remaining X's to pid digits with 0's to the left. */ + for (; xcnt > 0; xcnt--) + { + *--trv = (pid % 10) + '0'; + pid /= 10; + } + + /* update xtra for next call. */ + if (xtra[0] != 'z') + xtra[0]++; + else + { + xtra[0] = 'a'; + if (xtra[1] != 'z') + xtra[1]++; + else + xtra[1] = 'a'; + } + + /* + * check the target directory; if you have six X's and it doesn't exist + * this runs for a *very* long time. + */ + for (start = trv + 1;; --trv) + { + if (trv <= path) + break; + if (*trv == '/') + { + int e; + + *trv = '\0'; + e = stat(path, &sbuf); + *trv = '/'; + if (e == -1) + return doopen == NULL && !domkdir; + if (!S_ISDIR(sbuf.st_mode)) + { + errno = ENOTDIR; + return doopen == NULL && !domkdir; + } + break; + } + } + + for (;;) + { + if (doopen) + { + if ((*doopen = + open(path, O_CREAT | O_EXCL | O_RDWR, 0600)) >= 0) + return 1; + if (errno != EEXIST) + return 0; + } + else if (domkdir) + { + if (mkdir(path, 0700) >= 0) + return 1; + if (errno != EEXIST) + return 0; + } + else if (lstat(path, &sbuf)) + return errno == ENOENT ? 1 : 0; + + /* tricky little algorithm for backward compatibility */ + for (trv = start;;) + { + if (!*trv) + return 0; + if (*trv == 'z') + *trv++ = 'a'; + else + { + if (isdigit((unsigned char) *trv)) + *trv = 'a'; + else + ++*trv; + break; + } + } + } + /* NOTREACHED */ +} + +#endif /* !HAVE_NBTOOL_CONFIG_H || !HAVE_MKSTEMP || + * !HAVE_MKDTEMP */ + + +/* $NetBSD: mkdtemp.c,v 1.11 2012/03/15 18:22:30 christos Exp $ */ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#if !HAVE_NBTOOL_CONFIG_H || !HAVE_MKDTEMP + +#ifdef NOT_POSTGRESQL + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: mkdtemp.c,v 1.11 2012/03/15 18:22:30 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#if HAVE_NBTOOL_CONFIG_H +#define GETTEMP __nbcompat_gettemp +#else +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "reentrant.h" +#include "local.h" +#define GETTEMP __gettemp +#endif + +#endif + +char * +mkdtemp(char *path) +{ + _DIAGASSERT(path != NULL); + + return GETTEMP(path, NULL, 1) ? path : NULL; +} + +#endif /* !HAVE_NBTOOL_CONFIG_H || !HAVE_MKDTEMP */ diff --git a/src/port/noblock.c b/src/port/noblock.c new file mode 100644 index 0000000..d050a03 --- /dev/null +++ b/src/port/noblock.c @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- + * + * noblock.c + * set a file descriptor as blocking or non-blocking + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/noblock.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <fcntl.h> + + +/* + * Put socket into nonblock mode. + * Returns true on success, false on failure. + */ +bool +pg_set_noblock(pgsocket sock) +{ +#if !defined(WIN32) + int flags; + + flags = fcntl(sock, F_GETFL); + if (flags < 0) + return false; + if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) == -1) + return false; + return true; +#else + unsigned long ioctlsocket_ret = 1; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} + +/* + * Put socket into blocking mode. + * Returns true on success, false on failure. + */ +bool +pg_set_block(pgsocket sock) +{ +#if !defined(WIN32) + int flags; + + flags = fcntl(sock, F_GETFL); + if (flags < 0) + return false; + if (fcntl(sock, F_SETFL, (flags & ~O_NONBLOCK)) == -1) + return false; + return true; +#else + unsigned long ioctlsocket_ret = 0; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} diff --git a/src/port/open.c b/src/port/open.c new file mode 100644 index 0000000..0bd9755 --- /dev/null +++ b/src/port/open.c @@ -0,0 +1,222 @@ +/*------------------------------------------------------------------------- + * + * open.c + * Win32 open() replacement + * + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * src/port/open.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include "port/win32ntdll.h" + +#include <fcntl.h> +#include <assert.h> +#include <sys/stat.h> + +static int +openFlagsToCreateFileFlags(int openFlags) +{ + switch (openFlags & (O_CREAT | O_TRUNC | O_EXCL)) + { + /* O_EXCL is meaningless without O_CREAT */ + case 0: + case O_EXCL: + return OPEN_EXISTING; + + case O_CREAT: + return OPEN_ALWAYS; + + /* O_EXCL is meaningless without O_CREAT */ + case O_TRUNC: + case O_TRUNC | O_EXCL: + return TRUNCATE_EXISTING; + + case O_CREAT | O_TRUNC: + return CREATE_ALWAYS; + + /* O_TRUNC is meaningless with O_CREAT */ + case O_CREAT | O_EXCL: + case O_CREAT | O_TRUNC | O_EXCL: + return CREATE_NEW; + } + + /* will never get here */ + return 0; +} + +/* + * Internal function used by pgwin32_open() and _pgstat64(). When + * backup_semantics is true, directories may be opened (for limited uses). On + * failure, INVALID_HANDLE_VALUE is returned and errno is set. + */ +HANDLE +pgwin32_open_handle(const char *fileName, int fileFlags, bool backup_semantics) +{ + HANDLE h; + SECURITY_ATTRIBUTES sa; + int loops = 0; + + if (initialize_ntdll() < 0) + return INVALID_HANDLE_VALUE; + + /* Check that we can handle the request */ + assert((fileFlags & ((O_RDONLY | O_WRONLY | O_RDWR) | O_APPEND | + (O_RANDOM | O_SEQUENTIAL | O_TEMPORARY) | + _O_SHORT_LIVED | O_DSYNC | O_DIRECT | + (O_CREAT | O_TRUNC | O_EXCL) | (O_TEXT | O_BINARY))) == fileFlags); + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + while ((h = CreateFile(fileName, + /* cannot use O_RDONLY, as it == 0 */ + (fileFlags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) : + ((fileFlags & O_WRONLY) ? GENERIC_WRITE : GENERIC_READ), + /* These flags allow concurrent rename/unlink */ + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + &sa, + openFlagsToCreateFileFlags(fileFlags), + FILE_ATTRIBUTE_NORMAL | + (backup_semantics ? FILE_FLAG_BACKUP_SEMANTICS : 0) | + ((fileFlags & O_RANDOM) ? FILE_FLAG_RANDOM_ACCESS : 0) | + ((fileFlags & O_SEQUENTIAL) ? FILE_FLAG_SEQUENTIAL_SCAN : 0) | + ((fileFlags & _O_SHORT_LIVED) ? FILE_ATTRIBUTE_TEMPORARY : 0) | + ((fileFlags & O_TEMPORARY) ? FILE_FLAG_DELETE_ON_CLOSE : 0) | + ((fileFlags & O_DIRECT) ? FILE_FLAG_NO_BUFFERING : 0) | + ((fileFlags & O_DSYNC) ? FILE_FLAG_WRITE_THROUGH : 0), + NULL)) == INVALID_HANDLE_VALUE) + { + /* + * Sharing violation or locking error can indicate antivirus, backup + * or similar software that's locking the file. Wait a bit and try + * again, giving up after 30 seconds. + */ + DWORD err = GetLastError(); + + if (err == ERROR_SHARING_VIOLATION || + err == ERROR_LOCK_VIOLATION) + { +#ifndef FRONTEND + if (loops == 50) + ereport(LOG, + (errmsg("could not open file \"%s\": %s", fileName, + (err == ERROR_SHARING_VIOLATION) ? _("sharing violation") : _("lock violation")), + errdetail("Continuing to retry for 30 seconds."), + errhint("You might have antivirus, backup, or similar software interfering with the database system."))); +#endif + + if (loops < 300) + { + pg_usleep(100000); + loops++; + continue; + } + } + + /* + * ERROR_ACCESS_DENIED is returned if the file is deleted but not yet + * gone (Windows NT status code is STATUS_DELETE_PENDING). In that + * case, we'd better ask for the NT status too so we can translate it + * to a more Unix-like error. We hope that nothing clobbers the NT + * status in between the internal NtCreateFile() call and CreateFile() + * returning. + * + * If there's no O_CREAT flag, then we'll pretend the file is + * invisible. With O_CREAT, we have no choice but to report that + * there's a file in the way (which wouldn't happen on Unix). + */ + if (err == ERROR_ACCESS_DENIED && + pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING) + { + if (fileFlags & O_CREAT) + err = ERROR_FILE_EXISTS; + else + err = ERROR_FILE_NOT_FOUND; + } + + _dosmaperr(err); + return INVALID_HANDLE_VALUE; + } + + return h; +} + +int +pgwin32_open(const char *fileName, int fileFlags,...) +{ + HANDLE h; + int fd; + + h = pgwin32_open_handle(fileName, fileFlags, false); + if (h == INVALID_HANDLE_VALUE) + return -1; + +#ifdef FRONTEND + + /* + * Since PostgreSQL 12, those concurrent-safe versions of open() and + * fopen() can be used by frontends, having as side-effect to switch the + * file-translation mode from O_TEXT to O_BINARY if none is specified. + * Caller may want to enforce the binary or text mode, but if nothing is + * defined make sure that the default mode maps with what versions older + * than 12 have been doing. + */ + if ((fileFlags & O_BINARY) == 0) + fileFlags |= O_TEXT; +#endif + + /* _open_osfhandle will, on error, set errno accordingly */ + if ((fd = _open_osfhandle((intptr_t) h, fileFlags & O_APPEND)) < 0) + CloseHandle(h); /* will not affect errno */ + else if (fileFlags & (O_TEXT | O_BINARY) && + _setmode(fd, fileFlags & (O_TEXT | O_BINARY)) < 0) + { + _close(fd); + return -1; + } + + return fd; +} + +FILE * +pgwin32_fopen(const char *fileName, const char *mode) +{ + int openmode = 0; + int fd; + + if (strstr(mode, "r+")) + openmode |= O_RDWR; + else if (strchr(mode, 'r')) + openmode |= O_RDONLY; + if (strstr(mode, "w+")) + openmode |= O_RDWR | O_CREAT | O_TRUNC; + else if (strchr(mode, 'w')) + openmode |= O_WRONLY | O_CREAT | O_TRUNC; + if (strchr(mode, 'a')) + openmode |= O_WRONLY | O_CREAT | O_APPEND; + + if (strchr(mode, 'b')) + openmode |= O_BINARY; + if (strchr(mode, 't')) + openmode |= O_TEXT; + + fd = pgwin32_open(fileName, openmode); + if (fd == -1) + return NULL; + return _fdopen(fd, mode); +} + +#endif diff --git a/src/port/path.c b/src/port/path.c new file mode 100644 index 0000000..65c7943 --- /dev/null +++ b/src/port/path.c @@ -0,0 +1,1057 @@ +/*------------------------------------------------------------------------- + * + * path.c + * portable path handling routines + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/path.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include <ctype.h> +#include <sys/stat.h> +#ifdef WIN32 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0500 +#ifdef near +#undef near +#endif +#define near +#include <shlobj.h> +#else +#include <unistd.h> +#endif + +#include "pg_config_paths.h" + + +#ifndef WIN32 +#define IS_PATH_VAR_SEP(ch) ((ch) == ':') +#else +#define IS_PATH_VAR_SEP(ch) ((ch) == ';') +#endif + +static void make_relative_path(char *ret_path, const char *target_path, + const char *bin_path, const char *my_exec_path); +static char *trim_directory(char *path); +static void trim_trailing_separator(char *path); +static char *append_subdir_to_path(char *path, char *subdir); + + +/* + * skip_drive + * + * On Windows, a path may begin with "C:" or "//network/". Advance over + * this and point to the effective start of the path. + */ +#ifdef WIN32 + +static char * +skip_drive(const char *path) +{ + if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1])) + { + path += 2; + while (*path && !IS_DIR_SEP(*path)) + path++; + } + else if (isalpha((unsigned char) path[0]) && path[1] == ':') + { + path += 2; + } + return (char *) path; +} +#else + +#define skip_drive(path) (path) +#endif + +/* + * has_drive_prefix + * + * Return true if the given pathname has a drive prefix. + */ +bool +has_drive_prefix(const char *path) +{ +#ifdef WIN32 + return skip_drive(path) != path; +#else + return false; +#endif +} + +/* + * first_dir_separator + * + * Find the location of the first directory separator, return + * NULL if not found. + */ +char * +first_dir_separator(const char *filename) +{ + const char *p; + + for (p = skip_drive(filename); *p; p++) + if (IS_DIR_SEP(*p)) + return unconstify(char *, p); + return NULL; +} + +/* + * first_path_var_separator + * + * Find the location of the first path separator (i.e. ':' on + * Unix, ';' on Windows), return NULL if not found. + */ +char * +first_path_var_separator(const char *pathlist) +{ + const char *p; + + /* skip_drive is not needed */ + for (p = pathlist; *p; p++) + if (IS_PATH_VAR_SEP(*p)) + return unconstify(char *, p); + return NULL; +} + +/* + * last_dir_separator + * + * Find the location of the last directory separator, return + * NULL if not found. + */ +char * +last_dir_separator(const char *filename) +{ + const char *p, + *ret = NULL; + + for (p = skip_drive(filename); *p; p++) + if (IS_DIR_SEP(*p)) + ret = p; + return unconstify(char *, ret); +} + + +/* + * make_native_path - on WIN32, change / to \ in the path + * + * This effectively undoes canonicalize_path. + * + * This is required because WIN32 COPY is an internal CMD.EXE + * command and doesn't process forward slashes in the same way + * as external commands. Quoting the first argument to COPY + * does not convert forward to backward slashes, but COPY does + * properly process quoted forward slashes in the second argument. + * + * COPY works with quoted forward slashes in the first argument + * only if the current directory is the same as the directory + * of the first argument. + */ +void +make_native_path(char *filename) +{ +#ifdef WIN32 + char *p; + + for (p = filename; *p; p++) + if (*p == '/') + *p = '\\'; +#endif +} + + +/* + * This function cleans up the paths for use with either cmd.exe or Msys + * on Windows. We need them to use filenames without spaces, for which a + * short filename is the safest equivalent, eg: + * C:/Progra~1/ + */ +void +cleanup_path(char *path) +{ +#ifdef WIN32 + char *ptr; + + /* + * GetShortPathName() will fail if the path does not exist, or short names + * are disabled on this file system. In both cases, we just return the + * original path. This is particularly useful for --sysconfdir, which + * might not exist. + */ + GetShortPathName(path, path, MAXPGPATH - 1); + + /* Replace '\' with '/' */ + for (ptr = path; *ptr; ptr++) + { + if (*ptr == '\\') + *ptr = '/'; + } +#endif +} + + +/* + * join_path_components - join two path components, inserting a slash + * + * We omit the slash if either given component is empty. + * + * ret_path is the output area (must be of size MAXPGPATH) + * + * ret_path can be the same as head, but not the same as tail. + */ +void +join_path_components(char *ret_path, + const char *head, const char *tail) +{ + if (ret_path != head) + strlcpy(ret_path, head, MAXPGPATH); + + /* + * We used to try to simplify some cases involving "." and "..", but now + * we just leave that to be done by canonicalize_path() later. + */ + + if (*tail) + { + /* only separate with slash if head wasn't empty */ + snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path), + "%s%s", + (*(skip_drive(head)) != '\0') ? "/" : "", + tail); + } +} + + +/* State-machine states for canonicalize_path */ +typedef enum +{ + ABSOLUTE_PATH_INIT, /* Just past the leading '/' (and Windows + * drive name if any) of an absolute path */ + ABSOLUTE_WITH_N_DEPTH, /* We collected 'pathdepth' directories in an + * absolute path */ + RELATIVE_PATH_INIT, /* At start of a relative path */ + RELATIVE_WITH_N_DEPTH, /* We collected 'pathdepth' directories in a + * relative path */ + RELATIVE_WITH_PARENT_REF /* Relative path containing only double-dots */ +} canonicalize_state; + +/* + * Clean up path by: + * o make Win32 path use Unix slashes + * o remove trailing quote on Win32 + * o remove trailing slash + * o remove duplicate (adjacent) separators + * o remove '.' (unless path reduces to only '.') + * o process '..' ourselves, removing it if possible + */ +void +canonicalize_path(char *path) +{ + char *p, + *to_p; + char *spath; + char *parsed; + char *unparse; + bool was_sep = false; + canonicalize_state state; + int pathdepth = 0; /* counts collected regular directory names */ + +#ifdef WIN32 + + /* + * The Windows command processor will accept suitably quoted paths with + * forward slashes, but barfs badly with mixed forward and back slashes. + */ + for (p = path; *p; p++) + { + if (*p == '\\') + *p = '/'; + } + + /* + * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d" + * as argv[2], so trim off trailing quote. + */ + if (p > path && *(p - 1) == '"') + *(p - 1) = '/'; +#endif + + /* + * Removing the trailing slash on a path means we never get ugly double + * trailing slashes. Also, Win32 can't stat() a directory with a trailing + * slash. Don't remove a leading slash, though. + */ + trim_trailing_separator(path); + + /* + * Remove duplicate adjacent separators + */ + p = path; +#ifdef WIN32 + /* Don't remove leading double-slash on Win32 */ + if (*p) + p++; +#endif + to_p = p; + for (; *p; p++, to_p++) + { + /* Handle many adjacent slashes, like "/a///b" */ + while (*p == '/' && was_sep) + p++; + if (to_p != p) + *to_p = *p; + was_sep = (*p == '/'); + } + *to_p = '\0'; + + /* + * Remove any uses of "." and process ".." ourselves + * + * Note that "/../.." should reduce to just "/", while "../.." has to be + * kept as-is. Also note that we want a Windows drive spec to be visible + * to trim_directory(), but it's not part of the logic that's looking at + * the name components; hence distinction between path and spath. + * + * This loop overwrites the path in-place. This is safe since we'll never + * make the path longer. "unparse" points to where we are reading the + * path, "parse" to where we are writing. + */ + spath = skip_drive(path); + if (*spath == '\0') + return; /* empty path is returned as-is */ + + if (*spath == '/') + { + state = ABSOLUTE_PATH_INIT; + /* Skip the leading slash for absolute path */ + parsed = unparse = (spath + 1); + } + else + { + state = RELATIVE_PATH_INIT; + parsed = unparse = spath; + } + + while (*unparse != '\0') + { + char *unparse_next; + bool is_double_dot; + + /* Split off this dir name, and set unparse_next to the next one */ + unparse_next = unparse; + while (*unparse_next && *unparse_next != '/') + unparse_next++; + if (*unparse_next != '\0') + *unparse_next++ = '\0'; + + /* Identify type of this dir name */ + if (strcmp(unparse, ".") == 0) + { + /* We can ignore "." components in all cases */ + unparse = unparse_next; + continue; + } + + if (strcmp(unparse, "..") == 0) + is_double_dot = true; + else + { + /* adjacent separators were eliminated above */ + Assert(*unparse != '\0'); + is_double_dot = false; + } + + switch (state) + { + case ABSOLUTE_PATH_INIT: + /* We can ignore ".." immediately after / */ + if (!is_double_dot) + { + /* Append first dir name (we already have leading slash) */ + parsed = append_subdir_to_path(parsed, unparse); + state = ABSOLUTE_WITH_N_DEPTH; + pathdepth++; + } + break; + case ABSOLUTE_WITH_N_DEPTH: + if (is_double_dot) + { + /* Remove last parsed dir */ + /* (trim_directory won't remove the leading slash) */ + *parsed = '\0'; + parsed = trim_directory(path); + if (--pathdepth == 0) + state = ABSOLUTE_PATH_INIT; + } + else + { + /* Append normal dir */ + *parsed++ = '/'; + parsed = append_subdir_to_path(parsed, unparse); + pathdepth++; + } + break; + case RELATIVE_PATH_INIT: + if (is_double_dot) + { + /* Append irreducible double-dot (..) */ + parsed = append_subdir_to_path(parsed, unparse); + state = RELATIVE_WITH_PARENT_REF; + } + else + { + /* Append normal dir */ + parsed = append_subdir_to_path(parsed, unparse); + state = RELATIVE_WITH_N_DEPTH; + pathdepth++; + } + break; + case RELATIVE_WITH_N_DEPTH: + if (is_double_dot) + { + /* Remove last parsed dir */ + *parsed = '\0'; + parsed = trim_directory(path); + if (--pathdepth == 0) + { + /* + * If the output path is now empty, we're back to the + * INIT state. However, we could have processed a + * path like "../dir/.." and now be down to "..", in + * which case enter the correct state for that. + */ + if (parsed == spath) + state = RELATIVE_PATH_INIT; + else + state = RELATIVE_WITH_PARENT_REF; + } + } + else + { + /* Append normal dir */ + *parsed++ = '/'; + parsed = append_subdir_to_path(parsed, unparse); + pathdepth++; + } + break; + case RELATIVE_WITH_PARENT_REF: + if (is_double_dot) + { + /* Append next irreducible double-dot (..) */ + *parsed++ = '/'; + parsed = append_subdir_to_path(parsed, unparse); + } + else + { + /* Append normal dir */ + *parsed++ = '/'; + parsed = append_subdir_to_path(parsed, unparse); + + /* + * We can now start counting normal dirs. But if later + * double-dots make us remove this dir again, we'd better + * revert to RELATIVE_WITH_PARENT_REF not INIT state. + */ + state = RELATIVE_WITH_N_DEPTH; + pathdepth = 1; + } + break; + } + + unparse = unparse_next; + } + + /* + * If our output path is empty at this point, insert ".". We don't want + * to do this any earlier because it'd result in an extra dot in corner + * cases such as "../dir/..". Since we rejected the wholly-empty-path + * case above, there is certainly room. + */ + if (parsed == spath) + *parsed++ = '.'; + + /* And finally, ensure the output path is nul-terminated. */ + *parsed = '\0'; +} + +/* + * Detect whether a path contains any parent-directory references ("..") + * + * The input *must* have been put through canonicalize_path previously. + */ +bool +path_contains_parent_reference(const char *path) +{ + /* + * Once canonicalized, an absolute path cannot contain any ".." at all, + * while a relative path could contain ".."(s) only at the start. So it + * is sufficient to check the start of the path, after skipping any + * Windows drive/network specifier. + */ + path = skip_drive(path); /* C: shouldn't affect our conclusion */ + + if (path[0] == '.' && + path[1] == '.' && + (path[2] == '\0' || path[2] == '/')) + return true; + + return false; +} + +/* + * Detect whether a path is only in or below the current working directory. + * + * The input *must* have been put through canonicalize_path previously. + * + * An absolute path that matches the current working directory should + * return false (we only want relative to the cwd). + */ +bool +path_is_relative_and_below_cwd(const char *path) +{ + if (is_absolute_path(path)) + return false; + /* don't allow anything above the cwd */ + else if (path_contains_parent_reference(path)) + return false; +#ifdef WIN32 + + /* + * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is + * relative to the cwd on that drive, or the drive's root directory if + * that drive has no cwd. Because the path itself cannot tell us which is + * the case, we have to assume the worst, i.e. that it is not below the + * cwd. We could use GetFullPathName() to find the full path but that + * could change if the current directory for the drive changes underneath + * us, so we just disallow it. + */ + else if (isalpha((unsigned char) path[0]) && path[1] == ':' && + !IS_DIR_SEP(path[2])) + return false; +#endif + else + return true; +} + +/* + * Detect whether path1 is a prefix of path2 (including equality). + * + * This is pretty trivial, but it seems better to export a function than + * to export IS_DIR_SEP. + */ +bool +path_is_prefix_of_path(const char *path1, const char *path2) +{ + int path1_len = strlen(path1); + + if (strncmp(path1, path2, path1_len) == 0 && + (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0')) + return true; + return false; +} + +/* + * Extracts the actual name of the program as called - + * stripped of .exe suffix if any + */ +const char * +get_progname(const char *argv0) +{ + const char *nodir_name; + char *progname; + + nodir_name = last_dir_separator(argv0); + if (nodir_name) + nodir_name++; + else + nodir_name = skip_drive(argv0); + + /* + * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but + * called only once. + */ + progname = strdup(nodir_name); + if (progname == NULL) + { + fprintf(stderr, "%s: out of memory\n", nodir_name); + abort(); /* This could exit the postmaster */ + } + +#if defined(__CYGWIN__) || defined(WIN32) + /* strip ".exe" suffix, regardless of case */ + if (strlen(progname) > sizeof(EXE) - 1 && + pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0) + progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0'; +#endif + + return progname; +} + + +/* + * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal, + * and we honor filesystem case insensitivity if known + */ +static int +dir_strcmp(const char *s1, const char *s2) +{ + while (*s1 && *s2) + { + if ( +#ifndef WIN32 + *s1 != *s2 +#else + /* On windows, paths are case-insensitive */ + pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2) +#endif + && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2))) + return (int) *s1 - (int) *s2; + s1++, s2++; + } + if (*s1) + return 1; /* s1 longer */ + if (*s2) + return -1; /* s2 longer */ + return 0; +} + + +/* + * make_relative_path - make a path relative to the actual binary location + * + * This function exists to support relocation of installation trees. + * + * ret_path is the output area (must be of size MAXPGPATH) + * target_path is the compiled-in path to the directory we want to find + * bin_path is the compiled-in path to the directory of executables + * my_exec_path is the actual location of my executable + * + * We determine the common prefix of target_path and bin_path, then compare + * the remainder of bin_path to the last directory component(s) of + * my_exec_path. If they match, build the result as the part of my_exec_path + * preceding the match, joined to the remainder of target_path. If no match, + * return target_path as-is. + * + * For example: + * target_path = '/usr/local/share/postgresql' + * bin_path = '/usr/local/bin' + * my_exec_path = '/opt/pgsql/bin/postgres' + * Given these inputs, the common prefix is '/usr/local/', the tail of + * bin_path is 'bin' which does match the last directory component of + * my_exec_path, so we would return '/opt/pgsql/share/postgresql' + */ +static void +make_relative_path(char *ret_path, const char *target_path, + const char *bin_path, const char *my_exec_path) +{ + int prefix_len; + int tail_start; + int tail_len; + int i; + + /* + * Determine the common prefix --- note we require it to end on a + * directory separator, consider eg '/usr/lib' and '/usr/libexec'. + */ + prefix_len = 0; + for (i = 0; target_path[i] && bin_path[i]; i++) + { + if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i])) + prefix_len = i + 1; + else if (target_path[i] != bin_path[i]) + break; + } + if (prefix_len == 0) + goto no_match; /* no common prefix? */ + tail_len = strlen(bin_path) - prefix_len; + + /* + * Set up my_exec_path without the actual executable name, and + * canonicalize to simplify comparison to bin_path. + */ + strlcpy(ret_path, my_exec_path, MAXPGPATH); + trim_directory(ret_path); /* remove my executable name */ + canonicalize_path(ret_path); + + /* + * Tail match? + */ + tail_start = (int) strlen(ret_path) - tail_len; + if (tail_start > 0 && + IS_DIR_SEP(ret_path[tail_start - 1]) && + dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0) + { + ret_path[tail_start] = '\0'; + trim_trailing_separator(ret_path); + join_path_components(ret_path, ret_path, target_path + prefix_len); + canonicalize_path(ret_path); + return; + } + +no_match: + strlcpy(ret_path, target_path, MAXPGPATH); + canonicalize_path(ret_path); +} + + +/* + * make_absolute_path + * + * If the given pathname isn't already absolute, make it so, interpreting + * it relative to the current working directory. + * + * Also canonicalizes the path. The result is always a malloc'd copy. + * + * In backend, failure cases result in ereport(ERROR); in frontend, + * we write a complaint on stderr and return NULL. + * + * Note: interpretation of relative-path arguments during postmaster startup + * should happen before doing ChangeToDataDir(), else the user will probably + * not like the results. + */ +char * +make_absolute_path(const char *path) +{ + char *new; + + /* Returning null for null input is convenient for some callers */ + if (path == NULL) + return NULL; + + if (!is_absolute_path(path)) + { + char *buf; + size_t buflen; + + buflen = MAXPGPATH; + for (;;) + { + buf = malloc(buflen); + if (!buf) + { +#ifndef FRONTEND + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); +#else + fprintf(stderr, _("out of memory\n")); + return NULL; +#endif + } + + if (getcwd(buf, buflen)) + break; + else if (errno == ERANGE) + { + free(buf); + buflen *= 2; + continue; + } + else + { + int save_errno = errno; + + free(buf); + errno = save_errno; +#ifndef FRONTEND + elog(ERROR, "could not get current working directory: %m"); +#else + fprintf(stderr, _("could not get current working directory: %s\n"), + strerror(errno)); + return NULL; +#endif + } + } + + new = malloc(strlen(buf) + strlen(path) + 2); + if (!new) + { + free(buf); +#ifndef FRONTEND + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); +#else + fprintf(stderr, _("out of memory\n")); + return NULL; +#endif + } + sprintf(new, "%s/%s", buf, path); + free(buf); + } + else + { + new = strdup(path); + if (!new) + { +#ifndef FRONTEND + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); +#else + fprintf(stderr, _("out of memory\n")); + return NULL; +#endif + } + } + + /* Make sure punctuation is canonical, too */ + canonicalize_path(new); + + return new; +} + + +/* + * get_share_path + */ +void +get_share_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path); +} + +/* + * get_etc_path + */ +void +get_etc_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path); +} + +/* + * get_include_path + */ +void +get_include_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path); +} + +/* + * get_pkginclude_path + */ +void +get_pkginclude_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path); +} + +/* + * get_includeserver_path + */ +void +get_includeserver_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path); +} + +/* + * get_lib_path + */ +void +get_lib_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path); +} + +/* + * get_pkglib_path + */ +void +get_pkglib_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path); +} + +/* + * get_locale_path + */ +void +get_locale_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path); +} + +/* + * get_doc_path + */ +void +get_doc_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path); +} + +/* + * get_html_path + */ +void +get_html_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path); +} + +/* + * get_man_path + */ +void +get_man_path(const char *my_exec_path, char *ret_path) +{ + make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path); +} + + +/* + * get_home_path + * + * On Unix, this actually returns the user's home directory. On Windows + * it returns the PostgreSQL-specific application data folder. + */ +bool +get_home_path(char *ret_path) +{ +#ifndef WIN32 + /* + * We first consult $HOME. If that's unset, try to get the info from + * <pwd.h>. + */ + const char *home; + + home = getenv("HOME"); + if (home == NULL || home[0] == '\0') + return pg_get_user_home_dir(geteuid(), ret_path, MAXPGPATH); + strlcpy(ret_path, home, MAXPGPATH); + return true; +#else + char *tmppath; + + /* + * Note: We use getenv() here because the more modern SHGetFolderPath() + * would force the backend to link with shell32.lib, which eats valuable + * desktop heap. XXX This function is used only in psql, which already + * brings in shell32 via libpq. Moving this function to its own file + * would keep it out of the backend, freeing it from this concern. + */ + tmppath = getenv("APPDATA"); + if (!tmppath) + return false; + snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath); + return true; +#endif +} + + +/* + * get_parent_directory + * + * Modify the given string in-place to name the parent directory of the + * named file. + * + * If the input is just a file name with no directory part, the result is + * an empty string, not ".". This is appropriate when the next step is + * join_path_components(), but might need special handling otherwise. + * + * Caution: this will not produce desirable results if the string ends + * with "..". For most callers this is not a problem since the string + * is already known to name a regular file. If in doubt, apply + * canonicalize_path() first. + */ +void +get_parent_directory(char *path) +{ + trim_directory(path); +} + + +/* + * trim_directory + * + * Trim trailing directory from path, that is, remove any trailing slashes, + * the last pathname component, and the slash just ahead of it --- but never + * remove a leading slash. + * + * For the convenience of canonicalize_path, the path's new end location + * is returned. + */ +static char * +trim_directory(char *path) +{ + char *p; + + path = skip_drive(path); + + if (path[0] == '\0') + return path; + + /* back up over trailing slash(es) */ + for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--) + ; + /* back up over directory name */ + for (; !IS_DIR_SEP(*p) && p > path; p--) + ; + /* if multiple slashes before directory name, remove 'em all */ + for (; p > path && IS_DIR_SEP(*(p - 1)); p--) + ; + /* don't erase a leading slash */ + if (p == path && IS_DIR_SEP(*p)) + p++; + *p = '\0'; + return p; +} + + +/* + * trim_trailing_separator + * + * trim off trailing slashes, but not a leading slash + */ +static void +trim_trailing_separator(char *path) +{ + char *p; + + path = skip_drive(path); + p = path + strlen(path); + if (p > path) + for (p--; p > path && IS_DIR_SEP(*p); p--) + *p = '\0'; +} + +/* + * append_subdir_to_path + * + * Append the currently-considered subdirectory name to the output + * path in canonicalize_path. Return the new end location of the + * output path. + * + * Since canonicalize_path updates the path in-place, we must use + * memmove not memcpy, and we don't yet terminate the path with '\0'. + */ +static char * +append_subdir_to_path(char *path, char *subdir) +{ + size_t len = strlen(subdir); + + /* No need to copy data if path and subdir are the same. */ + if (path != subdir) + memmove(path, subdir, len); + + return path + len; +} diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c new file mode 100644 index 0000000..1f3dea2 --- /dev/null +++ b/src/port/pg_bitutils.c @@ -0,0 +1,335 @@ +/*------------------------------------------------------------------------- + * + * pg_bitutils.c + * Miscellaneous functions for bit-wise operations. + * + * Copyright (c) 2019-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/pg_bitutils.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#ifdef HAVE__GET_CPUID +#include <cpuid.h> +#endif +#ifdef HAVE__CPUID +#include <intrin.h> +#endif + +#include "port/pg_bitutils.h" + + +/* + * Array giving the position of the left-most set bit for each possible + * byte value. We count the right-most position as the 0th bit, and the + * left-most the 7th bit. The 0th entry of the array should not be used. + * + * Note: this is not used by the functions in pg_bitutils.h when + * HAVE__BUILTIN_CLZ is defined, but we provide it anyway, so that + * extensions possibly compiled with a different compiler can use it. + */ +const uint8 pg_leftmost_one_pos[256] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 +}; + +/* + * Array giving the position of the right-most set bit for each possible + * byte value. We count the right-most position as the 0th bit, and the + * left-most the 7th bit. The 0th entry of the array should not be used. + * + * Note: this is not used by the functions in pg_bitutils.h when + * HAVE__BUILTIN_CTZ is defined, but we provide it anyway, so that + * extensions possibly compiled with a different compiler can use it. + */ +const uint8 pg_rightmost_one_pos[256] = { + 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* + * Array giving the number of 1-bits in each possible byte value. + * + * Note: we export this for use by functions in which explicit use + * of the popcount functions seems unlikely to be a win. + */ +const uint8 pg_number_of_ones[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +static int pg_popcount32_slow(uint32 word); +static int pg_popcount64_slow(uint64 word); + +#ifdef TRY_POPCNT_FAST +static bool pg_popcount_available(void); +static int pg_popcount32_choose(uint32 word); +static int pg_popcount64_choose(uint64 word); +static int pg_popcount32_fast(uint32 word); +static int pg_popcount64_fast(uint64 word); + +int (*pg_popcount32) (uint32 word) = pg_popcount32_choose; +int (*pg_popcount64) (uint64 word) = pg_popcount64_choose; +#endif /* TRY_POPCNT_FAST */ + +#ifdef TRY_POPCNT_FAST + +/* + * Return true if CPUID indicates that the POPCNT instruction is available. + */ +static bool +pg_popcount_available(void) +{ + unsigned int exx[4] = {0, 0, 0, 0}; + +#if defined(HAVE__GET_CPUID) + __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]); +#elif defined(HAVE__CPUID) + __cpuid(exx, 1); +#else +#error cpuid instruction not available +#endif + + return (exx[2] & (1 << 23)) != 0; /* POPCNT */ +} + +/* + * These functions get called on the first call to pg_popcount32 etc. + * They detect whether we can use the asm implementations, and replace + * the function pointers so that subsequent calls are routed directly to + * the chosen implementation. + */ +static int +pg_popcount32_choose(uint32 word) +{ + if (pg_popcount_available()) + { + pg_popcount32 = pg_popcount32_fast; + pg_popcount64 = pg_popcount64_fast; + } + else + { + pg_popcount32 = pg_popcount32_slow; + pg_popcount64 = pg_popcount64_slow; + } + + return pg_popcount32(word); +} + +static int +pg_popcount64_choose(uint64 word) +{ + if (pg_popcount_available()) + { + pg_popcount32 = pg_popcount32_fast; + pg_popcount64 = pg_popcount64_fast; + } + else + { + pg_popcount32 = pg_popcount32_slow; + pg_popcount64 = pg_popcount64_slow; + } + + return pg_popcount64(word); +} + +/* + * pg_popcount32_fast + * Return the number of 1 bits set in word + */ +static int +pg_popcount32_fast(uint32 word) +{ +#ifdef _MSC_VER + return __popcnt(word); +#else + uint32 res; + +__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc"); + return (int) res; +#endif +} + +/* + * pg_popcount64_fast + * Return the number of 1 bits set in word + */ +static int +pg_popcount64_fast(uint64 word) +{ +#ifdef _MSC_VER + return __popcnt64(word); +#else + uint64 res; + +__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc"); + return (int) res; +#endif +} + +#endif /* TRY_POPCNT_FAST */ + + +/* + * pg_popcount32_slow + * Return the number of 1 bits set in word + */ +static int +pg_popcount32_slow(uint32 word) +{ +#ifdef HAVE__BUILTIN_POPCOUNT + return __builtin_popcount(word); +#else /* !HAVE__BUILTIN_POPCOUNT */ + int result = 0; + + while (word != 0) + { + result += pg_number_of_ones[word & 255]; + word >>= 8; + } + + return result; +#endif /* HAVE__BUILTIN_POPCOUNT */ +} + +/* + * pg_popcount64_slow + * Return the number of 1 bits set in word + */ +static int +pg_popcount64_slow(uint64 word) +{ +#ifdef HAVE__BUILTIN_POPCOUNT +#if defined(HAVE_LONG_INT_64) + return __builtin_popcountl(word); +#elif defined(HAVE_LONG_LONG_INT_64) + return __builtin_popcountll(word); +#else +#error must have a working 64-bit integer datatype +#endif +#else /* !HAVE__BUILTIN_POPCOUNT */ + int result = 0; + + while (word != 0) + { + result += pg_number_of_ones[word & 255]; + word >>= 8; + } + + return result; +#endif /* HAVE__BUILTIN_POPCOUNT */ +} + +#ifndef TRY_POPCNT_FAST + +/* + * When the POPCNT instruction is not available, there's no point in using + * function pointers to vary the implementation between the fast and slow + * method. We instead just make these actual external functions when + * TRY_POPCNT_FAST is not defined. The compiler should be able to inline + * the slow versions here. + */ +int +pg_popcount32(uint32 word) +{ + return pg_popcount32_slow(word); +} + +int +pg_popcount64(uint64 word) +{ + return pg_popcount64_slow(word); +} + +#endif /* !TRY_POPCNT_FAST */ + +/* + * pg_popcount + * Returns the number of 1-bits in buf + */ +uint64 +pg_popcount(const char *buf, int bytes) +{ + uint64 popcnt = 0; + +#if SIZEOF_VOID_P >= 8 + /* Process in 64-bit chunks if the buffer is aligned. */ + if (buf == (const char *) TYPEALIGN(8, buf)) + { + const uint64 *words = (const uint64 *) buf; + + while (bytes >= 8) + { + popcnt += pg_popcount64(*words++); + bytes -= 8; + } + + buf = (const char *) words; + } +#else + /* Process in 32-bit chunks if the buffer is aligned. */ + if (buf == (const char *) TYPEALIGN(4, buf)) + { + const uint32 *words = (const uint32 *) buf; + + while (bytes >= 4) + { + popcnt += pg_popcount32(*words++); + bytes -= 4; + } + + buf = (const char *) words; + } +#endif + + /* Process any remaining bytes */ + while (bytes--) + popcnt += pg_number_of_ones[(unsigned char) *buf++]; + + return popcnt; +} diff --git a/src/port/pg_crc32c_armv8.c b/src/port/pg_crc32c_armv8.c new file mode 100644 index 0000000..d8fae51 --- /dev/null +++ b/src/port/pg_crc32c_armv8.c @@ -0,0 +1,75 @@ +/*------------------------------------------------------------------------- + * + * pg_crc32c_armv8.c + * Compute CRC-32C checksum using ARMv8 CRC Extension instructions + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pg_crc32c_armv8.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include <arm_acle.h> + +#include "port/pg_crc32c.h" + +pg_crc32c +pg_comp_crc32c_armv8(pg_crc32c crc, const void *data, size_t len) +{ + const unsigned char *p = data; + const unsigned char *pend = p + len; + + /* + * ARMv8 doesn't require alignment, but aligned memory access is + * significantly faster. Process leading bytes so that the loop below + * starts with a pointer aligned to eight bytes. + */ + if (!PointerIsAligned(p, uint16) && + p + 1 <= pend) + { + crc = __crc32cb(crc, *p); + p += 1; + } + if (!PointerIsAligned(p, uint32) && + p + 2 <= pend) + { + crc = __crc32ch(crc, *(uint16 *) p); + p += 2; + } + if (!PointerIsAligned(p, uint64) && + p + 4 <= pend) + { + crc = __crc32cw(crc, *(uint32 *) p); + p += 4; + } + + /* Process eight bytes at a time, as far as we can. */ + while (p + 8 <= pend) + { + crc = __crc32cd(crc, *(uint64 *) p); + p += 8; + } + + /* Process remaining 0-7 bytes. */ + if (p + 4 <= pend) + { + crc = __crc32cw(crc, *(uint32 *) p); + p += 4; + } + if (p + 2 <= pend) + { + crc = __crc32ch(crc, *(uint16 *) p); + p += 2; + } + if (p < pend) + { + crc = __crc32cb(crc, *p); + } + + return crc; +} diff --git a/src/port/pg_crc32c_armv8_choose.c b/src/port/pg_crc32c_armv8_choose.c new file mode 100644 index 0000000..0fdddcc --- /dev/null +++ b/src/port/pg_crc32c_armv8_choose.c @@ -0,0 +1,95 @@ +/*------------------------------------------------------------------------- + * + * pg_crc32c_armv8_choose.c + * Choose between ARMv8 and software CRC-32C implementation. + * + * On first call, checks if the CPU we're running on supports the ARMv8 + * CRC Extension. If it does, use the special instructions for CRC-32C + * computation. Otherwise, fall back to the pure software implementation + * (slicing-by-8). + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pg_crc32c_armv8_choose.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include <setjmp.h> +#include <signal.h> + +#include "port/pg_crc32c.h" + + +static sigjmp_buf illegal_instruction_jump; + +/* + * Probe by trying to execute pg_comp_crc32c_armv8(). If the instruction + * isn't available, we expect to get SIGILL, which we can trap. + */ +static void +illegal_instruction_handler(SIGNAL_ARGS) +{ + siglongjmp(illegal_instruction_jump, 1); +} + +static bool +pg_crc32c_armv8_available(void) +{ + uint64 data = 42; + int result; + + /* + * Be careful not to do anything that might throw an error while we have + * the SIGILL handler set to a nonstandard value. + */ + pqsignal(SIGILL, illegal_instruction_handler); + if (sigsetjmp(illegal_instruction_jump, 1) == 0) + { + /* Rather than hard-wiring an expected result, compare to SB8 code */ + result = (pg_comp_crc32c_armv8(0, &data, sizeof(data)) == + pg_comp_crc32c_sb8(0, &data, sizeof(data))); + } + else + { + /* We got the SIGILL trap */ + result = -1; + } + pqsignal(SIGILL, SIG_DFL); + +#ifndef FRONTEND + /* We don't expect this case, so complain loudly */ + if (result == 0) + elog(ERROR, "crc32 hardware and software results disagree"); + + elog(DEBUG1, "using armv8 crc32 hardware = %d", (result > 0)); +#endif + + return (result > 0); +} + +/* + * This gets called on the first call. It replaces the function pointer + * so that subsequent calls are routed directly to the chosen implementation. + */ +static pg_crc32c +pg_comp_crc32c_choose(pg_crc32c crc, const void *data, size_t len) +{ + if (pg_crc32c_armv8_available()) + pg_comp_crc32c = pg_comp_crc32c_armv8; + else + pg_comp_crc32c = pg_comp_crc32c_sb8; + + return pg_comp_crc32c(crc, data, len); +} + +pg_crc32c (*pg_comp_crc32c) (pg_crc32c crc, const void *data, size_t len) = pg_comp_crc32c_choose; diff --git a/src/port/pg_crc32c_sb8.c b/src/port/pg_crc32c_sb8.c new file mode 100644 index 0000000..cdd3e05 --- /dev/null +++ b/src/port/pg_crc32c_sb8.c @@ -0,0 +1,1169 @@ +/*------------------------------------------------------------------------- + * + * pg_crc32c_sb8.c + * Compute CRC-32C checksum using slicing-by-8 algorithm. + * + * Michael E. Kounavis, Frank L. Berry, + * "Novel Table Lookup-Based Algorithms for High-Performance CRC + * Generation", IEEE Transactions on Computers, vol.57, no. 11, + * pp. 1550-1560, November 2008, doi:10.1109/TC.2008.85 + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pg_crc32c_sb8.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include "port/pg_crc32c.h" + +static const uint32 pg_crc32c_table[8][256]; + +/* Accumulate one input byte */ +#ifdef WORDS_BIGENDIAN +#define CRC8(x) pg_crc32c_table[0][((crc >> 24) ^ (x)) & 0xFF] ^ (crc << 8) +#else +#define CRC8(x) pg_crc32c_table[0][(crc ^ (x)) & 0xFF] ^ (crc >> 8) +#endif + +pg_crc32c +pg_comp_crc32c_sb8(pg_crc32c crc, const void *data, size_t len) +{ + const unsigned char *p = data; + const uint32 *p4; + + /* + * Handle 0-3 initial bytes one at a time, so that the loop below starts + * with a pointer aligned to four bytes. + */ + while (len > 0 && ((uintptr_t) p & 3)) + { + crc = CRC8(*p++); + len--; + } + + /* + * Process eight bytes of data at a time. + */ + p4 = (const uint32 *) p; + while (len >= 8) + { + uint32 a = *p4++ ^ crc; + uint32 b = *p4++; + +#ifdef WORDS_BIGENDIAN + const uint8 c0 = b; + const uint8 c1 = b >> 8; + const uint8 c2 = b >> 16; + const uint8 c3 = b >> 24; + const uint8 c4 = a; + const uint8 c5 = a >> 8; + const uint8 c6 = a >> 16; + const uint8 c7 = a >> 24; +#else + const uint8 c0 = b >> 24; + const uint8 c1 = b >> 16; + const uint8 c2 = b >> 8; + const uint8 c3 = b; + const uint8 c4 = a >> 24; + const uint8 c5 = a >> 16; + const uint8 c6 = a >> 8; + const uint8 c7 = a; +#endif + + crc = + pg_crc32c_table[0][c0] ^ pg_crc32c_table[1][c1] ^ + pg_crc32c_table[2][c2] ^ pg_crc32c_table[3][c3] ^ + pg_crc32c_table[4][c4] ^ pg_crc32c_table[5][c5] ^ + pg_crc32c_table[6][c6] ^ pg_crc32c_table[7][c7]; + + len -= 8; + } + + /* + * Handle any remaining bytes one at a time. + */ + p = (const unsigned char *) p4; + while (len > 0) + { + crc = CRC8(*p++); + len--; + } + + return crc; +} + +/* + * Lookup tables for the slicing-by-8 algorithm, for the so-called Castagnoli + * polynomial (the same that is used e.g. in iSCSI), 0x1EDC6F41. Using + * Williams' terms, this is the "normal", not "reflected" version. However, on + * big-endian systems the values in the tables are stored in byte-reversed + * order (IOW, the tables are stored in little-endian order even on big-endian + * systems). + */ +static const uint32 pg_crc32c_table[8][256] = { +#ifndef WORDS_BIGENDIAN + { + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, + 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, + 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, + 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, + 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, + 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, + 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, + 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, + 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, + 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, + 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, + 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, + 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, + 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, + 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, + 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, + 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, + 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, + 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, + 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, + 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, + 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, + 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, + 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, + 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, + 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, + 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, + 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, + 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, + 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, + 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, + 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, + 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 + }, + { + 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, + 0x4E8A61DC, 0x5D28F9AB, 0x69CF5132, 0x7A6DC945, + 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21, + 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD, + 0x3FC5F181, 0x2C6769F6, 0x1880C16F, 0x0B225918, + 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4, + 0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, + 0xEC5B53E5, 0xFFF9CB92, 0xCB1E630B, 0xD8BCFB7C, + 0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B, + 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47, + 0xE29F20BA, 0xF13DB8CD, 0xC5DA1054, 0xD6788823, + 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF, + 0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, + 0x0EC4735F, 0x1D66EB28, 0x298143B1, 0x3A23DBC6, + 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2, + 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E, + 0xFF17C604, 0xECB55E73, 0xD852F6EA, 0xCBF06E9D, + 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41, + 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, + 0x2C896460, 0x3F2BFC17, 0x0BCC548E, 0x186ECCF9, + 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C, + 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0, + 0x5DC6F43D, 0x4E646C4A, 0x7A83C4D3, 0x69215CA4, + 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78, + 0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, + 0xCE1644DA, 0xDDB4DCAD, 0xE9537434, 0xFAF1EC43, + 0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27, + 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB, + 0xBF59D487, 0xACFB4CF0, 0x981CE469, 0x8BBE7C1E, + 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2, + 0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, + 0x6CC776E3, 0x7F65EE94, 0x4B82460D, 0x5820DE7A, + 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260, + 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC, + 0x66D73941, 0x7575A136, 0x419209AF, 0x523091D8, + 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004, + 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, + 0x8A8C6AA4, 0x992EF2D3, 0xADC95A4A, 0xBE6BC23D, + 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059, + 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185, + 0x844819FB, 0x97EA818C, 0xA30D2915, 0xB0AFB162, + 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE, + 0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, + 0x57D6BB9F, 0x447423E8, 0x70938B71, 0x63311306, + 0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3, + 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F, + 0x26992BC2, 0x353BB3B5, 0x01DC1B2C, 0x127E835B, + 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287, + 0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, + 0x4A5E5D21, 0x59FCC556, 0x6D1B6DCF, 0x7EB9F5B8, + 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC, + 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600, + 0x3B11CD7C, 0x28B3550B, 0x1C54FD92, 0x0FF665E5, + 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439, + 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, + 0xE88F6F18, 0xFB2DF76F, 0xCFCA5FF6, 0xDC68C781, + 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766, + 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA, + 0xE64B1C47, 0xF5E98430, 0xC10E2CA9, 0xD2ACB4DE, + 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502, + 0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, + 0x0A104FA2, 0x19B2D7D5, 0x2D557F4C, 0x3EF7E73B, + 0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F, + 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483 + }, + { + 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, + 0x9EDEA41A, 0x3B9F3664, 0xD1B1F617, 0x74F06469, + 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6, + 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC, + 0x70A27D8A, 0xD5E3EFF4, 0x3FCD2F87, 0x9A8CBDF9, + 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3, + 0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, + 0xD62DE755, 0x736C752B, 0x9942B558, 0x3C032726, + 0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67, + 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D, + 0xD915C5D1, 0x7C5457AF, 0x967A97DC, 0x333B05A2, + 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8, + 0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, + 0x0F382284, 0xAA79B0FA, 0x40577089, 0xE516E2F7, + 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828, + 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32, + 0xC76580D9, 0x622412A7, 0x880AD2D4, 0x2D4B40AA, + 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0, + 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, + 0x61EA1A06, 0xC4AB8878, 0x2E85480B, 0x8BC4DA75, + 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20, + 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A, + 0x8F96C396, 0x2AD751E8, 0xC0F9919B, 0x65B803E5, + 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF, + 0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, + 0xB8FFDFD7, 0x1DBE4DA9, 0xF7908DDA, 0x52D11FA4, + 0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B, + 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161, + 0x56830647, 0xF3C29439, 0x19EC544A, 0xBCADC634, + 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E, + 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, + 0xF00C9C98, 0x554D0EE6, 0xBF63CE95, 0x1A225CEB, + 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730, + 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A, + 0xB3764986, 0x1637DBF8, 0xFC191B8B, 0x595889F5, + 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF, + 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, + 0x655BAED3, 0xC01A3CAD, 0x2A34FCDE, 0x8F756EA0, + 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F, + 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065, + 0x6A638C57, 0xCF221E29, 0x250CDE5A, 0x804D4C24, + 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E, + 0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, + 0xCCEC1688, 0x69AD84F6, 0x83834485, 0x26C2D6FB, + 0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE, + 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4, + 0x2290CF18, 0x87D15D66, 0x6DFF9D15, 0xC8BE0F6B, + 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71, + 0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, + 0xD29C5380, 0x77DDC1FE, 0x9DF3018D, 0x38B293F3, + 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C, + 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36, + 0x3CE08A10, 0x99A1186E, 0x738FD81D, 0xD6CE4A63, + 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79, + 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, + 0x9A6F10CF, 0x3F2E82B1, 0xD50042C2, 0x7041D0BC, + 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD, + 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7, + 0x9557324B, 0x3016A035, 0xDA386046, 0x7F79F238, + 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622, + 0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, + 0x437AD51E, 0xE63B4760, 0x0C158713, 0xA954156D, + 0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2, + 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8 + }, + { + 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, + 0x7B2231F3, 0xA6679B4B, 0xC4451272, 0x1900B8CA, + 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF, + 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C, + 0xE964B13D, 0x34211B85, 0x560392BC, 0x8B463804, + 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7, + 0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, + 0x6402E328, 0xB9474990, 0xDB65C0A9, 0x06206A11, + 0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2, + 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41, + 0x2161776D, 0xFC24DDD5, 0x9E0654EC, 0x4343FE54, + 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7, + 0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, + 0x45639445, 0x98263EFD, 0xFA04B7C4, 0x27411D7C, + 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69, + 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A, + 0xABA65FE7, 0x76E3F55F, 0x14C17C66, 0xC984D6DE, + 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D, + 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, + 0x26C00DF2, 0xFB85A74A, 0x99A72E73, 0x44E284CB, + 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3, + 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610, + 0xB4868D3C, 0x69C32784, 0x0BE1AEBD, 0xD6A40405, + 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6, + 0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, + 0x07A17A9F, 0xDAE4D027, 0xB8C6591E, 0x6583F3A6, + 0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3, + 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040, + 0x95E7FA51, 0x48A250E9, 0x2A80D9D0, 0xF7C57368, + 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B, + 0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, + 0x1881A844, 0xC5C402FC, 0xA7E68BC5, 0x7AA3217D, + 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006, + 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5, + 0xA4E4AAD9, 0x79A10061, 0x1B838958, 0xC6C623E0, + 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213, + 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, + 0xC0E649F1, 0x1DA3E349, 0x7F816A70, 0xA2C4C0C8, + 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD, + 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E, + 0x8585DDB4, 0x58C0770C, 0x3AE2FE35, 0xE7A7548D, + 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E, + 0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, + 0x08E38FA1, 0xD5A62519, 0xB784AC20, 0x6AC10698, + 0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0, + 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443, + 0x9AA50F6F, 0x47E0A5D7, 0x25C22CEE, 0xF8878656, + 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5, + 0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, + 0x8224A72B, 0x5F610D93, 0x3D4384AA, 0xE0062E12, + 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07, + 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4, + 0x106227E5, 0xCD278D5D, 0xAF050464, 0x7240AEDC, + 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F, + 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, + 0x9D0475F0, 0x4041DF48, 0x22635671, 0xFF26FCC9, + 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A, + 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99, + 0xD867E1B5, 0x05224B0D, 0x6700C234, 0xBA45688C, + 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F, + 0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, + 0xBC65029D, 0x6120A825, 0x0302211C, 0xDE478BA4, + 0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1, + 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842 + }, + { + 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, + 0xE045BEB0, 0xD854D11C, 0x906761E8, 0xA8760E44, + 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65, + 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5, + 0x8F2261D3, 0xB7330E7F, 0xFF00BE8B, 0xC711D127, + 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97, + 0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, + 0xAA00D4F2, 0x9211BB5E, 0xDA220BAA, 0xE2336406, + 0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3, + 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13, + 0xDECFBEC6, 0xE6DED16A, 0xAEED619E, 0x96FC0E32, + 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082, + 0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, + 0x74CF6A34, 0x4CDE0598, 0x04EDB56C, 0x3CFCDAC0, + 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1, + 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151, + 0x37516AAE, 0x0F400502, 0x4773B5F6, 0x7F62DA5A, + 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA, + 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, + 0x1273DF8F, 0x2A62B023, 0x625100D7, 0x5A406F7B, + 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89, + 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539, + 0x7D1400EC, 0x45056F40, 0x0D36DFB4, 0x3527B018, + 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8, + 0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, + 0xCCBC6149, 0xF4AD0EE5, 0xBC9EBE11, 0x848FD1BD, + 0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C, + 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C, + 0xA3DBBE2A, 0x9BCAD186, 0xD3F96172, 0xEBE80EDE, + 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E, + 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, + 0x86F90B0B, 0xBEE864A7, 0xF6DBD453, 0xCECABBFF, + 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8, + 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18, + 0xABC5DECD, 0x93D4B161, 0xDBE70195, 0xE3F66E39, + 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089, + 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, + 0x01C50A3F, 0x39D46593, 0x71E7D567, 0x49F6BACB, + 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA, + 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A, + 0x750A600B, 0x4D1B0FA7, 0x0528BF53, 0x3D39D0FF, + 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F, + 0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, + 0x5028D52A, 0x6839BA86, 0x200A0A72, 0x181B65DE, + 0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C, + 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C, + 0x3F4F0A49, 0x075E65E5, 0x4F6DD511, 0x777CBABD, + 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D, + 0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, + 0xB9B60142, 0x81A76EEE, 0xC994DE1A, 0xF185B1B6, + 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497, + 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27, + 0xD6D1DE21, 0xEEC0B18D, 0xA6F30179, 0x9EE26ED5, + 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065, + 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, + 0xF3F36B00, 0xCBE204AC, 0x83D1B458, 0xBBC0DBF4, + 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51, + 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1, + 0x873C0134, 0xBF2D6E98, 0xF71EDE6C, 0xCF0FB1C0, + 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70, + 0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, + 0x2D3CD5C6, 0x152DBA6A, 0x5D1E0A9E, 0x650F6532, + 0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013, + 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3 + }, + { + 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, + 0xB2F53777, 0x5DC55C6E, 0x697997B4, 0x8649FCAD, + 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5, + 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2, + 0xC00C303E, 0x2F3C5B27, 0x1B8090FD, 0xF4B0FBE4, + 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93, + 0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, + 0x12FF1F56, 0xFDCF744F, 0xC973BF95, 0x2643D48C, + 0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57, + 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20, + 0xE5F20E92, 0x0AC2658B, 0x3E7EAE51, 0xD14EC548, + 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F, + 0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, + 0xF70D11C4, 0x183D7ADD, 0x2C81B107, 0xC3B1DA1E, + 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576, + 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201, + 0x0E045BEB, 0xE13430F2, 0xD588FB28, 0x3AB89031, + 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746, + 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, + 0xDCF77483, 0x33C71F9A, 0x077BD440, 0xE84BBF59, + 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F, + 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778, + 0xAE0E73CA, 0x413E18D3, 0x7582D309, 0x9AB2B810, + 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67, + 0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, + 0x39057A11, 0xD6351108, 0xE289DAD2, 0x0DB9B1CB, + 0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3, + 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4, + 0x4BFC7D58, 0xA4CC1641, 0x9070DD9B, 0x7F40B682, + 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5, + 0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, + 0x990F5230, 0x763F3929, 0x4283F2F3, 0xADB399EA, + 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C, + 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B, + 0x7C0EAFC9, 0x933EC4D0, 0xA7820F0A, 0x48B26413, + 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364, + 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, + 0x6EF1B09F, 0x81C1DB86, 0xB57D105C, 0x5A4D7B45, + 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D, + 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A, + 0x99FCA15B, 0x76CCCA42, 0x42700198, 0xAD406A81, + 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6, + 0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, + 0x4B0F8E33, 0xA43FE52A, 0x90832EF0, 0x7FB345E9, + 0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF, + 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8, + 0x39F6897A, 0xD6C6E263, 0xE27A29B9, 0x0D4A42A0, + 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7, + 0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, + 0xA0F9DB4A, 0x4FC9B053, 0x7B757B89, 0x94451090, + 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8, + 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F, + 0xD200DC03, 0x3D30B71A, 0x098C7CC0, 0xE6BC17D9, + 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE, + 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, + 0x00F3F36B, 0xEFC39872, 0xDB7F53A8, 0x344F38B1, + 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A, + 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D, + 0xF7FEE2AF, 0x18CE89B6, 0x2C72426C, 0xC3422975, + 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02, + 0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, + 0xE501FDF9, 0x0A3196E0, 0x3E8D5D3A, 0xD1BD3623, + 0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B, + 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C + }, + { + 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, + 0xA5E0C5D1, 0xCDE3E919, 0x75E69C41, 0x1DE5B089, + 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B, + 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA, + 0x9C5BFAA6, 0xF458D66E, 0x4C5DA336, 0x245E8FFE, + 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F, + 0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, + 0x7796C224, 0x1F95EEEC, 0xA7909BB4, 0xCF93B77C, + 0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5, + 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334, + 0x73767EEE, 0x1B755226, 0xA370277E, 0xCB730BB6, + 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67, + 0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, + 0x04E0BCCA, 0x6CE39002, 0xD4E6E55A, 0xBCE5C992, + 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110, + 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1, + 0x7AB7077A, 0x12B42BB2, 0xAAB15EEA, 0xC2B27222, + 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3, + 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, + 0x917A3FF8, 0xF9791330, 0x417C6668, 0x297F4AA0, + 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884, + 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55, + 0xA8C1008F, 0xC0C22C47, 0x78C7591F, 0x10C475D7, + 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006, + 0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, + 0xE20C4116, 0x8A0F6DDE, 0x320A1886, 0x5A09344E, + 0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC, + 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D, + 0xDBB77E61, 0xB3B452A9, 0x0BB127F1, 0x63B20B39, + 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8, + 0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, + 0x307A46E3, 0x58796A2B, 0xE07C1F73, 0x887F33BB, + 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC, + 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D, + 0xBB43F3A7, 0xD340DF6F, 0x6B45AA37, 0x034686FF, + 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E, + 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, + 0xCCD53183, 0xA4D61D4B, 0x1CD36813, 0x74D044DB, + 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59, + 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988, + 0xC8358D49, 0xA036A181, 0x1833D4D9, 0x7030F811, + 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0, + 0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, + 0x23F8B5CB, 0x4BFB9903, 0xF3FEEC5B, 0x9BFDC093, + 0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7, + 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766, + 0x1A438ABC, 0x7240A674, 0xCA45D32C, 0xA246FFE4, + 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35, + 0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, + 0x2A39CC5F, 0x423AE097, 0xFA3F95CF, 0x923CB907, + 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185, + 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454, + 0x1382F328, 0x7B81DFE0, 0xC384AAB8, 0xAB878670, + 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1, + 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, + 0xF84FCBAA, 0x904CE762, 0x2849923A, 0x404ABEF2, + 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B, + 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA, + 0xFCAF7760, 0x94AC5BA8, 0x2CA92EF0, 0x44AA0238, + 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9, + 0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, + 0x8B39B544, 0xE33A998C, 0x5B3FECD4, 0x333CC01C, + 0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E, + 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F + }, + { + 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, + 0x211D826D, 0x6821FF4A, 0xB3657823, 0xFA590504, + 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3, + 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE, + 0x847609B4, 0xCD4A7493, 0x160EF3FA, 0x5F328EDD, + 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0, + 0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, + 0xE7508F03, 0xAE6CF224, 0x7528754D, 0x3C14086A, + 0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0, + 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D, + 0x4F3B6143, 0x06071C64, 0xDD439B0D, 0x947FE62A, + 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447, + 0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, + 0xA86BEE40, 0xE1579367, 0x3A13140E, 0x732F6929, + 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E, + 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3, + 0x1A00CB32, 0x533CB615, 0x8878317C, 0xC1444C5B, + 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36, + 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, + 0x79264D85, 0x301A30A2, 0xEB5EB7CB, 0xA262CAEC, + 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF, + 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782, + 0xDC4DC65C, 0x9571BB7B, 0x4E353C12, 0x07094135, + 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358, + 0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, + 0x361D2CC6, 0x7F2151E1, 0xA465D688, 0xED59ABAF, + 0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18, + 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75, + 0x9376A71F, 0xDA4ADA38, 0x010E5D51, 0x48322076, + 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B, + 0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, + 0xF05021A8, 0xB96C5C8F, 0x6228DBE6, 0x2B14A6C1, + 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D, + 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360, + 0x763A92BE, 0x3F06EF99, 0xE44268F0, 0xAD7E15D7, + 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA, + 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, + 0x916A1DBD, 0xD856609A, 0x0312E7F3, 0x4A2E9AD4, + 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63, + 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E, + 0x3901F3FD, 0x703D8EDA, 0xAB7909B3, 0xE2457494, + 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9, + 0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, + 0x5A27754A, 0x131B086D, 0xC85F8F04, 0x8163F223, + 0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20, + 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D, + 0xFF4CFE93, 0xB67083B4, 0x6D3404DD, 0x240879FA, + 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97, + 0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, + 0x0F1CDF3B, 0x4620A21C, 0x9D642575, 0xD4585852, + 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5, + 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88, + 0xAA7754E2, 0xE34B29C5, 0x380FAEAC, 0x7133D38B, + 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6, + 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, + 0xC951D255, 0x806DAF72, 0x5B29281B, 0x1215553C, + 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6, + 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB, + 0x613A3C15, 0x28064132, 0xF342C65B, 0xBA7EBB7C, + 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911, + 0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, + 0x866AB316, 0xCF56CE31, 0x14124958, 0x5D2E347F, + 0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8, + 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5 + } +#else /* !WORDS_BIGENDIAN */ + { + 0x00000000, 0x03836BF2, 0xF7703BE1, 0xF4F35013, + 0x1F979AC7, 0x1C14F135, 0xE8E7A126, 0xEB64CAD4, + 0xCF58D98A, 0xCCDBB278, 0x3828E26B, 0x3BAB8999, + 0xD0CF434D, 0xD34C28BF, 0x27BF78AC, 0x243C135E, + 0x6FC75E10, 0x6C4435E2, 0x98B765F1, 0x9B340E03, + 0x7050C4D7, 0x73D3AF25, 0x8720FF36, 0x84A394C4, + 0xA09F879A, 0xA31CEC68, 0x57EFBC7B, 0x546CD789, + 0xBF081D5D, 0xBC8B76AF, 0x487826BC, 0x4BFB4D4E, + 0xDE8EBD20, 0xDD0DD6D2, 0x29FE86C1, 0x2A7DED33, + 0xC11927E7, 0xC29A4C15, 0x36691C06, 0x35EA77F4, + 0x11D664AA, 0x12550F58, 0xE6A65F4B, 0xE52534B9, + 0x0E41FE6D, 0x0DC2959F, 0xF931C58C, 0xFAB2AE7E, + 0xB149E330, 0xB2CA88C2, 0x4639D8D1, 0x45BAB323, + 0xAEDE79F7, 0xAD5D1205, 0x59AE4216, 0x5A2D29E4, + 0x7E113ABA, 0x7D925148, 0x8961015B, 0x8AE26AA9, + 0x6186A07D, 0x6205CB8F, 0x96F69B9C, 0x9575F06E, + 0xBC1D7B41, 0xBF9E10B3, 0x4B6D40A0, 0x48EE2B52, + 0xA38AE186, 0xA0098A74, 0x54FADA67, 0x5779B195, + 0x7345A2CB, 0x70C6C939, 0x8435992A, 0x87B6F2D8, + 0x6CD2380C, 0x6F5153FE, 0x9BA203ED, 0x9821681F, + 0xD3DA2551, 0xD0594EA3, 0x24AA1EB0, 0x27297542, + 0xCC4DBF96, 0xCFCED464, 0x3B3D8477, 0x38BEEF85, + 0x1C82FCDB, 0x1F019729, 0xEBF2C73A, 0xE871ACC8, + 0x0315661C, 0x00960DEE, 0xF4655DFD, 0xF7E6360F, + 0x6293C661, 0x6110AD93, 0x95E3FD80, 0x96609672, + 0x7D045CA6, 0x7E873754, 0x8A746747, 0x89F70CB5, + 0xADCB1FEB, 0xAE487419, 0x5ABB240A, 0x59384FF8, + 0xB25C852C, 0xB1DFEEDE, 0x452CBECD, 0x46AFD53F, + 0x0D549871, 0x0ED7F383, 0xFA24A390, 0xF9A7C862, + 0x12C302B6, 0x11406944, 0xE5B33957, 0xE63052A5, + 0xC20C41FB, 0xC18F2A09, 0x357C7A1A, 0x36FF11E8, + 0xDD9BDB3C, 0xDE18B0CE, 0x2AEBE0DD, 0x29688B2F, + 0x783BF682, 0x7BB89D70, 0x8F4BCD63, 0x8CC8A691, + 0x67AC6C45, 0x642F07B7, 0x90DC57A4, 0x935F3C56, + 0xB7632F08, 0xB4E044FA, 0x401314E9, 0x43907F1B, + 0xA8F4B5CF, 0xAB77DE3D, 0x5F848E2E, 0x5C07E5DC, + 0x17FCA892, 0x147FC360, 0xE08C9373, 0xE30FF881, + 0x086B3255, 0x0BE859A7, 0xFF1B09B4, 0xFC986246, + 0xD8A47118, 0xDB271AEA, 0x2FD44AF9, 0x2C57210B, + 0xC733EBDF, 0xC4B0802D, 0x3043D03E, 0x33C0BBCC, + 0xA6B54BA2, 0xA5362050, 0x51C57043, 0x52461BB1, + 0xB922D165, 0xBAA1BA97, 0x4E52EA84, 0x4DD18176, + 0x69ED9228, 0x6A6EF9DA, 0x9E9DA9C9, 0x9D1EC23B, + 0x767A08EF, 0x75F9631D, 0x810A330E, 0x828958FC, + 0xC97215B2, 0xCAF17E40, 0x3E022E53, 0x3D8145A1, + 0xD6E58F75, 0xD566E487, 0x2195B494, 0x2216DF66, + 0x062ACC38, 0x05A9A7CA, 0xF15AF7D9, 0xF2D99C2B, + 0x19BD56FF, 0x1A3E3D0D, 0xEECD6D1E, 0xED4E06EC, + 0xC4268DC3, 0xC7A5E631, 0x3356B622, 0x30D5DDD0, + 0xDBB11704, 0xD8327CF6, 0x2CC12CE5, 0x2F424717, + 0x0B7E5449, 0x08FD3FBB, 0xFC0E6FA8, 0xFF8D045A, + 0x14E9CE8E, 0x176AA57C, 0xE399F56F, 0xE01A9E9D, + 0xABE1D3D3, 0xA862B821, 0x5C91E832, 0x5F1283C0, + 0xB4764914, 0xB7F522E6, 0x430672F5, 0x40851907, + 0x64B90A59, 0x673A61AB, 0x93C931B8, 0x904A5A4A, + 0x7B2E909E, 0x78ADFB6C, 0x8C5EAB7F, 0x8FDDC08D, + 0x1AA830E3, 0x192B5B11, 0xEDD80B02, 0xEE5B60F0, + 0x053FAA24, 0x06BCC1D6, 0xF24F91C5, 0xF1CCFA37, + 0xD5F0E969, 0xD673829B, 0x2280D288, 0x2103B97A, + 0xCA6773AE, 0xC9E4185C, 0x3D17484F, 0x3E9423BD, + 0x756F6EF3, 0x76EC0501, 0x821F5512, 0x819C3EE0, + 0x6AF8F434, 0x697B9FC6, 0x9D88CFD5, 0x9E0BA427, + 0xBA37B779, 0xB9B4DC8B, 0x4D478C98, 0x4EC4E76A, + 0xA5A02DBE, 0xA623464C, 0x52D0165F, 0x51537DAD, + }, + { + 0x00000000, 0x7798A213, 0xEE304527, 0x99A8E734, + 0xDC618A4E, 0xABF9285D, 0x3251CF69, 0x45C96D7A, + 0xB8C3149D, 0xCF5BB68E, 0x56F351BA, 0x216BF3A9, + 0x64A29ED3, 0x133A3CC0, 0x8A92DBF4, 0xFD0A79E7, + 0x81F1C53F, 0xF669672C, 0x6FC18018, 0x1859220B, + 0x5D904F71, 0x2A08ED62, 0xB3A00A56, 0xC438A845, + 0x3932D1A2, 0x4EAA73B1, 0xD7029485, 0xA09A3696, + 0xE5535BEC, 0x92CBF9FF, 0x0B631ECB, 0x7CFBBCD8, + 0x02E38B7F, 0x757B296C, 0xECD3CE58, 0x9B4B6C4B, + 0xDE820131, 0xA91AA322, 0x30B24416, 0x472AE605, + 0xBA209FE2, 0xCDB83DF1, 0x5410DAC5, 0x238878D6, + 0x664115AC, 0x11D9B7BF, 0x8871508B, 0xFFE9F298, + 0x83124E40, 0xF48AEC53, 0x6D220B67, 0x1ABAA974, + 0x5F73C40E, 0x28EB661D, 0xB1438129, 0xC6DB233A, + 0x3BD15ADD, 0x4C49F8CE, 0xD5E11FFA, 0xA279BDE9, + 0xE7B0D093, 0x90287280, 0x098095B4, 0x7E1837A7, + 0x04C617FF, 0x735EB5EC, 0xEAF652D8, 0x9D6EF0CB, + 0xD8A79DB1, 0xAF3F3FA2, 0x3697D896, 0x410F7A85, + 0xBC050362, 0xCB9DA171, 0x52354645, 0x25ADE456, + 0x6064892C, 0x17FC2B3F, 0x8E54CC0B, 0xF9CC6E18, + 0x8537D2C0, 0xF2AF70D3, 0x6B0797E7, 0x1C9F35F4, + 0x5956588E, 0x2ECEFA9D, 0xB7661DA9, 0xC0FEBFBA, + 0x3DF4C65D, 0x4A6C644E, 0xD3C4837A, 0xA45C2169, + 0xE1954C13, 0x960DEE00, 0x0FA50934, 0x783DAB27, + 0x06259C80, 0x71BD3E93, 0xE815D9A7, 0x9F8D7BB4, + 0xDA4416CE, 0xADDCB4DD, 0x347453E9, 0x43ECF1FA, + 0xBEE6881D, 0xC97E2A0E, 0x50D6CD3A, 0x274E6F29, + 0x62870253, 0x151FA040, 0x8CB74774, 0xFB2FE567, + 0x87D459BF, 0xF04CFBAC, 0x69E41C98, 0x1E7CBE8B, + 0x5BB5D3F1, 0x2C2D71E2, 0xB58596D6, 0xC21D34C5, + 0x3F174D22, 0x488FEF31, 0xD1270805, 0xA6BFAA16, + 0xE376C76C, 0x94EE657F, 0x0D46824B, 0x7ADE2058, + 0xF9FAC3FB, 0x8E6261E8, 0x17CA86DC, 0x605224CF, + 0x259B49B5, 0x5203EBA6, 0xCBAB0C92, 0xBC33AE81, + 0x4139D766, 0x36A17575, 0xAF099241, 0xD8913052, + 0x9D585D28, 0xEAC0FF3B, 0x7368180F, 0x04F0BA1C, + 0x780B06C4, 0x0F93A4D7, 0x963B43E3, 0xE1A3E1F0, + 0xA46A8C8A, 0xD3F22E99, 0x4A5AC9AD, 0x3DC26BBE, + 0xC0C81259, 0xB750B04A, 0x2EF8577E, 0x5960F56D, + 0x1CA99817, 0x6B313A04, 0xF299DD30, 0x85017F23, + 0xFB194884, 0x8C81EA97, 0x15290DA3, 0x62B1AFB0, + 0x2778C2CA, 0x50E060D9, 0xC94887ED, 0xBED025FE, + 0x43DA5C19, 0x3442FE0A, 0xADEA193E, 0xDA72BB2D, + 0x9FBBD657, 0xE8237444, 0x718B9370, 0x06133163, + 0x7AE88DBB, 0x0D702FA8, 0x94D8C89C, 0xE3406A8F, + 0xA68907F5, 0xD111A5E6, 0x48B942D2, 0x3F21E0C1, + 0xC22B9926, 0xB5B33B35, 0x2C1BDC01, 0x5B837E12, + 0x1E4A1368, 0x69D2B17B, 0xF07A564F, 0x87E2F45C, + 0xFD3CD404, 0x8AA47617, 0x130C9123, 0x64943330, + 0x215D5E4A, 0x56C5FC59, 0xCF6D1B6D, 0xB8F5B97E, + 0x45FFC099, 0x3267628A, 0xABCF85BE, 0xDC5727AD, + 0x999E4AD7, 0xEE06E8C4, 0x77AE0FF0, 0x0036ADE3, + 0x7CCD113B, 0x0B55B328, 0x92FD541C, 0xE565F60F, + 0xA0AC9B75, 0xD7343966, 0x4E9CDE52, 0x39047C41, + 0xC40E05A6, 0xB396A7B5, 0x2A3E4081, 0x5DA6E292, + 0x186F8FE8, 0x6FF72DFB, 0xF65FCACF, 0x81C768DC, + 0xFFDF5F7B, 0x8847FD68, 0x11EF1A5C, 0x6677B84F, + 0x23BED535, 0x54267726, 0xCD8E9012, 0xBA163201, + 0x471C4BE6, 0x3084E9F5, 0xA92C0EC1, 0xDEB4ACD2, + 0x9B7DC1A8, 0xECE563BB, 0x754D848F, 0x02D5269C, + 0x7E2E9A44, 0x09B63857, 0x901EDF63, 0xE7867D70, + 0xA24F100A, 0xD5D7B219, 0x4C7F552D, 0x3BE7F73E, + 0xC6ED8ED9, 0xB1752CCA, 0x28DDCBFE, 0x5F4569ED, + 0x1A8C0497, 0x6D14A684, 0xF4BC41B0, 0x8324E3A3, + }, + { + 0x00000000, 0x7E9241A5, 0x0D526F4F, 0x73C02EEA, + 0x1AA4DE9E, 0x64369F3B, 0x17F6B1D1, 0x6964F074, + 0xC53E5138, 0xBBAC109D, 0xC86C3E77, 0xB6FE7FD2, + 0xDF9A8FA6, 0xA108CE03, 0xD2C8E0E9, 0xAC5AA14C, + 0x8A7DA270, 0xF4EFE3D5, 0x872FCD3F, 0xF9BD8C9A, + 0x90D97CEE, 0xEE4B3D4B, 0x9D8B13A1, 0xE3195204, + 0x4F43F348, 0x31D1B2ED, 0x42119C07, 0x3C83DDA2, + 0x55E72DD6, 0x2B756C73, 0x58B54299, 0x2627033C, + 0x14FB44E1, 0x6A690544, 0x19A92BAE, 0x673B6A0B, + 0x0E5F9A7F, 0x70CDDBDA, 0x030DF530, 0x7D9FB495, + 0xD1C515D9, 0xAF57547C, 0xDC977A96, 0xA2053B33, + 0xCB61CB47, 0xB5F38AE2, 0xC633A408, 0xB8A1E5AD, + 0x9E86E691, 0xE014A734, 0x93D489DE, 0xED46C87B, + 0x8422380F, 0xFAB079AA, 0x89705740, 0xF7E216E5, + 0x5BB8B7A9, 0x252AF60C, 0x56EAD8E6, 0x28789943, + 0x411C6937, 0x3F8E2892, 0x4C4E0678, 0x32DC47DD, + 0xD98065C7, 0xA7122462, 0xD4D20A88, 0xAA404B2D, + 0xC324BB59, 0xBDB6FAFC, 0xCE76D416, 0xB0E495B3, + 0x1CBE34FF, 0x622C755A, 0x11EC5BB0, 0x6F7E1A15, + 0x061AEA61, 0x7888ABC4, 0x0B48852E, 0x75DAC48B, + 0x53FDC7B7, 0x2D6F8612, 0x5EAFA8F8, 0x203DE95D, + 0x49591929, 0x37CB588C, 0x440B7666, 0x3A9937C3, + 0x96C3968F, 0xE851D72A, 0x9B91F9C0, 0xE503B865, + 0x8C674811, 0xF2F509B4, 0x8135275E, 0xFFA766FB, + 0xCD7B2126, 0xB3E96083, 0xC0294E69, 0xBEBB0FCC, + 0xD7DFFFB8, 0xA94DBE1D, 0xDA8D90F7, 0xA41FD152, + 0x0845701E, 0x76D731BB, 0x05171F51, 0x7B855EF4, + 0x12E1AE80, 0x6C73EF25, 0x1FB3C1CF, 0x6121806A, + 0x47068356, 0x3994C2F3, 0x4A54EC19, 0x34C6ADBC, + 0x5DA25DC8, 0x23301C6D, 0x50F03287, 0x2E627322, + 0x8238D26E, 0xFCAA93CB, 0x8F6ABD21, 0xF1F8FC84, + 0x989C0CF0, 0xE60E4D55, 0x95CE63BF, 0xEB5C221A, + 0x4377278B, 0x3DE5662E, 0x4E2548C4, 0x30B70961, + 0x59D3F915, 0x2741B8B0, 0x5481965A, 0x2A13D7FF, + 0x864976B3, 0xF8DB3716, 0x8B1B19FC, 0xF5895859, + 0x9CEDA82D, 0xE27FE988, 0x91BFC762, 0xEF2D86C7, + 0xC90A85FB, 0xB798C45E, 0xC458EAB4, 0xBACAAB11, + 0xD3AE5B65, 0xAD3C1AC0, 0xDEFC342A, 0xA06E758F, + 0x0C34D4C3, 0x72A69566, 0x0166BB8C, 0x7FF4FA29, + 0x16900A5D, 0x68024BF8, 0x1BC26512, 0x655024B7, + 0x578C636A, 0x291E22CF, 0x5ADE0C25, 0x244C4D80, + 0x4D28BDF4, 0x33BAFC51, 0x407AD2BB, 0x3EE8931E, + 0x92B23252, 0xEC2073F7, 0x9FE05D1D, 0xE1721CB8, + 0x8816ECCC, 0xF684AD69, 0x85448383, 0xFBD6C226, + 0xDDF1C11A, 0xA36380BF, 0xD0A3AE55, 0xAE31EFF0, + 0xC7551F84, 0xB9C75E21, 0xCA0770CB, 0xB495316E, + 0x18CF9022, 0x665DD187, 0x159DFF6D, 0x6B0FBEC8, + 0x026B4EBC, 0x7CF90F19, 0x0F3921F3, 0x71AB6056, + 0x9AF7424C, 0xE46503E9, 0x97A52D03, 0xE9376CA6, + 0x80539CD2, 0xFEC1DD77, 0x8D01F39D, 0xF393B238, + 0x5FC91374, 0x215B52D1, 0x529B7C3B, 0x2C093D9E, + 0x456DCDEA, 0x3BFF8C4F, 0x483FA2A5, 0x36ADE300, + 0x108AE03C, 0x6E18A199, 0x1DD88F73, 0x634ACED6, + 0x0A2E3EA2, 0x74BC7F07, 0x077C51ED, 0x79EE1048, + 0xD5B4B104, 0xAB26F0A1, 0xD8E6DE4B, 0xA6749FEE, + 0xCF106F9A, 0xB1822E3F, 0xC24200D5, 0xBCD04170, + 0x8E0C06AD, 0xF09E4708, 0x835E69E2, 0xFDCC2847, + 0x94A8D833, 0xEA3A9996, 0x99FAB77C, 0xE768F6D9, + 0x4B325795, 0x35A01630, 0x466038DA, 0x38F2797F, + 0x5196890B, 0x2F04C8AE, 0x5CC4E644, 0x2256A7E1, + 0x0471A4DD, 0x7AE3E578, 0x0923CB92, 0x77B18A37, + 0x1ED57A43, 0x60473BE6, 0x1387150C, 0x6D1554A9, + 0xC14FF5E5, 0xBFDDB440, 0xCC1D9AAA, 0xB28FDB0F, + 0xDBEB2B7B, 0xA5796ADE, 0xD6B94434, 0xA82B0591, + }, + { + 0x00000000, 0xB8AA45DD, 0x812367BF, 0x39892262, + 0xF331227B, 0x4B9B67A6, 0x721245C4, 0xCAB80019, + 0xE66344F6, 0x5EC9012B, 0x67402349, 0xDFEA6694, + 0x1552668D, 0xADF82350, 0x94710132, 0x2CDB44EF, + 0x3DB164E9, 0x851B2134, 0xBC920356, 0x0438468B, + 0xCE804692, 0x762A034F, 0x4FA3212D, 0xF70964F0, + 0xDBD2201F, 0x637865C2, 0x5AF147A0, 0xE25B027D, + 0x28E30264, 0x904947B9, 0xA9C065DB, 0x116A2006, + 0x8B1425D7, 0x33BE600A, 0x0A374268, 0xB29D07B5, + 0x782507AC, 0xC08F4271, 0xF9066013, 0x41AC25CE, + 0x6D776121, 0xD5DD24FC, 0xEC54069E, 0x54FE4343, + 0x9E46435A, 0x26EC0687, 0x1F6524E5, 0xA7CF6138, + 0xB6A5413E, 0x0E0F04E3, 0x37862681, 0x8F2C635C, + 0x45946345, 0xFD3E2698, 0xC4B704FA, 0x7C1D4127, + 0x50C605C8, 0xE86C4015, 0xD1E56277, 0x694F27AA, + 0xA3F727B3, 0x1B5D626E, 0x22D4400C, 0x9A7E05D1, + 0xE75FA6AB, 0x5FF5E376, 0x667CC114, 0xDED684C9, + 0x146E84D0, 0xACC4C10D, 0x954DE36F, 0x2DE7A6B2, + 0x013CE25D, 0xB996A780, 0x801F85E2, 0x38B5C03F, + 0xF20DC026, 0x4AA785FB, 0x732EA799, 0xCB84E244, + 0xDAEEC242, 0x6244879F, 0x5BCDA5FD, 0xE367E020, + 0x29DFE039, 0x9175A5E4, 0xA8FC8786, 0x1056C25B, + 0x3C8D86B4, 0x8427C369, 0xBDAEE10B, 0x0504A4D6, + 0xCFBCA4CF, 0x7716E112, 0x4E9FC370, 0xF63586AD, + 0x6C4B837C, 0xD4E1C6A1, 0xED68E4C3, 0x55C2A11E, + 0x9F7AA107, 0x27D0E4DA, 0x1E59C6B8, 0xA6F38365, + 0x8A28C78A, 0x32828257, 0x0B0BA035, 0xB3A1E5E8, + 0x7919E5F1, 0xC1B3A02C, 0xF83A824E, 0x4090C793, + 0x51FAE795, 0xE950A248, 0xD0D9802A, 0x6873C5F7, + 0xA2CBC5EE, 0x1A618033, 0x23E8A251, 0x9B42E78C, + 0xB799A363, 0x0F33E6BE, 0x36BAC4DC, 0x8E108101, + 0x44A88118, 0xFC02C4C5, 0xC58BE6A7, 0x7D21A37A, + 0x3FC9A052, 0x8763E58F, 0xBEEAC7ED, 0x06408230, + 0xCCF88229, 0x7452C7F4, 0x4DDBE596, 0xF571A04B, + 0xD9AAE4A4, 0x6100A179, 0x5889831B, 0xE023C6C6, + 0x2A9BC6DF, 0x92318302, 0xABB8A160, 0x1312E4BD, + 0x0278C4BB, 0xBAD28166, 0x835BA304, 0x3BF1E6D9, + 0xF149E6C0, 0x49E3A31D, 0x706A817F, 0xC8C0C4A2, + 0xE41B804D, 0x5CB1C590, 0x6538E7F2, 0xDD92A22F, + 0x172AA236, 0xAF80E7EB, 0x9609C589, 0x2EA38054, + 0xB4DD8585, 0x0C77C058, 0x35FEE23A, 0x8D54A7E7, + 0x47ECA7FE, 0xFF46E223, 0xC6CFC041, 0x7E65859C, + 0x52BEC173, 0xEA1484AE, 0xD39DA6CC, 0x6B37E311, + 0xA18FE308, 0x1925A6D5, 0x20AC84B7, 0x9806C16A, + 0x896CE16C, 0x31C6A4B1, 0x084F86D3, 0xB0E5C30E, + 0x7A5DC317, 0xC2F786CA, 0xFB7EA4A8, 0x43D4E175, + 0x6F0FA59A, 0xD7A5E047, 0xEE2CC225, 0x568687F8, + 0x9C3E87E1, 0x2494C23C, 0x1D1DE05E, 0xA5B7A583, + 0xD89606F9, 0x603C4324, 0x59B56146, 0xE11F249B, + 0x2BA72482, 0x930D615F, 0xAA84433D, 0x122E06E0, + 0x3EF5420F, 0x865F07D2, 0xBFD625B0, 0x077C606D, + 0xCDC46074, 0x756E25A9, 0x4CE707CB, 0xF44D4216, + 0xE5276210, 0x5D8D27CD, 0x640405AF, 0xDCAE4072, + 0x1616406B, 0xAEBC05B6, 0x973527D4, 0x2F9F6209, + 0x034426E6, 0xBBEE633B, 0x82674159, 0x3ACD0484, + 0xF075049D, 0x48DF4140, 0x71566322, 0xC9FC26FF, + 0x5382232E, 0xEB2866F3, 0xD2A14491, 0x6A0B014C, + 0xA0B30155, 0x18194488, 0x219066EA, 0x993A2337, + 0xB5E167D8, 0x0D4B2205, 0x34C20067, 0x8C6845BA, + 0x46D045A3, 0xFE7A007E, 0xC7F3221C, 0x7F5967C1, + 0x6E3347C7, 0xD699021A, 0xEF102078, 0x57BA65A5, + 0x9D0265BC, 0x25A82061, 0x1C210203, 0xA48B47DE, + 0x88500331, 0x30FA46EC, 0x0973648E, 0xB1D92153, + 0x7B61214A, 0xC3CB6497, 0xFA4246F5, 0x42E80328, + }, + { + 0x00000000, 0xAC6F1138, 0x58DF2270, 0xF4B03348, + 0xB0BE45E0, 0x1CD154D8, 0xE8616790, 0x440E76A8, + 0x910B67C5, 0x3D6476FD, 0xC9D445B5, 0x65BB548D, + 0x21B52225, 0x8DDA331D, 0x796A0055, 0xD505116D, + 0xD361228F, 0x7F0E33B7, 0x8BBE00FF, 0x27D111C7, + 0x63DF676F, 0xCFB07657, 0x3B00451F, 0x976F5427, + 0x426A454A, 0xEE055472, 0x1AB5673A, 0xB6DA7602, + 0xF2D400AA, 0x5EBB1192, 0xAA0B22DA, 0x066433E2, + 0x57B5A81B, 0xFBDAB923, 0x0F6A8A6B, 0xA3059B53, + 0xE70BEDFB, 0x4B64FCC3, 0xBFD4CF8B, 0x13BBDEB3, + 0xC6BECFDE, 0x6AD1DEE6, 0x9E61EDAE, 0x320EFC96, + 0x76008A3E, 0xDA6F9B06, 0x2EDFA84E, 0x82B0B976, + 0x84D48A94, 0x28BB9BAC, 0xDC0BA8E4, 0x7064B9DC, + 0x346ACF74, 0x9805DE4C, 0x6CB5ED04, 0xC0DAFC3C, + 0x15DFED51, 0xB9B0FC69, 0x4D00CF21, 0xE16FDE19, + 0xA561A8B1, 0x090EB989, 0xFDBE8AC1, 0x51D19BF9, + 0xAE6A5137, 0x0205400F, 0xF6B57347, 0x5ADA627F, + 0x1ED414D7, 0xB2BB05EF, 0x460B36A7, 0xEA64279F, + 0x3F6136F2, 0x930E27CA, 0x67BE1482, 0xCBD105BA, + 0x8FDF7312, 0x23B0622A, 0xD7005162, 0x7B6F405A, + 0x7D0B73B8, 0xD1646280, 0x25D451C8, 0x89BB40F0, + 0xCDB53658, 0x61DA2760, 0x956A1428, 0x39050510, + 0xEC00147D, 0x406F0545, 0xB4DF360D, 0x18B02735, + 0x5CBE519D, 0xF0D140A5, 0x046173ED, 0xA80E62D5, + 0xF9DFF92C, 0x55B0E814, 0xA100DB5C, 0x0D6FCA64, + 0x4961BCCC, 0xE50EADF4, 0x11BE9EBC, 0xBDD18F84, + 0x68D49EE9, 0xC4BB8FD1, 0x300BBC99, 0x9C64ADA1, + 0xD86ADB09, 0x7405CA31, 0x80B5F979, 0x2CDAE841, + 0x2ABEDBA3, 0x86D1CA9B, 0x7261F9D3, 0xDE0EE8EB, + 0x9A009E43, 0x366F8F7B, 0xC2DFBC33, 0x6EB0AD0B, + 0xBBB5BC66, 0x17DAAD5E, 0xE36A9E16, 0x4F058F2E, + 0x0B0BF986, 0xA764E8BE, 0x53D4DBF6, 0xFFBBCACE, + 0x5CD5A26E, 0xF0BAB356, 0x040A801E, 0xA8659126, + 0xEC6BE78E, 0x4004F6B6, 0xB4B4C5FE, 0x18DBD4C6, + 0xCDDEC5AB, 0x61B1D493, 0x9501E7DB, 0x396EF6E3, + 0x7D60804B, 0xD10F9173, 0x25BFA23B, 0x89D0B303, + 0x8FB480E1, 0x23DB91D9, 0xD76BA291, 0x7B04B3A9, + 0x3F0AC501, 0x9365D439, 0x67D5E771, 0xCBBAF649, + 0x1EBFE724, 0xB2D0F61C, 0x4660C554, 0xEA0FD46C, + 0xAE01A2C4, 0x026EB3FC, 0xF6DE80B4, 0x5AB1918C, + 0x0B600A75, 0xA70F1B4D, 0x53BF2805, 0xFFD0393D, + 0xBBDE4F95, 0x17B15EAD, 0xE3016DE5, 0x4F6E7CDD, + 0x9A6B6DB0, 0x36047C88, 0xC2B44FC0, 0x6EDB5EF8, + 0x2AD52850, 0x86BA3968, 0x720A0A20, 0xDE651B18, + 0xD80128FA, 0x746E39C2, 0x80DE0A8A, 0x2CB11BB2, + 0x68BF6D1A, 0xC4D07C22, 0x30604F6A, 0x9C0F5E52, + 0x490A4F3F, 0xE5655E07, 0x11D56D4F, 0xBDBA7C77, + 0xF9B40ADF, 0x55DB1BE7, 0xA16B28AF, 0x0D043997, + 0xF2BFF359, 0x5ED0E261, 0xAA60D129, 0x060FC011, + 0x4201B6B9, 0xEE6EA781, 0x1ADE94C9, 0xB6B185F1, + 0x63B4949C, 0xCFDB85A4, 0x3B6BB6EC, 0x9704A7D4, + 0xD30AD17C, 0x7F65C044, 0x8BD5F30C, 0x27BAE234, + 0x21DED1D6, 0x8DB1C0EE, 0x7901F3A6, 0xD56EE29E, + 0x91609436, 0x3D0F850E, 0xC9BFB646, 0x65D0A77E, + 0xB0D5B613, 0x1CBAA72B, 0xE80A9463, 0x4465855B, + 0x006BF3F3, 0xAC04E2CB, 0x58B4D183, 0xF4DBC0BB, + 0xA50A5B42, 0x09654A7A, 0xFDD57932, 0x51BA680A, + 0x15B41EA2, 0xB9DB0F9A, 0x4D6B3CD2, 0xE1042DEA, + 0x34013C87, 0x986E2DBF, 0x6CDE1EF7, 0xC0B10FCF, + 0x84BF7967, 0x28D0685F, 0xDC605B17, 0x700F4A2F, + 0x766B79CD, 0xDA0468F5, 0x2EB45BBD, 0x82DB4A85, + 0xC6D53C2D, 0x6ABA2D15, 0x9E0A1E5D, 0x32650F65, + 0xE7601E08, 0x4B0F0F30, 0xBFBF3C78, 0x13D02D40, + 0x57DE5BE8, 0xFBB14AD0, 0x0F017998, 0xA36E68A0, + }, + { + 0x00000000, 0x196B30EF, 0xC3A08CDB, 0xDACBBC34, + 0x7737F5B2, 0x6E5CC55D, 0xB4977969, 0xADFC4986, + 0x1F180660, 0x0673368F, 0xDCB88ABB, 0xC5D3BA54, + 0x682FF3D2, 0x7144C33D, 0xAB8F7F09, 0xB2E44FE6, + 0x3E300CC0, 0x275B3C2F, 0xFD90801B, 0xE4FBB0F4, + 0x4907F972, 0x506CC99D, 0x8AA775A9, 0x93CC4546, + 0x21280AA0, 0x38433A4F, 0xE288867B, 0xFBE3B694, + 0x561FFF12, 0x4F74CFFD, 0x95BF73C9, 0x8CD44326, + 0x8D16F485, 0x947DC46A, 0x4EB6785E, 0x57DD48B1, + 0xFA210137, 0xE34A31D8, 0x39818DEC, 0x20EABD03, + 0x920EF2E5, 0x8B65C20A, 0x51AE7E3E, 0x48C54ED1, + 0xE5390757, 0xFC5237B8, 0x26998B8C, 0x3FF2BB63, + 0xB326F845, 0xAA4DC8AA, 0x7086749E, 0x69ED4471, + 0xC4110DF7, 0xDD7A3D18, 0x07B1812C, 0x1EDAB1C3, + 0xAC3EFE25, 0xB555CECA, 0x6F9E72FE, 0x76F54211, + 0xDB090B97, 0xC2623B78, 0x18A9874C, 0x01C2B7A3, + 0xEB5B040E, 0xF23034E1, 0x28FB88D5, 0x3190B83A, + 0x9C6CF1BC, 0x8507C153, 0x5FCC7D67, 0x46A74D88, + 0xF443026E, 0xED283281, 0x37E38EB5, 0x2E88BE5A, + 0x8374F7DC, 0x9A1FC733, 0x40D47B07, 0x59BF4BE8, + 0xD56B08CE, 0xCC003821, 0x16CB8415, 0x0FA0B4FA, + 0xA25CFD7C, 0xBB37CD93, 0x61FC71A7, 0x78974148, + 0xCA730EAE, 0xD3183E41, 0x09D38275, 0x10B8B29A, + 0xBD44FB1C, 0xA42FCBF3, 0x7EE477C7, 0x678F4728, + 0x664DF08B, 0x7F26C064, 0xA5ED7C50, 0xBC864CBF, + 0x117A0539, 0x081135D6, 0xD2DA89E2, 0xCBB1B90D, + 0x7955F6EB, 0x603EC604, 0xBAF57A30, 0xA39E4ADF, + 0x0E620359, 0x170933B6, 0xCDC28F82, 0xD4A9BF6D, + 0x587DFC4B, 0x4116CCA4, 0x9BDD7090, 0x82B6407F, + 0x2F4A09F9, 0x36213916, 0xECEA8522, 0xF581B5CD, + 0x4765FA2B, 0x5E0ECAC4, 0x84C576F0, 0x9DAE461F, + 0x30520F99, 0x29393F76, 0xF3F28342, 0xEA99B3AD, + 0xD6B7081C, 0xCFDC38F3, 0x151784C7, 0x0C7CB428, + 0xA180FDAE, 0xB8EBCD41, 0x62207175, 0x7B4B419A, + 0xC9AF0E7C, 0xD0C43E93, 0x0A0F82A7, 0x1364B248, + 0xBE98FBCE, 0xA7F3CB21, 0x7D387715, 0x645347FA, + 0xE88704DC, 0xF1EC3433, 0x2B278807, 0x324CB8E8, + 0x9FB0F16E, 0x86DBC181, 0x5C107DB5, 0x457B4D5A, + 0xF79F02BC, 0xEEF43253, 0x343F8E67, 0x2D54BE88, + 0x80A8F70E, 0x99C3C7E1, 0x43087BD5, 0x5A634B3A, + 0x5BA1FC99, 0x42CACC76, 0x98017042, 0x816A40AD, + 0x2C96092B, 0x35FD39C4, 0xEF3685F0, 0xF65DB51F, + 0x44B9FAF9, 0x5DD2CA16, 0x87197622, 0x9E7246CD, + 0x338E0F4B, 0x2AE53FA4, 0xF02E8390, 0xE945B37F, + 0x6591F059, 0x7CFAC0B6, 0xA6317C82, 0xBF5A4C6D, + 0x12A605EB, 0x0BCD3504, 0xD1068930, 0xC86DB9DF, + 0x7A89F639, 0x63E2C6D6, 0xB9297AE2, 0xA0424A0D, + 0x0DBE038B, 0x14D53364, 0xCE1E8F50, 0xD775BFBF, + 0x3DEC0C12, 0x24873CFD, 0xFE4C80C9, 0xE727B026, + 0x4ADBF9A0, 0x53B0C94F, 0x897B757B, 0x90104594, + 0x22F40A72, 0x3B9F3A9D, 0xE15486A9, 0xF83FB646, + 0x55C3FFC0, 0x4CA8CF2F, 0x9663731B, 0x8F0843F4, + 0x03DC00D2, 0x1AB7303D, 0xC07C8C09, 0xD917BCE6, + 0x74EBF560, 0x6D80C58F, 0xB74B79BB, 0xAE204954, + 0x1CC406B2, 0x05AF365D, 0xDF648A69, 0xC60FBA86, + 0x6BF3F300, 0x7298C3EF, 0xA8537FDB, 0xB1384F34, + 0xB0FAF897, 0xA991C878, 0x735A744C, 0x6A3144A3, + 0xC7CD0D25, 0xDEA63DCA, 0x046D81FE, 0x1D06B111, + 0xAFE2FEF7, 0xB689CE18, 0x6C42722C, 0x752942C3, + 0xD8D50B45, 0xC1BE3BAA, 0x1B75879E, 0x021EB771, + 0x8ECAF457, 0x97A1C4B8, 0x4D6A788C, 0x54014863, + 0xF9FD01E5, 0xE096310A, 0x3A5D8D3E, 0x2336BDD1, + 0x91D2F237, 0x88B9C2D8, 0x52727EEC, 0x4B194E03, + 0xE6E50785, 0xFF8E376A, 0x25458B5E, 0x3C2EBBB1, + }, + { + 0x00000000, 0xC82C0368, 0x905906D0, 0x587505B8, + 0xD1C5E0A5, 0x19E9E3CD, 0x419CE675, 0x89B0E51D, + 0x53FD2D4E, 0x9BD12E26, 0xC3A42B9E, 0x0B8828F6, + 0x8238CDEB, 0x4A14CE83, 0x1261CB3B, 0xDA4DC853, + 0xA6FA5B9C, 0x6ED658F4, 0x36A35D4C, 0xFE8F5E24, + 0x773FBB39, 0xBF13B851, 0xE766BDE9, 0x2F4ABE81, + 0xF50776D2, 0x3D2B75BA, 0x655E7002, 0xAD72736A, + 0x24C29677, 0xECEE951F, 0xB49B90A7, 0x7CB793CF, + 0xBD835B3D, 0x75AF5855, 0x2DDA5DED, 0xE5F65E85, + 0x6C46BB98, 0xA46AB8F0, 0xFC1FBD48, 0x3433BE20, + 0xEE7E7673, 0x2652751B, 0x7E2770A3, 0xB60B73CB, + 0x3FBB96D6, 0xF79795BE, 0xAFE29006, 0x67CE936E, + 0x1B7900A1, 0xD35503C9, 0x8B200671, 0x430C0519, + 0xCABCE004, 0x0290E36C, 0x5AE5E6D4, 0x92C9E5BC, + 0x48842DEF, 0x80A82E87, 0xD8DD2B3F, 0x10F12857, + 0x9941CD4A, 0x516DCE22, 0x0918CB9A, 0xC134C8F2, + 0x7A07B77A, 0xB22BB412, 0xEA5EB1AA, 0x2272B2C2, + 0xABC257DF, 0x63EE54B7, 0x3B9B510F, 0xF3B75267, + 0x29FA9A34, 0xE1D6995C, 0xB9A39CE4, 0x718F9F8C, + 0xF83F7A91, 0x301379F9, 0x68667C41, 0xA04A7F29, + 0xDCFDECE6, 0x14D1EF8E, 0x4CA4EA36, 0x8488E95E, + 0x0D380C43, 0xC5140F2B, 0x9D610A93, 0x554D09FB, + 0x8F00C1A8, 0x472CC2C0, 0x1F59C778, 0xD775C410, + 0x5EC5210D, 0x96E92265, 0xCE9C27DD, 0x06B024B5, + 0xC784EC47, 0x0FA8EF2F, 0x57DDEA97, 0x9FF1E9FF, + 0x16410CE2, 0xDE6D0F8A, 0x86180A32, 0x4E34095A, + 0x9479C109, 0x5C55C261, 0x0420C7D9, 0xCC0CC4B1, + 0x45BC21AC, 0x8D9022C4, 0xD5E5277C, 0x1DC92414, + 0x617EB7DB, 0xA952B4B3, 0xF127B10B, 0x390BB263, + 0xB0BB577E, 0x78975416, 0x20E251AE, 0xE8CE52C6, + 0x32839A95, 0xFAAF99FD, 0xA2DA9C45, 0x6AF69F2D, + 0xE3467A30, 0x2B6A7958, 0x731F7CE0, 0xBB337F88, + 0xF40E6EF5, 0x3C226D9D, 0x64576825, 0xAC7B6B4D, + 0x25CB8E50, 0xEDE78D38, 0xB5928880, 0x7DBE8BE8, + 0xA7F343BB, 0x6FDF40D3, 0x37AA456B, 0xFF864603, + 0x7636A31E, 0xBE1AA076, 0xE66FA5CE, 0x2E43A6A6, + 0x52F43569, 0x9AD83601, 0xC2AD33B9, 0x0A8130D1, + 0x8331D5CC, 0x4B1DD6A4, 0x1368D31C, 0xDB44D074, + 0x01091827, 0xC9251B4F, 0x91501EF7, 0x597C1D9F, + 0xD0CCF882, 0x18E0FBEA, 0x4095FE52, 0x88B9FD3A, + 0x498D35C8, 0x81A136A0, 0xD9D43318, 0x11F83070, + 0x9848D56D, 0x5064D605, 0x0811D3BD, 0xC03DD0D5, + 0x1A701886, 0xD25C1BEE, 0x8A291E56, 0x42051D3E, + 0xCBB5F823, 0x0399FB4B, 0x5BECFEF3, 0x93C0FD9B, + 0xEF776E54, 0x275B6D3C, 0x7F2E6884, 0xB7026BEC, + 0x3EB28EF1, 0xF69E8D99, 0xAEEB8821, 0x66C78B49, + 0xBC8A431A, 0x74A64072, 0x2CD345CA, 0xE4FF46A2, + 0x6D4FA3BF, 0xA563A0D7, 0xFD16A56F, 0x353AA607, + 0x8E09D98F, 0x4625DAE7, 0x1E50DF5F, 0xD67CDC37, + 0x5FCC392A, 0x97E03A42, 0xCF953FFA, 0x07B93C92, + 0xDDF4F4C1, 0x15D8F7A9, 0x4DADF211, 0x8581F179, + 0x0C311464, 0xC41D170C, 0x9C6812B4, 0x544411DC, + 0x28F38213, 0xE0DF817B, 0xB8AA84C3, 0x708687AB, + 0xF93662B6, 0x311A61DE, 0x696F6466, 0xA143670E, + 0x7B0EAF5D, 0xB322AC35, 0xEB57A98D, 0x237BAAE5, + 0xAACB4FF8, 0x62E74C90, 0x3A924928, 0xF2BE4A40, + 0x338A82B2, 0xFBA681DA, 0xA3D38462, 0x6BFF870A, + 0xE24F6217, 0x2A63617F, 0x721664C7, 0xBA3A67AF, + 0x6077AFFC, 0xA85BAC94, 0xF02EA92C, 0x3802AA44, + 0xB1B24F59, 0x799E4C31, 0x21EB4989, 0xE9C74AE1, + 0x9570D92E, 0x5D5CDA46, 0x0529DFFE, 0xCD05DC96, + 0x44B5398B, 0x8C993AE3, 0xD4EC3F5B, 0x1CC03C33, + 0xC68DF460, 0x0EA1F708, 0x56D4F2B0, 0x9EF8F1D8, + 0x174814C5, 0xDF6417AD, 0x87111215, 0x4F3D117D, + }, + { + 0x00000000, 0x277D3C49, 0x4EFA7892, 0x698744DB, + 0x6D821D21, 0x4AFF2168, 0x237865B3, 0x040559FA, + 0xDA043B42, 0xFD79070B, 0x94FE43D0, 0xB3837F99, + 0xB7862663, 0x90FB1A2A, 0xF97C5EF1, 0xDE0162B8, + 0xB4097684, 0x93744ACD, 0xFAF30E16, 0xDD8E325F, + 0xD98B6BA5, 0xFEF657EC, 0x97711337, 0xB00C2F7E, + 0x6E0D4DC6, 0x4970718F, 0x20F73554, 0x078A091D, + 0x038F50E7, 0x24F26CAE, 0x4D752875, 0x6A08143C, + 0x9965000D, 0xBE183C44, 0xD79F789F, 0xF0E244D6, + 0xF4E71D2C, 0xD39A2165, 0xBA1D65BE, 0x9D6059F7, + 0x43613B4F, 0x641C0706, 0x0D9B43DD, 0x2AE67F94, + 0x2EE3266E, 0x099E1A27, 0x60195EFC, 0x476462B5, + 0x2D6C7689, 0x0A114AC0, 0x63960E1B, 0x44EB3252, + 0x40EE6BA8, 0x679357E1, 0x0E14133A, 0x29692F73, + 0xF7684DCB, 0xD0157182, 0xB9923559, 0x9EEF0910, + 0x9AEA50EA, 0xBD976CA3, 0xD4102878, 0xF36D1431, + 0x32CB001A, 0x15B63C53, 0x7C317888, 0x5B4C44C1, + 0x5F491D3B, 0x78342172, 0x11B365A9, 0x36CE59E0, + 0xE8CF3B58, 0xCFB20711, 0xA63543CA, 0x81487F83, + 0x854D2679, 0xA2301A30, 0xCBB75EEB, 0xECCA62A2, + 0x86C2769E, 0xA1BF4AD7, 0xC8380E0C, 0xEF453245, + 0xEB406BBF, 0xCC3D57F6, 0xA5BA132D, 0x82C72F64, + 0x5CC64DDC, 0x7BBB7195, 0x123C354E, 0x35410907, + 0x314450FD, 0x16396CB4, 0x7FBE286F, 0x58C31426, + 0xABAE0017, 0x8CD33C5E, 0xE5547885, 0xC22944CC, + 0xC62C1D36, 0xE151217F, 0x88D665A4, 0xAFAB59ED, + 0x71AA3B55, 0x56D7071C, 0x3F5043C7, 0x182D7F8E, + 0x1C282674, 0x3B551A3D, 0x52D25EE6, 0x75AF62AF, + 0x1FA77693, 0x38DA4ADA, 0x515D0E01, 0x76203248, + 0x72256BB2, 0x555857FB, 0x3CDF1320, 0x1BA22F69, + 0xC5A34DD1, 0xE2DE7198, 0x8B593543, 0xAC24090A, + 0xA82150F0, 0x8F5C6CB9, 0xE6DB2862, 0xC1A6142B, + 0x64960134, 0x43EB3D7D, 0x2A6C79A6, 0x0D1145EF, + 0x09141C15, 0x2E69205C, 0x47EE6487, 0x609358CE, + 0xBE923A76, 0x99EF063F, 0xF06842E4, 0xD7157EAD, + 0xD3102757, 0xF46D1B1E, 0x9DEA5FC5, 0xBA97638C, + 0xD09F77B0, 0xF7E24BF9, 0x9E650F22, 0xB918336B, + 0xBD1D6A91, 0x9A6056D8, 0xF3E71203, 0xD49A2E4A, + 0x0A9B4CF2, 0x2DE670BB, 0x44613460, 0x631C0829, + 0x671951D3, 0x40646D9A, 0x29E32941, 0x0E9E1508, + 0xFDF30139, 0xDA8E3D70, 0xB30979AB, 0x947445E2, + 0x90711C18, 0xB70C2051, 0xDE8B648A, 0xF9F658C3, + 0x27F73A7B, 0x008A0632, 0x690D42E9, 0x4E707EA0, + 0x4A75275A, 0x6D081B13, 0x048F5FC8, 0x23F26381, + 0x49FA77BD, 0x6E874BF4, 0x07000F2F, 0x207D3366, + 0x24786A9C, 0x030556D5, 0x6A82120E, 0x4DFF2E47, + 0x93FE4CFF, 0xB48370B6, 0xDD04346D, 0xFA790824, + 0xFE7C51DE, 0xD9016D97, 0xB086294C, 0x97FB1505, + 0x565D012E, 0x71203D67, 0x18A779BC, 0x3FDA45F5, + 0x3BDF1C0F, 0x1CA22046, 0x7525649D, 0x525858D4, + 0x8C593A6C, 0xAB240625, 0xC2A342FE, 0xE5DE7EB7, + 0xE1DB274D, 0xC6A61B04, 0xAF215FDF, 0x885C6396, + 0xE25477AA, 0xC5294BE3, 0xACAE0F38, 0x8BD33371, + 0x8FD66A8B, 0xA8AB56C2, 0xC12C1219, 0xE6512E50, + 0x38504CE8, 0x1F2D70A1, 0x76AA347A, 0x51D70833, + 0x55D251C9, 0x72AF6D80, 0x1B28295B, 0x3C551512, + 0xCF380123, 0xE8453D6A, 0x81C279B1, 0xA6BF45F8, + 0xA2BA1C02, 0x85C7204B, 0xEC406490, 0xCB3D58D9, + 0x153C3A61, 0x32410628, 0x5BC642F3, 0x7CBB7EBA, + 0x78BE2740, 0x5FC31B09, 0x36445FD2, 0x1139639B, + 0x7B3177A7, 0x5C4C4BEE, 0x35CB0F35, 0x12B6337C, + 0x16B36A86, 0x31CE56CF, 0x58491214, 0x7F342E5D, + 0xA1354CE5, 0x864870AC, 0xEFCF3477, 0xC8B2083E, + 0xCCB751C4, 0xEBCA6D8D, 0x824D2956, 0xA530151F + } +#endif /* WORDS_BIGENDIAN */ +}; diff --git a/src/port/pg_crc32c_sse42.c b/src/port/pg_crc32c_sse42.c new file mode 100644 index 0000000..0bac452 --- /dev/null +++ b/src/port/pg_crc32c_sse42.c @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- + * + * pg_crc32c_sse42.c + * Compute CRC-32C checksum using Intel SSE 4.2 instructions. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pg_crc32c_sse42.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include <nmmintrin.h> + +#include "port/pg_crc32c.h" + +pg_attribute_no_sanitize_alignment() +pg_crc32c +pg_comp_crc32c_sse42(pg_crc32c crc, const void *data, size_t len) +{ + const unsigned char *p = data; + const unsigned char *pend = p + len; + + /* + * Process eight bytes of data at a time. + * + * NB: We do unaligned accesses here. The Intel architecture allows that, + * and performance testing didn't show any performance gain from aligning + * the begin address. + */ +#ifdef __x86_64__ + while (p + 8 <= pend) + { + crc = (uint32) _mm_crc32_u64(crc, *((const uint64 *) p)); + p += 8; + } + + /* Process remaining full four bytes if any */ + if (p + 4 <= pend) + { + crc = _mm_crc32_u32(crc, *((const unsigned int *) p)); + p += 4; + } +#else + + /* + * Process four bytes at a time. (The eight byte instruction is not + * available on the 32-bit x86 architecture). + */ + while (p + 4 <= pend) + { + crc = _mm_crc32_u32(crc, *((const unsigned int *) p)); + p += 4; + } +#endif /* __x86_64__ */ + + /* Process any remaining bytes one at a time. */ + while (p < pend) + { + crc = _mm_crc32_u8(crc, *p); + p++; + } + + return crc; +} diff --git a/src/port/pg_crc32c_sse42_choose.c b/src/port/pg_crc32c_sse42_choose.c new file mode 100644 index 0000000..41ff4a3 --- /dev/null +++ b/src/port/pg_crc32c_sse42_choose.c @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * pg_crc32c_sse42_choose.c + * Choose between Intel SSE 4.2 and software CRC-32C implementation. + * + * On first call, checks if the CPU we're running on supports Intel SSE + * 4.2. If it does, use the special SSE instructions for CRC-32C + * computation. Otherwise, fall back to the pure software implementation + * (slicing-by-8). + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pg_crc32c_sse42_choose.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#ifdef HAVE__GET_CPUID +#include <cpuid.h> +#endif + +#ifdef HAVE__CPUID +#include <intrin.h> +#endif + +#include "port/pg_crc32c.h" + +static bool +pg_crc32c_sse42_available(void) +{ + unsigned int exx[4] = {0, 0, 0, 0}; + +#if defined(HAVE__GET_CPUID) + __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]); +#elif defined(HAVE__CPUID) + __cpuid(exx, 1); +#else +#error cpuid instruction not available +#endif + + return (exx[2] & (1 << 20)) != 0; /* SSE 4.2 */ +} + +/* + * This gets called on the first call. It replaces the function pointer + * so that subsequent calls are routed directly to the chosen implementation. + */ +static pg_crc32c +pg_comp_crc32c_choose(pg_crc32c crc, const void *data, size_t len) +{ + if (pg_crc32c_sse42_available()) + pg_comp_crc32c = pg_comp_crc32c_sse42; + else + pg_comp_crc32c = pg_comp_crc32c_sb8; + + return pg_comp_crc32c(crc, data, len); +} + +pg_crc32c (*pg_comp_crc32c) (pg_crc32c crc, const void *data, size_t len) = pg_comp_crc32c_choose; diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c new file mode 100644 index 0000000..862c84a --- /dev/null +++ b/src/port/pg_strong_random.c @@ -0,0 +1,182 @@ +/*------------------------------------------------------------------------- + * + * pg_strong_random.c + * generate a cryptographically secure random number + * + * Our definition of "strong" is that it's suitable for generating random + * salts and query cancellation keys, during authentication. + * + * Note: this code is run quite early in postmaster and backend startup; + * therefore, even when built for backend, it cannot rely on backend + * infrastructure such as elog() or palloc(). + * + * Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/pg_strong_random.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> + +/* + * pg_strong_random & pg_strong_random_init + * + * Generate requested number of random bytes. The returned bytes are + * cryptographically secure, suitable for use e.g. in authentication. + * + * Before pg_strong_random is called in any process, the generator must first + * be initialized by calling pg_strong_random_init(). + * + * We rely on system facilities for actually generating the numbers. + * We support a number of sources: + * + * 1. OpenSSL's RAND_bytes() + * 2. Windows' CryptGenRandom() function + * 3. /dev/urandom + * + * Returns true on success, and false if none of the sources + * were available. NB: It is important to check the return value! + * Proceeding with key generation when no random data was available + * would lead to predictable keys and security issues. + */ + + + +#ifdef USE_OPENSSL + +#include <openssl/rand.h> + +void +pg_strong_random_init(void) +{ + /* + * Make sure processes do not share OpenSSL randomness state. This is no + * longer required in OpenSSL 1.1.1 and later versions, but until we drop + * support for version < 1.1.1 we need to do this. + */ + RAND_poll(); +} + +bool +pg_strong_random(void *buf, size_t len) +{ + int i; + + /* + * Check that OpenSSL's CSPRNG has been sufficiently seeded, and if not + * add more seed data using RAND_poll(). With some older versions of + * OpenSSL, it may be necessary to call RAND_poll() a number of times. If + * RAND_poll() fails to generate seed data within the given amount of + * retries, subsequent RAND_bytes() calls will fail, but we allow that to + * happen to let pg_strong_random() callers handle that with appropriate + * error handling. + */ +#define NUM_RAND_POLL_RETRIES 8 + + for (i = 0; i < NUM_RAND_POLL_RETRIES; i++) + { + if (RAND_status() == 1) + { + /* The CSPRNG is sufficiently seeded */ + break; + } + + RAND_poll(); + } + + if (RAND_bytes(buf, len) == 1) + return true; + return false; +} + +#elif WIN32 + +#include <wincrypt.h> +/* + * Cache a global crypto provider that only gets freed when the process + * exits, in case we need random numbers more than once. + */ +static HCRYPTPROV hProvider = 0; + +void +pg_strong_random_init(void) +{ + /* No initialization needed on WIN32 */ +} + +bool +pg_strong_random(void *buf, size_t len) +{ + if (hProvider == 0) + { + if (!CryptAcquireContext(&hProvider, + NULL, + MS_DEF_PROV, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + /* + * On failure, set back to 0 in case the value was for some reason + * modified. + */ + hProvider = 0; + } + } + /* Re-check in case we just retrieved the provider */ + if (hProvider != 0) + { + if (CryptGenRandom(hProvider, len, buf)) + return true; + } + return false; +} + +#else /* not USE_OPENSSL or WIN32 */ + +/* + * Without OpenSSL or Win32 support, just read /dev/urandom ourselves. + */ + +void +pg_strong_random_init(void) +{ + /* No initialization needed */ +} + +bool +pg_strong_random(void *buf, size_t len) +{ + int f; + char *p = buf; + ssize_t res; + + f = open("/dev/urandom", O_RDONLY, 0); + if (f == -1) + return false; + + while (len) + { + res = read(f, p, len); + if (res <= 0) + { + if (errno == EINTR) + continue; /* interrupted by signal, just retry */ + + close(f); + return false; + } + + p += res; + len -= res; + } + + close(f); + return true; +} +#endif diff --git a/src/port/pgcheckdir.c b/src/port/pgcheckdir.c new file mode 100644 index 0000000..94aa40c --- /dev/null +++ b/src/port/pgcheckdir.c @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------- + * + * pgcheckdir.c + * + * A simple subroutine to check whether a directory exists and is empty or not. + * Useful in both initdb and the backend. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/pgcheckdir.c + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <dirent.h> + + +/* + * Test to see if a directory exists and is empty or not. + * + * Returns: + * 0 if nonexistent + * 1 if exists and empty + * 2 if exists and contains _only_ dot files + * 3 if exists and contains a mount point + * 4 if exists and not empty + * -1 if trouble accessing directory (errno reflects the error) + */ +int +pg_check_dir(const char *dir) +{ + int result = 1; + DIR *chkdir; + struct dirent *file; + bool dot_found = false; + bool mount_found = false; + int readdir_errno; + + chkdir = opendir(dir); + if (chkdir == NULL) + return (errno == ENOENT) ? 0 : -1; + + while (errno = 0, (file = readdir(chkdir)) != NULL) + { + if (strcmp(".", file->d_name) == 0 || + strcmp("..", file->d_name) == 0) + { + /* skip this and parent directory */ + continue; + } +#ifndef WIN32 + /* file starts with "." */ + else if (file->d_name[0] == '.') + { + dot_found = true; + } + /* lost+found directory */ + else if (strcmp("lost+found", file->d_name) == 0) + { + mount_found = true; + } +#endif + else + { + result = 4; /* not empty */ + break; + } + } + + if (errno) + result = -1; /* some kind of I/O error? */ + + /* Close chkdir and avoid overwriting the readdir errno on success */ + readdir_errno = errno; + if (closedir(chkdir)) + result = -1; /* error executing closedir */ + else + errno = readdir_errno; + + /* We report on mount point if we find a lost+found directory */ + if (result == 1 && mount_found) + result = 3; + + /* We report on dot-files if we _only_ find dot files */ + if (result == 1 && dot_found) + result = 2; + + return result; +} diff --git a/src/port/pgmkdirp.c b/src/port/pgmkdirp.c new file mode 100644 index 0000000..d943559 --- /dev/null +++ b/src/port/pgmkdirp.c @@ -0,0 +1,148 @@ +/* + * This is adapted from FreeBSD's src/bin/mkdir/mkdir.c, which bears + * the following copyright notice: + * + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "c.h" + +#include <sys/stat.h> + + +/* + * pg_mkdir_p --- create a directory and, if necessary, parent directories + * + * This is equivalent to "mkdir -p" except we don't complain if the target + * directory already exists. + * + * We assume the path is in canonical form, i.e., uses / as the separator. + * + * omode is the file permissions bits for the target directory. Note that any + * parent directories that have to be created get permissions according to the + * prevailing umask, but with u+wx forced on to ensure we can create there. + * (We declare omode as int, not mode_t, to minimize dependencies for port.h.) + * + * Returns 0 on success, -1 (with errno set) on failure. + * + * Note that on failure, the path arg has been modified to show the particular + * directory level we had problems with. + */ +int +pg_mkdir_p(char *path, int omode) +{ + struct stat sb; + mode_t numask, + oumask; + int last, + retval; + char *p; + + retval = 0; + p = path; + +#ifdef WIN32 + /* skip network and drive specifiers for win32 */ + if (strlen(p) >= 2) + { + if (p[0] == '/' && p[1] == '/') + { + /* network drive */ + p = strstr(p + 2, "/"); + if (p == NULL) + { + errno = EINVAL; + return -1; + } + } + else if (p[1] == ':' && + ((p[0] >= 'a' && p[0] <= 'z') || + (p[0] >= 'A' && p[0] <= 'Z'))) + { + /* local drive */ + p += 2; + } + } +#endif + + /* + * POSIX 1003.2: For each dir operand that does not name an existing + * directory, effects equivalent to those caused by the following command + * shall occur: + * + * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir + * + * We change the user's umask and then restore it, instead of doing + * chmod's. Note we assume umask() can't change errno. + */ + oumask = umask(0); + numask = oumask & ~(S_IWUSR | S_IXUSR); + (void) umask(numask); + + if (p[0] == '/') /* Skip leading '/'. */ + ++p; + for (last = 0; !last; ++p) + { + if (p[0] == '\0') + last = 1; + else if (p[0] != '/') + continue; + *p = '\0'; + if (!last && p[1] == '\0') + last = 1; + + if (last) + (void) umask(oumask); + + /* check for pre-existing directory */ + if (stat(path, &sb) == 0) + { + if (!S_ISDIR(sb.st_mode)) + { + if (last) + errno = EEXIST; + else + errno = ENOTDIR; + retval = -1; + break; + } + } + else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) + { + retval = -1; + break; + } + if (!last) + *p = '/'; + } + + /* ensure we restored umask */ + (void) umask(oumask); + + return retval; +} diff --git a/src/port/pgsleep.c b/src/port/pgsleep.c new file mode 100644 index 0000000..a1bcd78 --- /dev/null +++ b/src/port/pgsleep.c @@ -0,0 +1,57 @@ +/*------------------------------------------------------------------------- + * + * pgsleep.c + * Portable delay handling. + * + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * src/port/pgsleep.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include <time.h> + +/* + * In a Windows backend, we don't use this implementation, but rather + * the signal-aware version in src/backend/port/win32/signal.c. + */ +#if defined(FRONTEND) || !defined(WIN32) + +/* + * pg_usleep --- delay the specified number of microseconds. + * + * NOTE: Although the delay is specified in microseconds, older Unixen and + * Windows use periodic kernel ticks to wake up, which might increase the delay + * time significantly. We've observed delay increases as large as 20 + * milliseconds on supported platforms. + * + * On machines where "long" is 32 bits, the maximum delay is ~2000 seconds. + * + * CAUTION: It's not a good idea to use long sleeps in the backend. They will + * silently return early if a signal is caught, but that doesn't include + * latches being set on most OSes, and even signal handlers that set MyLatch + * might happen to run before the sleep begins, allowing the full delay. + * Better practice is to use WaitLatch() with a timeout, so that backends + * respond to latches and signals promptly. + */ +void +pg_usleep(long microsec) +{ + if (microsec > 0) + { +#ifndef WIN32 + struct timespec delay; + + delay.tv_sec = microsec / 1000000L; + delay.tv_nsec = (microsec % 1000000L) * 1000; + (void) nanosleep(&delay, NULL); +#else + SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE); +#endif + } +} + +#endif /* defined(FRONTEND) || !defined(WIN32) */ diff --git a/src/port/pgstrcasecmp.c b/src/port/pgstrcasecmp.c new file mode 100644 index 0000000..27c1546 --- /dev/null +++ b/src/port/pgstrcasecmp.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------- + * + * pgstrcasecmp.c + * Portable SQL-like case-independent comparisons and conversions. + * + * SQL99 specifies Unicode-aware case normalization, which we don't yet + * have the infrastructure for. Instead we use tolower() to provide a + * locale-aware translation. However, there are some locales where this + * is not right either (eg, Turkish may do strange things with 'i' and + * 'I'). Our current compromise is to use tolower() for characters with + * the high bit set, and use an ASCII-only downcasing for 7-bit + * characters. + * + * NB: this code should match downcase_truncate_identifier() in scansup.c. + * + * We also provide strict ASCII-only case conversion functions, which can + * be used to implement C/POSIX case folding semantics no matter what the + * C library thinks the locale is. + * + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * src/port/pgstrcasecmp.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include <ctype.h> + + +/* + * Case-independent comparison of two null-terminated strings. + */ +int +pg_strcasecmp(const char *s1, const char *s2) +{ + for (;;) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Case-independent comparison of two not-necessarily-null-terminated strings. + * At most n bytes will be examined from each string. + */ +int +pg_strncasecmp(const char *s1, const char *s2, size_t n) +{ + while (n-- > 0) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Fold a character to upper case. + * + * Unlike some versions of toupper(), this is safe to apply to characters + * that aren't lower case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + else if (IS_HIGHBIT_SET(ch) && islower(ch)) + ch = toupper(ch); + return ch; +} + +/* + * Fold a character to lower case. + * + * Unlike some versions of tolower(), this is safe to apply to characters + * that aren't upper case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch) && isupper(ch)) + ch = tolower(ch); + return ch; +} + +/* + * Fold a character to upper case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + return ch; +} + +/* + * Fold a character to lower case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + return ch; +} diff --git a/src/port/pgstrsignal.c b/src/port/pgstrsignal.c new file mode 100644 index 0000000..7d76d1c --- /dev/null +++ b/src/port/pgstrsignal.c @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * pgstrsignal.c + * Identify a Unix signal number + * + * On platforms compliant with modern POSIX, this just wraps strsignal(3). + * Elsewhere, we do the best we can. + * + * This file is not currently built in MSVC builds, since it's useless + * on non-Unix platforms. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/pgstrsignal.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + + +/* + * pg_strsignal + * + * Return a string identifying the given Unix signal number. + * + * The result is declared "const char *" because callers should not + * modify the string. Note, however, that POSIX does not promise that + * the string will remain valid across later calls to strsignal(). + * + * This version guarantees to return a non-NULL pointer, although + * some platforms' versions of strsignal() reputedly do not. + * + * Note that the fallback cases just return constant strings such as + * "unrecognized signal". Project style is for callers to print the + * numeric signal value along with the result of this function, so + * there's no need to work harder than that. + */ +const char * +pg_strsignal(int signum) +{ + const char *result; + + /* + * If we have strsignal(3), use that --- but check its result for NULL. + */ +#ifdef HAVE_STRSIGNAL + result = strsignal(signum); + if (result == NULL) + result = "unrecognized signal"; +#else + + /* + * We used to have code here to try to use sys_siglist[] if available. + * However, it seems that all platforms with sys_siglist[] have also had + * strsignal() for many years now, so that was just a waste of code. + */ + result = "(signal names not available on this platform)"; +#endif + + return result; +} diff --git a/src/port/pqsignal.c b/src/port/pqsignal.c new file mode 100644 index 0000000..83d876d --- /dev/null +++ b/src/port/pqsignal.c @@ -0,0 +1,62 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.c + * reliable BSD-style signal(2) routine stolen from RWW who stole it + * from Stevens... + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pqsignal.c + * + * We now assume that all Unix-oid systems have POSIX sigaction(2) + * with support for restartable signals (SA_RESTART). We used to also + * support BSD-style signal(2), but there really shouldn't be anything + * out there anymore that doesn't have the POSIX API. + * + * Windows, of course, is resolutely in a class by itself. In the backend, + * we don't use this file at all; src/backend/port/win32/signal.c provides + * pqsignal() for the backend environment. Frontend programs can use + * this version of pqsignal() if they wish, but beware that this does + * not provide restartable signals on Windows. + * + * ------------------------------------------------------------------------ + */ + +#include "c.h" + +#include <signal.h> + +#ifndef FRONTEND +#include "libpq/pqsignal.h" +#endif + +/* + * Set up a signal handler, with SA_RESTART, for signal "signo" + * + * Returns the previous handler. + */ +pqsigfunc +pqsignal(int signo, pqsigfunc func) +{ +#if !(defined(WIN32) && defined(FRONTEND)) + struct sigaction act, + oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; +#ifdef SA_NOCLDSTOP + if (signo == SIGCHLD) + act.sa_flags |= SA_NOCLDSTOP; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +#else + /* Forward to Windows native signal system. */ + return signal(signo, func); +#endif +} diff --git a/src/port/preadv.c b/src/port/preadv.c new file mode 100644 index 0000000..e762283 --- /dev/null +++ b/src/port/preadv.c @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------- + * + * preadv.c + * Implementation of preadv(2) for platforms that lack one. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/preadv.c + * + *------------------------------------------------------------------------- + */ + + +#include "c.h" + +#include <unistd.h> + +#include "port/pg_iovec.h" + +ssize_t +pg_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + ssize_t sum = 0; + ssize_t part; + + for (int i = 0; i < iovcnt; ++i) + { + part = pg_pread(fd, iov[i].iov_base, iov[i].iov_len, offset); + if (part < 0) + { + if (i == 0) + return -1; + else + return sum; + } + sum += part; + offset += part; + if (part < iov[i].iov_len) + return sum; + } + return sum; +} diff --git a/src/port/pthread-win32.h b/src/port/pthread-win32.h new file mode 100644 index 0000000..97ccc17 --- /dev/null +++ b/src/port/pthread-win32.h @@ -0,0 +1,22 @@ +/* + * src/port/pthread-win32.h + */ +#ifndef __PTHREAD_H +#define __PTHREAD_H + +typedef ULONG pthread_key_t; +typedef CRITICAL_SECTION *pthread_mutex_t; +typedef int pthread_once_t; + +DWORD pthread_self(void); + +void pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); + +int pthread_mutex_init(pthread_mutex_t *, void *attr); +int pthread_mutex_lock(pthread_mutex_t *); + +/* blocking */ +int pthread_mutex_unlock(pthread_mutex_t *); + +#endif diff --git a/src/port/pthread_barrier_wait.c b/src/port/pthread_barrier_wait.c new file mode 100644 index 0000000..8d3cdb9 --- /dev/null +++ b/src/port/pthread_barrier_wait.c @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------- + * + * pthread_barrier_wait.c + * Implementation of pthread_barrier_t support for platforms lacking it. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/pthread_barrier_wait.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include "port/pg_pthread.h" + +int +pthread_barrier_init(pthread_barrier_t *barrier, const void *attr, int count) +{ + int error; + + barrier->sense = false; + barrier->count = count; + barrier->arrived = 0; + if ((error = pthread_cond_init(&barrier->cond, NULL)) != 0) + return error; + if ((error = pthread_mutex_init(&barrier->mutex, NULL)) != 0) + { + pthread_cond_destroy(&barrier->cond); + return error; + } + + return 0; +} + +int +pthread_barrier_wait(pthread_barrier_t *barrier) +{ + bool initial_sense; + + pthread_mutex_lock(&barrier->mutex); + + /* We have arrived at the barrier. */ + barrier->arrived++; + Assert(barrier->arrived <= barrier->count); + + /* If we were the last to arrive, release the others and return. */ + if (barrier->arrived == barrier->count) + { + barrier->arrived = 0; + barrier->sense = !barrier->sense; + pthread_mutex_unlock(&barrier->mutex); + pthread_cond_broadcast(&barrier->cond); + + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + /* Wait for someone else to flip the sense. */ + initial_sense = barrier->sense; + do + { + pthread_cond_wait(&barrier->cond, &barrier->mutex); + } while (barrier->sense == initial_sense); + + pthread_mutex_unlock(&barrier->mutex); + + return 0; +} + +int +pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} diff --git a/src/port/pwritev.c b/src/port/pwritev.c new file mode 100644 index 0000000..519de45 --- /dev/null +++ b/src/port/pwritev.c @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------- + * + * pwritev.c + * Implementation of pwritev(2) for platforms that lack one. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/pwritev.c + * + *------------------------------------------------------------------------- + */ + + +#include "c.h" + +#include <unistd.h> + +#include "port/pg_iovec.h" + +ssize_t +pg_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + ssize_t sum = 0; + ssize_t part; + + for (int i = 0; i < iovcnt; ++i) + { + part = pg_pwrite(fd, iov[i].iov_base, iov[i].iov_len, offset); + if (part < 0) + { + if (i == 0) + return -1; + else + return sum; + } + sum += part; + offset += part; + if (part < iov[i].iov_len) + return sum; + } + return sum; +} diff --git a/src/port/qsort.c b/src/port/qsort.c new file mode 100644 index 0000000..7879e6c --- /dev/null +++ b/src/port/qsort.c @@ -0,0 +1,22 @@ +/* + * qsort.c: standard quicksort algorithm + */ + +#include "c.h" + +#define ST_SORT pg_qsort +#define ST_ELEMENT_TYPE_VOID +#define ST_COMPARE_RUNTIME_POINTER +#define ST_SCOPE +#define ST_DECLARE +#define ST_DEFINE +#include "lib/sort_template.h" + +/* + * qsort comparator wrapper for strcmp. + */ +int +pg_qsort_strcmp(const void *a, const void *b) +{ + return strcmp(*(const char *const *) a, *(const char *const *) b); +} diff --git a/src/port/qsort_arg.c b/src/port/qsort_arg.c new file mode 100644 index 0000000..fa7e11a --- /dev/null +++ b/src/port/qsort_arg.c @@ -0,0 +1,14 @@ +/* + * qsort_arg.c: qsort with a passthrough "void *" argument + */ + +#include "c.h" + +#define ST_SORT qsort_arg +#define ST_ELEMENT_TYPE_VOID +#define ST_COMPARATOR_TYPE_NAME qsort_arg_comparator +#define ST_COMPARE_RUNTIME_POINTER +#define ST_COMPARE_ARG_TYPE void +#define ST_SCOPE +#define ST_DEFINE +#include "lib/sort_template.h" diff --git a/src/port/quotes.c b/src/port/quotes.c new file mode 100644 index 0000000..a80e88d --- /dev/null +++ b/src/port/quotes.c @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * quotes.c + * string quoting and escaping functions + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/quotes.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +/* + * Escape (by doubling) any single quotes or backslashes in given string + * + * Note: this is used to process postgresql.conf entries and to quote + * string literals in pg_basebackup for writing the recovery configuration. + * Since postgresql.conf strings are defined to treat backslashes as escapes, + * we have to double backslashes here. + * + * Since this function is only used for parsing or creating configuration + * files, we do not care about encoding considerations. + * + * Returns a malloced() string that it's the responsibility of the caller + * to free. + */ +char * +escape_single_quotes_ascii(const char *src) +{ + int len = strlen(src), + i, + j; + char *result = malloc(len * 2 + 1); + + if (!result) + return NULL; + + for (i = 0, j = 0; i < len; i++) + { + if (SQL_STR_DOUBLE(src[i], true)) + result[j++] = src[i]; + result[j++] = src[i]; + } + result[j] = '\0'; + return result; +} diff --git a/src/port/snprintf.c b/src/port/snprintf.c new file mode 100644 index 0000000..e3e316e --- /dev/null +++ b/src/port/snprintf.c @@ -0,0 +1,1535 @@ +/* + * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * src/port/snprintf.c + */ + +#include "c.h" + +#include <math.h> + +/* + * We used to use the platform's NL_ARGMAX here, but that's a bad idea, + * first because the point of this module is to remove platform dependencies + * not perpetuate them, and second because some platforms use ridiculously + * large values, leading to excessive stack consumption in dopr(). + */ +#define PG_NL_ARGMAX 31 + + +/* + * SNPRINTF, VSNPRINTF and friends + * + * These versions have been grabbed off the net. They have been + * cleaned up to compile properly and support for most of the C99 + * specification has been added. Remaining unimplemented features are: + * + * 1. No locale support: the radix character is always '.' and the ' + * (single quote) format flag is ignored. + * + * 2. No support for the "%n" format specification. + * + * 3. No support for wide characters ("lc" and "ls" formats). + * + * 4. No support for "long double" ("Lf" and related formats). + * + * 5. Space and '#' flags are not implemented. + * + * In addition, we support some extensions over C99: + * + * 1. Argument order control through "%n$" and "*n$", as required by POSIX. + * + * 2. "%m" expands to the value of strerror(errno), where errno is the + * value that variable had at the start of the call. This is a glibc + * extension, but a very useful one. + * + * + * Historically the result values of sprintf/snprintf varied across platforms. + * This implementation now follows the C99 standard: + * + * 1. -1 is returned if an error is detected in the format string, or if + * a write to the target stream fails (as reported by fwrite). Note that + * overrunning snprintf's target buffer is *not* an error. + * + * 2. For successful writes to streams, the actual number of bytes written + * to the stream is returned. + * + * 3. For successful sprintf/snprintf, the number of bytes that would have + * been written to an infinite-size buffer (excluding the trailing '\0') + * is returned. snprintf will truncate its output to fit in the buffer + * (ensuring a trailing '\0' unless count == 0), but this is not reflected + * in the function result. + * + * snprintf buffer overrun can be detected by checking for function result + * greater than or equal to the supplied count. + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point. (now it does ... tgl) + **************************************************************/ + +/* Prevent recursion */ +#undef vsnprintf +#undef snprintf +#undef vsprintf +#undef sprintf +#undef vfprintf +#undef fprintf +#undef vprintf +#undef printf + +/* + * Info about where the formatted output is going. + * + * dopr and subroutines will not write at/past bufend, but snprintf + * reserves one byte, ensuring it may place the trailing '\0' there. + * + * In snprintf, we use nchars to count the number of bytes dropped on the + * floor due to buffer overrun. The correct result of snprintf is thus + * (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might + * seem: nchars is the number of emitted bytes that are not in the buffer now, + * either because we sent them to the stream or because we couldn't fit them + * into the buffer to begin with.) + */ +typedef struct +{ + char *bufptr; /* next buffer output position */ + char *bufstart; /* first buffer element */ + char *bufend; /* last+1 buffer element, or NULL */ + /* bufend == NULL is for sprintf, where we assume buf is big enough */ + FILE *stream; /* eventual output destination, or NULL */ + int nchars; /* # chars sent to stream, or dropped */ + bool failed; /* call is a failure; errno is set */ +} PrintfTarget; + +/* + * Info about the type and value of a formatting parameter. Note that we + * don't currently support "long double", "wint_t", or "wchar_t *" data, + * nor the '%n' formatting code; else we'd need more types. Also, at this + * level we need not worry about signed vs unsigned values. + */ +typedef enum +{ + ATYPE_NONE = 0, + ATYPE_INT, + ATYPE_LONG, + ATYPE_LONGLONG, + ATYPE_DOUBLE, + ATYPE_CHARPTR +} PrintfArgType; + +typedef union +{ + int i; + long l; + long long ll; + double d; + char *cptr; +} PrintfArgValue; + + +static void flushbuffer(PrintfTarget *target); +static void dopr(PrintfTarget *target, const char *format, va_list args); + + +/* + * Externally visible entry points. + * + * All of these are just wrappers around dopr(). Note it's essential that + * they not change the value of "errno" before reaching dopr(). + */ + +int +pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + PrintfTarget target; + char onebyte[1]; + + /* + * C99 allows the case str == NULL when count == 0. Rather than + * special-casing this situation further down, we substitute a one-byte + * local buffer. Callers cannot tell, since the function result doesn't + * depend on count. + */ + if (count == 0) + { + str = onebyte; + count = 1; + } + target.bufstart = target.bufptr = str; + target.bufend = str + count - 1; + target.stream = NULL; + target.nchars = 0; + target.failed = false; + dopr(&target, fmt, args); + *(target.bufptr) = '\0'; + return target.failed ? -1 : (target.bufptr - target.bufstart + + target.nchars); +} + +int +pg_snprintf(char *str, size_t count, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsnprintf(str, count, fmt, args); + va_end(args); + return len; +} + +int +pg_vsprintf(char *str, const char *fmt, va_list args) +{ + PrintfTarget target; + + target.bufstart = target.bufptr = str; + target.bufend = NULL; + target.stream = NULL; + target.nchars = 0; /* not really used in this case */ + target.failed = false; + dopr(&target, fmt, args); + *(target.bufptr) = '\0'; + return target.failed ? -1 : (target.bufptr - target.bufstart + + target.nchars); +} + +int +pg_sprintf(char *str, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsprintf(str, fmt, args); + va_end(args); + return len; +} + +int +pg_vfprintf(FILE *stream, const char *fmt, va_list args) +{ + PrintfTarget target; + char buffer[1024]; /* size is arbitrary */ + + if (stream == NULL) + { + errno = EINVAL; + return -1; + } + target.bufstart = target.bufptr = buffer; + target.bufend = buffer + sizeof(buffer); /* use the whole buffer */ + target.stream = stream; + target.nchars = 0; + target.failed = false; + dopr(&target, fmt, args); + /* dump any remaining buffer contents */ + flushbuffer(&target); + return target.failed ? -1 : target.nchars; +} + +int +pg_fprintf(FILE *stream, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stream, fmt, args); + va_end(args); + return len; +} + +int +pg_vprintf(const char *fmt, va_list args) +{ + return pg_vfprintf(stdout, fmt, args); +} + +int +pg_printf(const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stdout, fmt, args); + va_end(args); + return len; +} + +/* + * Attempt to write the entire buffer to target->stream; discard the entire + * buffer in any case. Call this only when target->stream is defined. + */ +static void +flushbuffer(PrintfTarget *target) +{ + size_t nc = target->bufptr - target->bufstart; + + /* + * Don't write anything if we already failed; this is to ensure we + * preserve the original failure's errno. + */ + if (!target->failed && nc > 0) + { + size_t written; + + written = fwrite(target->bufstart, 1, nc, target->stream); + target->nchars += written; + if (written != nc) + target->failed = true; + } + target->bufptr = target->bufstart; +} + + +static bool find_arguments(const char *format, va_list args, + PrintfArgValue *argvalues); +static void fmtstr(const char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target); +static void fmtptr(const void *value, PrintfTarget *target); +static void fmtint(long long value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target); +static void fmtfloat(double value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void dostr(const char *str, int slen, PrintfTarget *target); +static void dopr_outch(int c, PrintfTarget *target); +static void dopr_outchmulti(int c, int slen, PrintfTarget *target); +static int adjust_sign(int is_negative, int forcesign, int *signvalue); +static int compute_padlen(int minlen, int vallen, int leftjust); +static void leading_pad(int zpad, int signvalue, int *padlen, + PrintfTarget *target); +static void trailing_pad(int padlen, PrintfTarget *target); + +/* + * If strchrnul exists (it's a glibc-ism), it's a good bit faster than the + * equivalent manual loop. If it doesn't exist, provide a replacement. + * + * Note: glibc declares this as returning "char *", but that would require + * casting away const internally, so we don't follow that detail. + */ +#ifndef HAVE_STRCHRNUL + +static inline const char * +strchrnul(const char *s, int c) +{ + while (*s != '\0' && *s != c) + s++; + return s; +} + +#else + +/* + * glibc's <string.h> declares strchrnul only if _GNU_SOURCE is defined. + * While we typically use that on glibc platforms, configure will set + * HAVE_STRCHRNUL whether it's used or not. Fill in the missing declaration + * so that this file will compile cleanly with or without _GNU_SOURCE. + */ +#ifndef _GNU_SOURCE +extern char *strchrnul(const char *s, int c); +#endif + +#endif /* HAVE_STRCHRNUL */ + + +/* + * dopr(): the guts of *printf for all cases. + */ +static void +dopr(PrintfTarget *target, const char *format, va_list args) +{ + int save_errno = errno; + const char *first_pct = NULL; + int ch; + bool have_dollar; + bool have_star; + bool afterstar; + int accum; + int longlongflag; + int longflag; + int pointflag; + int leftjust; + int fieldwidth; + int precision; + int zpad; + int forcesign; + int fmtpos; + int cvalue; + long long numvalue; + double fvalue; + const char *strvalue; + PrintfArgValue argvalues[PG_NL_ARGMAX + 1]; + + /* + * Initially, we suppose the format string does not use %n$. The first + * time we come to a conversion spec that has that, we'll call + * find_arguments() to check for consistent use of %n$ and fill the + * argvalues array with the argument values in the correct order. + */ + have_dollar = false; + + while (*format != '\0') + { + /* Locate next conversion specifier */ + if (*format != '%') + { + /* Scan to next '%' or end of string */ + const char *next_pct = strchrnul(format + 1, '%'); + + /* Dump literal data we just scanned over */ + dostr(format, next_pct - format, target); + if (target->failed) + break; + + if (*next_pct == '\0') + break; + format = next_pct; + } + + /* + * Remember start of first conversion spec; if we find %n$, then it's + * sufficient for find_arguments() to start here, without rescanning + * earlier literal text. + */ + if (first_pct == NULL) + first_pct = format; + + /* Process conversion spec starting at *format */ + format++; + + /* Fast path for conversion spec that is exactly %s */ + if (*format == 's') + { + format++; + strvalue = va_arg(args, char *); + if (strvalue == NULL) + strvalue = "(null)"; + dostr(strvalue, strlen(strvalue), target); + if (target->failed) + break; + continue; + } + + fieldwidth = precision = zpad = leftjust = forcesign = 0; + longflag = longlongflag = pointflag = 0; + fmtpos = accum = 0; + have_star = afterstar = false; +nextch2: + ch = *format++; + switch (ch) + { + case '-': + leftjust = 1; + goto nextch2; + case '+': + forcesign = 1; + goto nextch2; + case '0': + /* set zero padding if no nonzero digits yet */ + if (accum == 0 && !pointflag) + zpad = '0'; + /* FALL THRU */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch2; + case '.': + if (have_star) + have_star = false; + else + fieldwidth = accum; + pointflag = 1; + accum = 0; + goto nextch2; + case '*': + if (have_dollar) + { + /* + * We'll process value after reading n$. Note it's OK to + * assume have_dollar is set correctly, because in a valid + * format string the initial % must have had n$ if * does. + */ + afterstar = true; + } + else + { + /* fetch and process value now */ + int starval = va_arg(args, int); + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + } + have_star = true; + accum = 0; + goto nextch2; + case '$': + /* First dollar sign? */ + if (!have_dollar) + { + /* Yup, so examine all conversion specs in format */ + if (!find_arguments(first_pct, args, argvalues)) + goto bad_format; + have_dollar = true; + } + if (afterstar) + { + /* fetch and process star value */ + int starval = argvalues[accum].i; + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch2; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch2; + case 'z': +#if SIZEOF_SIZE_T == 8 +#ifdef HAVE_LONG_INT_64 + longflag = 1; +#elif defined(HAVE_LONG_LONG_INT_64) + longlongflag = 1; +#else +#error "Don't know how to print 64bit integers" +#endif +#else + /* assume size_t is same size as int */ +#endif + goto nextch2; + case 'h': + case '\'': + /* ignore these */ + goto nextch2; + case 'd': + case 'i': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = argvalues[fmtpos].ll; + else if (longflag) + numvalue = argvalues[fmtpos].l; + else + numvalue = argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = va_arg(args, long long); + else if (longflag) + numvalue = va_arg(args, long); + else + numvalue = va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'o': + case 'u': + case 'x': + case 'X': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = (unsigned long long) argvalues[fmtpos].ll; + else if (longflag) + numvalue = (unsigned long) argvalues[fmtpos].l; + else + numvalue = (unsigned int) argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = (unsigned long long) va_arg(args, long long); + else if (longflag) + numvalue = (unsigned long) va_arg(args, long); + else + numvalue = (unsigned int) va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'c': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + cvalue = (unsigned char) argvalues[fmtpos].i; + else + cvalue = (unsigned char) va_arg(args, int); + fmtchar(cvalue, leftjust, fieldwidth, target); + break; + case 's': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + /* If string is NULL, silently substitute "(null)" */ + if (strvalue == NULL) + strvalue = "(null)"; + fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag, + target); + break; + case 'p': + /* fieldwidth/leftjust are ignored ... */ + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + fmtptr((const void *) strvalue, target); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + fvalue = argvalues[fmtpos].d; + else + fvalue = va_arg(args, double); + fmtfloat(fvalue, ch, forcesign, leftjust, + fieldwidth, zpad, + precision, pointflag, + target); + break; + case 'm': + { + char errbuf[PG_STRERROR_R_BUFLEN]; + const char *errm = strerror_r(save_errno, + errbuf, sizeof(errbuf)); + + dostr(errm, strlen(errm), target); + } + break; + case '%': + dopr_outch('%', target); + break; + default: + + /* + * Anything else --- in particular, '\0' indicating end of + * format string --- is bogus. + */ + goto bad_format; + } + + /* Check for failure after each conversion spec */ + if (target->failed) + break; + } + + return; + +bad_format: + errno = EINVAL; + target->failed = true; +} + +/* + * find_arguments(): sort out the arguments for a format spec with %n$ + * + * If format is valid, return true and fill argvalues[i] with the value + * for the conversion spec that has %i$ or *i$. Else return false. + */ +static bool +find_arguments(const char *format, va_list args, + PrintfArgValue *argvalues) +{ + int ch; + bool afterstar; + int accum; + int longlongflag; + int longflag; + int fmtpos; + int i; + int last_dollar = 0; /* Init to "no dollar arguments known" */ + PrintfArgType argtypes[PG_NL_ARGMAX + 1] = {0}; + + /* + * This loop must accept the same format strings as the one in dopr(). + * However, we don't need to analyze them to the same level of detail. + * + * Since we're only called if there's a dollar-type spec somewhere, we can + * fail immediately if we find a non-dollar spec. Per the C99 standard, + * all argument references in the format string must be one or the other. + */ + while (*format != '\0') + { + /* Locate next conversion specifier */ + if (*format != '%') + { + /* Unlike dopr, we can just quit if there's no more specifiers */ + format = strchr(format + 1, '%'); + if (format == NULL) + break; + } + + /* Process conversion spec starting at *format */ + format++; + longflag = longlongflag = 0; + fmtpos = accum = 0; + afterstar = false; +nextch1: + ch = *format++; + switch (ch) + { + case '-': + case '+': + goto nextch1; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch1; + case '.': + accum = 0; + goto nextch1; + case '*': + if (afterstar) + return false; /* previous star missing dollar */ + afterstar = true; + accum = 0; + goto nextch1; + case '$': + if (accum <= 0 || accum > PG_NL_ARGMAX) + return false; + if (afterstar) + { + if (argtypes[accum] && + argtypes[accum] != ATYPE_INT) + return false; + argtypes[accum] = ATYPE_INT; + last_dollar = Max(last_dollar, accum); + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch1; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch1; + case 'z': +#if SIZEOF_SIZE_T == 8 +#ifdef HAVE_LONG_INT_64 + longflag = 1; +#elif defined(HAVE_LONG_LONG_INT_64) + longlongflag = 1; +#else +#error "Don't know how to print 64bit integers" +#endif +#else + /* assume size_t is same size as int */ +#endif + goto nextch1; + case 'h': + case '\'': + /* ignore these */ + goto nextch1; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (fmtpos) + { + PrintfArgType atype; + + if (longlongflag) + atype = ATYPE_LONGLONG; + else if (longflag) + atype = ATYPE_LONG; + else + atype = ATYPE_INT; + if (argtypes[fmtpos] && + argtypes[fmtpos] != atype) + return false; + argtypes[fmtpos] = atype; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case 'c': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_INT) + return false; + argtypes[fmtpos] = ATYPE_INT; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case 's': + case 'p': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_CHARPTR) + return false; + argtypes[fmtpos] = ATYPE_CHARPTR; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_DOUBLE) + return false; + argtypes[fmtpos] = ATYPE_DOUBLE; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case 'm': + case '%': + break; + default: + return false; /* bogus format string */ + } + + /* + * If we finish the spec with afterstar still set, there's a + * non-dollar star in there. + */ + if (afterstar) + return false; /* non-dollar conversion spec */ + } + + /* + * Format appears valid so far, so collect the arguments in physical + * order. (Since we rejected any non-dollar specs that would have + * collected arguments, we know that dopr() hasn't collected any yet.) + */ + for (i = 1; i <= last_dollar; i++) + { + switch (argtypes[i]) + { + case ATYPE_NONE: + return false; + case ATYPE_INT: + argvalues[i].i = va_arg(args, int); + break; + case ATYPE_LONG: + argvalues[i].l = va_arg(args, long); + break; + case ATYPE_LONGLONG: + argvalues[i].ll = va_arg(args, long long); + break; + case ATYPE_DOUBLE: + argvalues[i].d = va_arg(args, double); + break; + case ATYPE_CHARPTR: + argvalues[i].cptr = va_arg(args, char *); + break; + } + } + + return true; +} + +static void +fmtstr(const char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target) +{ + int padlen, + vallen; /* amount to pad */ + + /* + * If a maxwidth (precision) is specified, we must not fetch more bytes + * than that. + */ + if (pointflag) + vallen = strnlen(value, maxwidth); + else + vallen = strlen(value); + + padlen = compute_padlen(minlen, vallen, leftjust); + + if (padlen > 0) + { + dopr_outchmulti(' ', padlen, target); + padlen = 0; + } + + dostr(value, vallen, target); + + trailing_pad(padlen, target); +} + +static void +fmtptr(const void *value, PrintfTarget *target) +{ + int vallen; + char convert[64]; + + /* we rely on regular C library's snprintf to do the basic conversion */ + vallen = snprintf(convert, sizeof(convert), "%p", value); + if (vallen < 0) + target->failed = true; + else + dostr(convert, vallen, target); +} + +static void +fmtint(long long value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + unsigned long long uvalue; + int base; + int dosign; + const char *cvt = "0123456789abcdef"; + int signvalue = 0; + char convert[64]; + int vallen = 0; + int padlen; /* amount to pad */ + int zeropad; /* extra leading zeroes */ + + switch (type) + { + case 'd': + case 'i': + base = 10; + dosign = 1; + break; + case 'o': + base = 8; + dosign = 0; + break; + case 'u': + base = 10; + dosign = 0; + break; + case 'x': + base = 16; + dosign = 0; + break; + case 'X': + cvt = "0123456789ABCDEF"; + base = 16; + dosign = 0; + break; + default: + return; /* keep compiler quiet */ + } + + /* disable MSVC warning about applying unary minus to an unsigned value */ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4146) +#endif + /* Handle +/- */ + if (dosign && adjust_sign((value < 0), forcesign, &signvalue)) + uvalue = -(unsigned long long) value; + else + uvalue = (unsigned long long) value; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + /* + * SUS: the result of converting 0 with an explicit precision of 0 is no + * characters + */ + if (value == 0 && pointflag && precision == 0) + vallen = 0; + else + { + /* + * Convert integer to string. We special-case each of the possible + * base values so as to avoid general-purpose divisions. On most + * machines, division by a fixed constant can be done much more + * cheaply than a general divide. + */ + if (base == 10) + { + do + { + convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10]; + uvalue = uvalue / 10; + } while (uvalue); + } + else if (base == 16) + { + do + { + convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16]; + uvalue = uvalue / 16; + } while (uvalue); + } + else /* base == 8 */ + { + do + { + convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8]; + uvalue = uvalue / 8; + } while (uvalue); + } + } + + zeropad = Max(0, precision - vallen); + + padlen = compute_padlen(minlen, vallen + zeropad, leftjust); + + leading_pad(zpad, signvalue, &padlen, target); + + if (zeropad > 0) + dopr_outchmulti('0', zeropad, target); + + dostr(convert + sizeof(convert) - vallen, vallen, target); + + trailing_pad(padlen, target); +} + +static void +fmtchar(int value, int leftjust, int minlen, PrintfTarget *target) +{ + int padlen; /* amount to pad */ + + padlen = compute_padlen(minlen, 1, leftjust); + + if (padlen > 0) + { + dopr_outchmulti(' ', padlen, target); + padlen = 0; + } + + dopr_outch(value, target); + + trailing_pad(padlen, target); +} + +static void +fmtfloat(double value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + int signvalue = 0; + int prec; + int vallen; + char fmt[8]; + char convert[1024]; + int zeropadlen = 0; /* amount to pad with zeroes */ + int padlen; /* amount to pad with spaces */ + + /* + * We rely on the regular C library's snprintf to do the basic conversion, + * then handle padding considerations here. + * + * The dynamic range of "double" is about 1E+-308 for IEEE math, and not + * too wildly more than that with other hardware. In "f" format, snprintf + * could therefore generate at most 308 characters to the left of the + * decimal point; while we need to allow the precision to get as high as + * 308+17 to ensure that we don't truncate significant digits from very + * small values. To handle both these extremes, we use a buffer of 1024 + * bytes and limit requested precision to 350 digits; this should prevent + * buffer overrun even with non-IEEE math. If the original precision + * request was more than 350, separately pad with zeroes. + * + * We handle infinities and NaNs specially to ensure platform-independent + * output. + */ + if (precision < 0) /* cover possible overflow of "accum" */ + precision = 0; + prec = Min(precision, 350); + + if (isnan(value)) + { + strcpy(convert, "NaN"); + vallen = 3; + /* no zero padding, regardless of precision spec */ + } + else + { + /* + * Handle sign (NaNs have no sign, so we don't do this in the case + * above). "value < 0.0" will not be true for IEEE minus zero, so we + * detect that by looking for the case where value equals 0.0 + * according to == but not according to memcmp. + */ + static const double dzero = 0.0; + + if (adjust_sign((value < 0.0 || + (value == 0.0 && + memcmp(&value, &dzero, sizeof(double)) != 0)), + forcesign, &signvalue)) + value = -value; + + if (isinf(value)) + { + strcpy(convert, "Infinity"); + vallen = 8; + /* no zero padding, regardless of precision spec */ + } + else if (pointflag) + { + zeropadlen = precision - prec; + fmt[0] = '%'; + fmt[1] = '.'; + fmt[2] = '*'; + fmt[3] = type; + fmt[4] = '\0'; + vallen = snprintf(convert, sizeof(convert), fmt, prec, value); + } + else + { + fmt[0] = '%'; + fmt[1] = type; + fmt[2] = '\0'; + vallen = snprintf(convert, sizeof(convert), fmt, value); + } + if (vallen < 0) + goto fail; + + /* + * Windows, alone among our supported platforms, likes to emit + * three-digit exponent fields even when two digits would do. Hack + * such results to look like the way everyone else does it. + */ +#ifdef WIN32 + if (vallen >= 6 && + convert[vallen - 5] == 'e' && + convert[vallen - 3] == '0') + { + convert[vallen - 3] = convert[vallen - 2]; + convert[vallen - 2] = convert[vallen - 1]; + vallen--; + } +#endif + } + + padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust); + + leading_pad(zpad, signvalue, &padlen, target); + + if (zeropadlen > 0) + { + /* If 'e' or 'E' format, inject zeroes before the exponent */ + char *epos = strrchr(convert, 'e'); + + if (!epos) + epos = strrchr(convert, 'E'); + if (epos) + { + /* pad before exponent */ + dostr(convert, epos - convert, target); + dopr_outchmulti('0', zeropadlen, target); + dostr(epos, vallen - (epos - convert), target); + } + else + { + /* no exponent, pad after the digits */ + dostr(convert, vallen, target); + dopr_outchmulti('0', zeropadlen, target); + } + } + else + { + /* no zero padding, just emit the number as-is */ + dostr(convert, vallen, target); + } + + trailing_pad(padlen, target); + return; + +fail: + target->failed = true; +} + +/* + * Nonstandard entry point to print a double value efficiently. + * + * This is approximately equivalent to strfromd(), but has an API more + * adapted to what float8out() wants. The behavior is like snprintf() + * with a format of "%.ng", where n is the specified precision. + * However, the target buffer must be nonempty (i.e. count > 0), and + * the precision is silently bounded to a sane range. + */ +int +pg_strfromd(char *str, size_t count, int precision, double value) +{ + PrintfTarget target; + int signvalue = 0; + int vallen; + char fmt[8]; + char convert[64]; + + /* Set up the target like pg_snprintf, but require nonempty buffer */ + Assert(count > 0); + target.bufstart = target.bufptr = str; + target.bufend = str + count - 1; + target.stream = NULL; + target.nchars = 0; + target.failed = false; + + /* + * We bound precision to a reasonable range; the combination of this and + * the knowledge that we're using "g" format without padding allows the + * convert[] buffer to be reasonably small. + */ + if (precision < 1) + precision = 1; + else if (precision > 32) + precision = 32; + + /* + * The rest is just an inlined version of the fmtfloat() logic above, + * simplified using the knowledge that no padding is wanted. + */ + if (isnan(value)) + { + strcpy(convert, "NaN"); + vallen = 3; + } + else + { + static const double dzero = 0.0; + + if (value < 0.0 || + (value == 0.0 && + memcmp(&value, &dzero, sizeof(double)) != 0)) + { + signvalue = '-'; + value = -value; + } + + if (isinf(value)) + { + strcpy(convert, "Infinity"); + vallen = 8; + } + else + { + fmt[0] = '%'; + fmt[1] = '.'; + fmt[2] = '*'; + fmt[3] = 'g'; + fmt[4] = '\0'; + vallen = snprintf(convert, sizeof(convert), fmt, precision, value); + if (vallen < 0) + { + target.failed = true; + goto fail; + } + +#ifdef WIN32 + if (vallen >= 6 && + convert[vallen - 5] == 'e' && + convert[vallen - 3] == '0') + { + convert[vallen - 3] = convert[vallen - 2]; + convert[vallen - 2] = convert[vallen - 1]; + vallen--; + } +#endif + } + } + + if (signvalue) + dopr_outch(signvalue, &target); + + dostr(convert, vallen, &target); + +fail: + *(target.bufptr) = '\0'; + return target.failed ? -1 : (target.bufptr - target.bufstart + + target.nchars); +} + + +static void +dostr(const char *str, int slen, PrintfTarget *target) +{ + /* fast path for common case of slen == 1 */ + if (slen == 1) + { + dopr_outch(*str, target); + return; + } + + while (slen > 0) + { + int avail; + + if (target->bufend != NULL) + avail = target->bufend - target->bufptr; + else + avail = slen; + if (avail <= 0) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + { + target->nchars += slen; /* no, lose the data */ + return; + } + flushbuffer(target); + continue; + } + avail = Min(avail, slen); + memmove(target->bufptr, str, avail); + target->bufptr += avail; + str += avail; + slen -= avail; + } +} + +static void +dopr_outch(int c, PrintfTarget *target) +{ + if (target->bufend != NULL && target->bufptr >= target->bufend) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + { + target->nchars++; /* no, lose the data */ + return; + } + flushbuffer(target); + } + *(target->bufptr++) = c; +} + +static void +dopr_outchmulti(int c, int slen, PrintfTarget *target) +{ + /* fast path for common case of slen == 1 */ + if (slen == 1) + { + dopr_outch(c, target); + return; + } + + while (slen > 0) + { + int avail; + + if (target->bufend != NULL) + avail = target->bufend - target->bufptr; + else + avail = slen; + if (avail <= 0) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + { + target->nchars += slen; /* no, lose the data */ + return; + } + flushbuffer(target); + continue; + } + avail = Min(avail, slen); + memset(target->bufptr, c, avail); + target->bufptr += avail; + slen -= avail; + } +} + + +static int +adjust_sign(int is_negative, int forcesign, int *signvalue) +{ + if (is_negative) + { + *signvalue = '-'; + return true; + } + else if (forcesign) + *signvalue = '+'; + return false; +} + + +static int +compute_padlen(int minlen, int vallen, int leftjust) +{ + int padlen; + + padlen = minlen - vallen; + if (padlen < 0) + padlen = 0; + if (leftjust) + padlen = -padlen; + return padlen; +} + + +static void +leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target) +{ + int maxpad; + + if (*padlen > 0 && zpad) + { + if (signvalue) + { + dopr_outch(signvalue, target); + --(*padlen); + signvalue = 0; + } + if (*padlen > 0) + { + dopr_outchmulti(zpad, *padlen, target); + *padlen = 0; + } + } + maxpad = (signvalue != 0); + if (*padlen > maxpad) + { + dopr_outchmulti(' ', *padlen - maxpad, target); + *padlen = maxpad; + } + if (signvalue) + { + dopr_outch(signvalue, target); + if (*padlen > 0) + --(*padlen); + else if (*padlen < 0) + ++(*padlen); + } +} + + +static void +trailing_pad(int padlen, PrintfTarget *target) +{ + if (padlen < 0) + dopr_outchmulti(' ', -padlen, target); +} diff --git a/src/port/strerror.c b/src/port/strerror.c new file mode 100644 index 0000000..26827d6 --- /dev/null +++ b/src/port/strerror.c @@ -0,0 +1,312 @@ +/*------------------------------------------------------------------------- + * + * strerror.c + * Replacements for standard strerror() and strerror_r() functions + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/strerror.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +/* + * Within this file, "strerror" means the platform's function not pg_strerror, + * and likewise for "strerror_r" + */ +#undef strerror +#undef strerror_r + +static char *gnuish_strerror_r(int errnum, char *buf, size_t buflen); +static char *get_errno_symbol(int errnum); +#ifdef WIN32 +static char *win32_socket_strerror(int errnum, char *buf, size_t buflen); +#endif + + +/* + * A slightly cleaned-up version of strerror() + */ +char * +pg_strerror(int errnum) +{ + static char errorstr_buf[PG_STRERROR_R_BUFLEN]; + + return pg_strerror_r(errnum, errorstr_buf, sizeof(errorstr_buf)); +} + +/* + * A slightly cleaned-up version of strerror_r() + */ +char * +pg_strerror_r(int errnum, char *buf, size_t buflen) +{ + char *str; + + /* If it's a Windows Winsock error, that needs special handling */ +#ifdef WIN32 + /* Winsock error code range, per WinError.h */ + if (errnum >= 10000 && errnum <= 11999) + return win32_socket_strerror(errnum, buf, buflen); +#endif + + /* Try the platform's strerror_r(), or maybe just strerror() */ + str = gnuish_strerror_r(errnum, buf, buflen); + + /* + * Some strerror()s return an empty string for out-of-range errno. This + * is ANSI C spec compliant, but not exactly useful. Also, we may get + * back strings of question marks if libc cannot transcode the message to + * the codeset specified by LC_CTYPE. If we get nothing useful, first try + * get_errno_symbol(), and if that fails, print the numeric errno. + */ + if (str == NULL || *str == '\0' || *str == '?') + str = get_errno_symbol(errnum); + + if (str == NULL) + { + snprintf(buf, buflen, _("operating system error %d"), errnum); + str = buf; + } + + return str; +} + +/* + * Simple wrapper to emulate GNU strerror_r if what the platform provides is + * POSIX. Also, if platform lacks strerror_r altogether, fall back to plain + * strerror; it might not be very thread-safe, but tough luck. + */ +static char * +gnuish_strerror_r(int errnum, char *buf, size_t buflen) +{ +#ifdef HAVE_STRERROR_R +#ifdef STRERROR_R_INT + /* POSIX API */ + if (strerror_r(errnum, buf, buflen) == 0) + return buf; + return NULL; /* let caller deal with failure */ +#else + /* GNU API */ + return strerror_r(errnum, buf, buflen); +#endif +#else /* !HAVE_STRERROR_R */ + char *sbuf = strerror(errnum); + + if (sbuf == NULL) /* can this still happen anywhere? */ + return NULL; + /* To minimize thread-unsafety hazard, copy into caller's buffer */ + strlcpy(buf, sbuf, buflen); + return buf; +#endif +} + +/* + * Returns a symbol (e.g. "ENOENT") for an errno code. + * Returns NULL if the code is unrecognized. + */ +static char * +get_errno_symbol(int errnum) +{ + switch (errnum) + { + case E2BIG: + return "E2BIG"; + case EACCES: + return "EACCES"; + case EADDRINUSE: + return "EADDRINUSE"; + case EADDRNOTAVAIL: + return "EADDRNOTAVAIL"; + case EAFNOSUPPORT: + return "EAFNOSUPPORT"; +#ifdef EAGAIN + case EAGAIN: + return "EAGAIN"; +#endif +#ifdef EALREADY + case EALREADY: + return "EALREADY"; +#endif + case EBADF: + return "EBADF"; +#ifdef EBADMSG + case EBADMSG: + return "EBADMSG"; +#endif + case EBUSY: + return "EBUSY"; + case ECHILD: + return "ECHILD"; + case ECONNABORTED: + return "ECONNABORTED"; + case ECONNREFUSED: + return "ECONNREFUSED"; + case ECONNRESET: + return "ECONNRESET"; + case EDEADLK: + return "EDEADLK"; + case EDOM: + return "EDOM"; + case EEXIST: + return "EEXIST"; + case EFAULT: + return "EFAULT"; + case EFBIG: + return "EFBIG"; + case EHOSTDOWN: + return "EHOSTDOWN"; + case EHOSTUNREACH: + return "EHOSTUNREACH"; + case EIDRM: + return "EIDRM"; + case EINPROGRESS: + return "EINPROGRESS"; + case EINTR: + return "EINTR"; + case EINVAL: + return "EINVAL"; + case EIO: + return "EIO"; + case EISCONN: + return "EISCONN"; + case EISDIR: + return "EISDIR"; +#ifdef ELOOP + case ELOOP: + return "ELOOP"; +#endif + case EMFILE: + return "EMFILE"; + case EMLINK: + return "EMLINK"; + case EMSGSIZE: + return "EMSGSIZE"; + case ENAMETOOLONG: + return "ENAMETOOLONG"; + case ENETDOWN: + return "ENETDOWN"; + case ENETRESET: + return "ENETRESET"; + case ENETUNREACH: + return "ENETUNREACH"; + case ENFILE: + return "ENFILE"; + case ENOBUFS: + return "ENOBUFS"; + case ENODEV: + return "ENODEV"; + case ENOENT: + return "ENOENT"; + case ENOEXEC: + return "ENOEXEC"; + case ENOMEM: + return "ENOMEM"; + case ENOSPC: + return "ENOSPC"; + case ENOSYS: + return "ENOSYS"; + case ENOTCONN: + return "ENOTCONN"; + case ENOTDIR: + return "ENOTDIR"; +#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */ + case ENOTEMPTY: + return "ENOTEMPTY"; +#endif + case ENOTSOCK: + return "ENOTSOCK"; +#ifdef ENOTSUP + case ENOTSUP: + return "ENOTSUP"; +#endif + case ENOTTY: + return "ENOTTY"; + case ENXIO: + return "ENXIO"; +#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP)) + case EOPNOTSUPP: + return "EOPNOTSUPP"; +#endif +#ifdef EOVERFLOW + case EOVERFLOW: + return "EOVERFLOW"; +#endif + case EPERM: + return "EPERM"; + case EPIPE: + return "EPIPE"; + case EPROTONOSUPPORT: + return "EPROTONOSUPPORT"; + case ERANGE: + return "ERANGE"; +#ifdef EROFS + case EROFS: + return "EROFS"; +#endif + case ESRCH: + return "ESRCH"; + case ETIMEDOUT: + return "ETIMEDOUT"; +#ifdef ETXTBSY + case ETXTBSY: + return "ETXTBSY"; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: + return "EWOULDBLOCK"; +#endif + case EXDEV: + return "EXDEV"; + } + + return NULL; +} + + +#ifdef WIN32 + +/* + * Windows' strerror() doesn't know the Winsock codes, so handle them this way + */ +static char * +win32_socket_strerror(int errnum, char *buf, size_t buflen) +{ + static HANDLE handleDLL = INVALID_HANDLE_VALUE; + + if (handleDLL == INVALID_HANDLE_VALUE) + { + handleDLL = LoadLibraryEx("netmsg.dll", NULL, + DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); + if (handleDLL == NULL) + { + snprintf(buf, buflen, + "winsock error %d (could not load netmsg.dll to translate: error code %lu)", + errnum, GetLastError()); + return buf; + } + } + + ZeroMemory(buf, buflen); + if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_FROM_HMODULE, + handleDLL, + errnum, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + buf, + buflen - 1, + NULL) == 0) + { + /* Failed to get id */ + snprintf(buf, buflen, "unrecognized winsock error %d", errnum); + } + + return buf; +} + +#endif /* WIN32 */ diff --git a/src/port/strlcat.c b/src/port/strlcat.c new file mode 100644 index 0000000..190e573 --- /dev/null +++ b/src/port/strlcat.c @@ -0,0 +1,60 @@ +/* + * src/port/strlcat.c + * + * $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "c.h" + + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return (dlen + strlen(s)); + while (*s != '\0') + { + if (n != 1) + { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return (dlen + (s - src)); /* count does not include NUL */ +} diff --git a/src/port/strlcpy.c b/src/port/strlcpy.c new file mode 100644 index 0000000..71b794e --- /dev/null +++ b/src/port/strlcpy.c @@ -0,0 +1,71 @@ +/*------------------------------------------------------------------------- + * + * strlcpy.c + * strncpy done right + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/strlcpy.c + * + * This file was taken from OpenBSD and is used on platforms that don't + * provide strlcpy(). The OpenBSD copyright terms follow. + *------------------------------------------------------------------------- + */ + +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "c.h" + + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + * Function creation history: http://www.gratisoft.us/todd/papers/strlcpy.html + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) + { + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (s - src - 1); /* count does not include NUL */ +} diff --git a/src/port/strnlen.c b/src/port/strnlen.c new file mode 100644 index 0000000..92d2575 --- /dev/null +++ b/src/port/strnlen.c @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------- + * + * strnlen.c + * Fallback implementation of strnlen(). + * + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/strnlen.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +/* + * Implementation of posix' strnlen for systems where it's not available. + * + * Returns the number of characters before a null-byte in the string pointed + * to by str, unless there's no null-byte before maxlen. In the latter case + * maxlen is returned. + */ +size_t +strnlen(const char *str, size_t maxlen) +{ + const char *p = str; + + while (maxlen-- > 0 && *p) + p++; + return p - str; +} diff --git a/src/port/strtof.c b/src/port/strtof.c new file mode 100644 index 0000000..fea45a8 --- /dev/null +++ b/src/port/strtof.c @@ -0,0 +1,85 @@ +/*------------------------------------------------------------------------- + * + * strtof.c + * + * Portions Copyright (c) 2019-2023, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/strtof.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <float.h> +#include <math.h> + + +/* + * Cygwin has a strtof() which is literally just (float)strtod(), which means + * we can't avoid the double-rounding problem; but using this wrapper does get + * us proper over/underflow checks. (Also, if they fix their strtof(), the + * wrapper doesn't break anything.) + * + * Test results on Mingw suggest that it has the same problem, though looking + * at the code I can't figure out why. + */ +float +pg_strtof(const char *nptr, char **endptr) +{ + int caller_errno = errno; + float fresult; + + errno = 0; + fresult = (strtof) (nptr, endptr); + if (errno) + { + /* On error, just return the error to the caller. */ + return fresult; + } + else if ((*endptr == nptr) || isnan(fresult) || + ((fresult >= FLT_MIN || fresult <= -FLT_MIN) && !isinf(fresult))) + { + /* + * If we got nothing parseable, or if we got a non-0 non-subnormal + * finite value (or NaN) without error, then return that to the caller + * without error. + */ + errno = caller_errno; + return fresult; + } + else + { + /* + * Try again. errno is already 0 here. + */ + double dresult = strtod(nptr, NULL); + + if (errno) + { + /* On error, just return the error */ + return fresult; + } + else if ((dresult == 0.0 && fresult == 0.0) || + (isinf(dresult) && isinf(fresult) && (fresult == dresult))) + { + /* both values are 0 or infinities of the same sign */ + errno = caller_errno; + return fresult; + } + else if ((dresult > 0 && dresult <= FLT_MIN && (float) dresult != 0.0) || + (dresult < 0 && dresult >= -FLT_MIN && (float) dresult != 0.0)) + { + /* subnormal but nonzero value */ + errno = caller_errno; + return (float) dresult; + } + else + { + errno = ERANGE; + return fresult; + } + } +} diff --git a/src/port/system.c b/src/port/system.c new file mode 100644 index 0000000..b6d1369 --- /dev/null +++ b/src/port/system.c @@ -0,0 +1,117 @@ +/*------------------------------------------------------------------------- + * + * system.c + * Win32 system() and popen() replacements + * + * + * Win32 needs double quotes at the beginning and end of system() + * strings. If not, it gets confused with multiple quoted strings. + * It also requires double-quotes around the executable name and + * any files used for redirection. Filter other args through + * appendShellString() to quote them. + * + * Generated using Win32 "CMD /?": + * + * 1. If all of the following conditions are met, then quote characters + * on the command line are preserved: + * + * - no /S switch + * - exactly two quote characters + * - no special characters between the two quote characters, where special + * is one of: &<>()@^| + * - there are one or more whitespace characters between the two quote + * characters + * - the string between the two quote characters is the name of an + * executable file. + * + * 2. Otherwise, old behavior is to see if the first character is a quote + * character and if so, strip the leading character and remove the last + * quote character on the command line, preserving any text after the last + * quote character. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * src/port/system.c + * + *------------------------------------------------------------------------- + */ + +#if defined(WIN32) && !defined(__CYGWIN__) + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include <fcntl.h> + +#undef system +#undef popen + +int +pgwin32_system(const char *command) +{ + size_t cmdlen = strlen(command); + char *buf; + int save_errno; + int res; + + /* + * Create a malloc'd copy of the command string, enclosed with an extra + * pair of quotes + */ + buf = malloc(cmdlen + 2 + 1); + if (buf == NULL) + { + errno = ENOMEM; + return -1; + } + buf[0] = '"'; + memcpy(&buf[1], command, cmdlen); + buf[cmdlen + 1] = '"'; + buf[cmdlen + 2] = '\0'; + + res = system(buf); + + save_errno = errno; + free(buf); + errno = save_errno; + + return res; +} + + +FILE * +pgwin32_popen(const char *command, const char *type) +{ + size_t cmdlen = strlen(command); + char *buf; + int save_errno; + FILE *res; + + /* + * Create a malloc'd copy of the command string, enclosed with an extra + * pair of quotes + */ + buf = malloc(cmdlen + 2 + 1); + if (buf == NULL) + { + errno = ENOMEM; + return NULL; + } + buf[0] = '"'; + memcpy(&buf[1], command, cmdlen); + buf[cmdlen + 1] = '"'; + buf[cmdlen + 2] = '\0'; + + res = _popen(buf, type); + + save_errno = errno; + free(buf); + errno = save_errno; + + return res; +} + +#endif diff --git a/src/port/tar.c b/src/port/tar.c new file mode 100644 index 0000000..4afe9f2 --- /dev/null +++ b/src/port/tar.c @@ -0,0 +1,206 @@ +#include "c.h" + +#include <sys/stat.h> + +#include "pgtar.h" + +/* + * Print a numeric field in a tar header. The field starts at *s and is of + * length len; val is the value to be written. + * + * Per POSIX, the way to write a number is in octal with leading zeroes and + * one trailing space (or NUL, but we use space) at the end of the specified + * field width. + * + * However, the given value may not fit in the available space in octal form. + * If that's true, we use the GNU extension of writing \200 followed by the + * number in base-256 form (ie, stored in binary MSB-first). (Note: here we + * support only non-negative numbers, so we don't worry about the GNU rules + * for handling negative numbers.) + */ +void +print_tar_number(char *s, int len, uint64 val) +{ + if (val < (((uint64) 1) << ((len - 1) * 3))) + { + /* Use octal with trailing space */ + s[--len] = ' '; + while (len) + { + s[--len] = (val & 7) + '0'; + val >>= 3; + } + } + else + { + /* Use base-256 with leading \200 */ + s[0] = '\200'; + while (len > 1) + { + s[--len] = (val & 255); + val >>= 8; + } + } +} + + +/* + * Read a numeric field in a tar header. The field starts at *s and is of + * length len. + * + * The POSIX-approved format for a number is octal, ending with a space or + * NUL. However, for values that don't fit, we recognize the GNU extension + * of \200 followed by the number in base-256 form (ie, stored in binary + * MSB-first). (Note: here we support only non-negative numbers, so we don't + * worry about the GNU rules for handling negative numbers.) + */ +uint64 +read_tar_number(const char *s, int len) +{ + uint64 result = 0; + + if (*s == '\200') + { + /* base-256 */ + while (--len) + { + result <<= 8; + result |= (unsigned char) (*++s); + } + } + else + { + /* octal */ + while (len-- && *s >= '0' && *s <= '7') + { + result <<= 3; + result |= (*s - '0'); + s++; + } + } + return result; +} + + +/* + * Calculate the tar checksum for a header. The header is assumed to always + * be 512 bytes, per the tar standard. + */ +int +tarChecksum(char *header) +{ + int i, + sum; + + /* + * Per POSIX, the checksum is the simple sum of all bytes in the header, + * treating the bytes as unsigned, and treating the checksum field (at + * offset 148) as though it contained 8 spaces. + */ + sum = 8 * ' '; /* presumed value for checksum field */ + for (i = 0; i < 512; i++) + if (i < 148 || i >= 156) + sum += 0xFF & header[i]; + return sum; +} + + +/* + * Fill in the buffer pointed to by h with a tar format header. This buffer + * must always have space for 512 characters, which is a requirement of + * the tar format. + */ +enum tarError +tarCreateHeader(char *h, const char *filename, const char *linktarget, + pgoff_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime) +{ + if (strlen(filename) > 99) + return TAR_NAME_TOO_LONG; + + if (linktarget && strlen(linktarget) > 99) + return TAR_SYMLINK_TOO_LONG; + + memset(h, 0, 512); /* assume tar header size */ + + /* Name 100 */ + strlcpy(&h[0], filename, 100); + if (linktarget != NULL || S_ISDIR(mode)) + { + /* + * We only support symbolic links to directories, and this is + * indicated in the tar format by adding a slash at the end of the + * name, the same as for regular directories. + */ + int flen = strlen(filename); + + flen = Min(flen, 99); + h[flen] = '/'; + h[flen + 1] = '\0'; + } + + /* Mode 8 - this doesn't include the file type bits (S_IFMT) */ + print_tar_number(&h[100], 8, (mode & 07777)); + + /* User ID 8 */ + print_tar_number(&h[108], 8, uid); + + /* Group 8 */ + print_tar_number(&h[116], 8, gid); + + /* File size 12 */ + if (linktarget != NULL || S_ISDIR(mode)) + /* Symbolic link or directory has size zero */ + print_tar_number(&h[124], 12, 0); + else + print_tar_number(&h[124], 12, size); + + /* Mod Time 12 */ + print_tar_number(&h[136], 12, mtime); + + /* Checksum 8 cannot be calculated until we've filled all other fields */ + + if (linktarget != NULL) + { + /* Type - Symbolic link */ + h[156] = '2'; + /* Link Name 100 */ + strlcpy(&h[157], linktarget, 100); + } + else if (S_ISDIR(mode)) + { + /* Type - directory */ + h[156] = '5'; + } + else + { + /* Type - regular file */ + h[156] = '0'; + } + + /* Magic 6 */ + strcpy(&h[257], "ustar"); + + /* Version 2 */ + memcpy(&h[263], "00", 2); + + /* User 32 */ + /* XXX: Do we need to care about setting correct username? */ + strlcpy(&h[265], "postgres", 32); + + /* Group 32 */ + /* XXX: Do we need to care about setting correct group name? */ + strlcpy(&h[297], "postgres", 32); + + /* Major Dev 8 */ + print_tar_number(&h[329], 8, 0); + + /* Minor Dev 8 */ + print_tar_number(&h[337], 8, 0); + + /* Prefix 155 - not used, leave as nulls */ + + /* Finally, compute and insert the checksum */ + print_tar_number(&h[148], 8, tarChecksum(h)); + + return TAR_OK; +} diff --git a/src/port/thread.c b/src/port/thread.c new file mode 100644 index 0000000..375c89b --- /dev/null +++ b/src/port/thread.c @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------- + * + * thread.c + * + * Prototypes and macros around system calls, used to help make + * threaded libraries reentrant and safe to use from threaded applications. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * src/port/thread.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <pwd.h> + + +/* + * Historically, the code in this module had to deal with operating systems + * that lacked getpwuid_r(). + */ + +#ifndef WIN32 + +/* + * pg_get_user_name - get the name of the user with the given ID + * + * On success, the user name is returned into the buffer (of size buflen), + * and "true" is returned. On failure, a localized error message is + * returned into the buffer, and "false" is returned. + */ +bool +pg_get_user_name(uid_t user_id, char *buffer, size_t buflen) +{ + char pwdbuf[BUFSIZ]; + struct passwd pwdstr; + struct passwd *pw = NULL; + int pwerr; + + pwerr = getpwuid_r(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw); + if (pw != NULL) + { + strlcpy(buffer, pw->pw_name, buflen); + return true; + } + if (pwerr != 0) + snprintf(buffer, buflen, + _("could not look up local user ID %d: %s"), + (int) user_id, + strerror_r(pwerr, pwdbuf, sizeof(pwdbuf))); + else + snprintf(buffer, buflen, + _("local user with ID %d does not exist"), + (int) user_id); + return false; +} + +/* + * pg_get_user_home_dir - get the home directory of the user with the given ID + * + * On success, the directory path is returned into the buffer (of size buflen), + * and "true" is returned. On failure, a localized error message is + * returned into the buffer, and "false" is returned. + * + * Note that this does not incorporate the common behavior of checking + * $HOME first, since it's independent of which user_id is queried. + */ +bool +pg_get_user_home_dir(uid_t user_id, char *buffer, size_t buflen) +{ + char pwdbuf[BUFSIZ]; + struct passwd pwdstr; + struct passwd *pw = NULL; + int pwerr; + + pwerr = getpwuid_r(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw); + if (pw != NULL) + { + strlcpy(buffer, pw->pw_dir, buflen); + return true; + } + if (pwerr != 0) + snprintf(buffer, buflen, + _("could not look up local user ID %d: %s"), + (int) user_id, + strerror_r(pwerr, pwdbuf, sizeof(pwdbuf))); + else + snprintf(buffer, buflen, + _("local user with ID %d does not exist"), + (int) user_id); + return false; +} + +#endif /* !WIN32 */ diff --git a/src/port/win32.ico b/src/port/win32.ico Binary files differnew file mode 100644 index 0000000..a58ee43 --- /dev/null +++ b/src/port/win32.ico diff --git a/src/port/win32common.c b/src/port/win32common.c new file mode 100644 index 0000000..2fd78f7 --- /dev/null +++ b/src/port/win32common.c @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- + * + * win32common.c + * Common routines shared among the win32*.c ports. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/win32common.c + * + *------------------------------------------------------------------------- + */ + +#ifdef FRONTEND +#include "postgres_fe.h" +#else +#include "postgres.h" +#endif + +#ifdef WIN32 + +/* + * pgwin32_get_file_type + * + * Convenience wrapper for GetFileType() with specific error handling for all the + * port implementations. Returns the file type associated with a HANDLE. + * + * On error, sets errno with FILE_TYPE_UNKNOWN as file type. + */ +DWORD +pgwin32_get_file_type(HANDLE hFile) +{ + DWORD fileType = FILE_TYPE_UNKNOWN; + DWORD lastError; + + errno = 0; + + /* + * When stdin, stdout, and stderr aren't associated with a stream the + * special value -2 is returned: + * https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle + */ + if (hFile == INVALID_HANDLE_VALUE || hFile == (HANDLE) -2) + { + errno = EINVAL; + return FILE_TYPE_UNKNOWN; + } + + fileType = GetFileType(hFile); + lastError = GetLastError(); + + /* + * Invoke GetLastError in order to distinguish between a "valid" return of + * FILE_TYPE_UNKNOWN and its return due to a calling error. In case of + * success, GetLastError() returns NO_ERROR. + */ + if (fileType == FILE_TYPE_UNKNOWN && lastError != NO_ERROR) + { + _dosmaperr(lastError); + return FILE_TYPE_UNKNOWN; + } + + return fileType; +} + +#endif /* WIN32 */ diff --git a/src/port/win32dlopen.c b/src/port/win32dlopen.c new file mode 100644 index 0000000..da82c86 --- /dev/null +++ b/src/port/win32dlopen.c @@ -0,0 +1,93 @@ +/*------------------------------------------------------------------------- + * + * win32dlopen.c + * dynamic loader for Windows + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/win32dlopen.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +static char last_dyn_error[512]; + +static void +set_dl_error(void) +{ + DWORD err = GetLastError(); + + if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + last_dyn_error, + sizeof(last_dyn_error) - 1, + NULL) == 0) + { + snprintf(last_dyn_error, sizeof(last_dyn_error) - 1, + "unknown error %lu", err); + } +} + +char * +dlerror(void) +{ + if (last_dyn_error[0]) + return last_dyn_error; + else + return NULL; +} + +int +dlclose(void *handle) +{ + if (!FreeLibrary((HMODULE) handle)) + { + set_dl_error(); + return 1; + } + last_dyn_error[0] = 0; + return 0; +} + +void * +dlsym(void *handle, const char *symbol) +{ + void *ptr; + + ptr = GetProcAddress((HMODULE) handle, symbol); + if (!ptr) + { + set_dl_error(); + return NULL; + } + last_dyn_error[0] = 0; + return ptr; +} + +void * +dlopen(const char *file, int mode) +{ + HMODULE h; + int prevmode; + + /* Disable popup error messages when loading DLLs */ + prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + h = LoadLibrary(file); + SetErrorMode(prevmode); + + if (!h) + { + set_dl_error(); + return NULL; + } + last_dyn_error[0] = 0; + return (void *) h; +} diff --git a/src/port/win32env.c b/src/port/win32env.c new file mode 100644 index 0000000..fbc6edd --- /dev/null +++ b/src/port/win32env.c @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------- + * + * win32env.c + * putenv(), setenv(), and unsetenv() for win32. + * + * These functions update both the process environment and caches in + * (potentially multiple) C run-time library (CRT) versions. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/win32env.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + + +/* + * Note that unlike POSIX putenv(), this doesn't use the passed-in string + * as permanent storage. + */ +int +pgwin32_putenv(const char *envval) +{ + char *envcpy; + char *cp; + typedef int (_cdecl * PUTENVPROC) (const char *); + static const char *const modulenames[] = { + "msvcrt", /* Visual Studio 6.0 / MinGW */ + "msvcrtd", + "msvcr70", /* Visual Studio 2002 */ + "msvcr70d", + "msvcr71", /* Visual Studio 2003 */ + "msvcr71d", + "msvcr80", /* Visual Studio 2005 */ + "msvcr80d", + "msvcr90", /* Visual Studio 2008 */ + "msvcr90d", + "msvcr100", /* Visual Studio 2010 */ + "msvcr100d", + "msvcr110", /* Visual Studio 2012 */ + "msvcr110d", + "msvcr120", /* Visual Studio 2013 */ + "msvcr120d", + "ucrtbase", /* Visual Studio 2015 and later */ + "ucrtbased", + NULL + }; + int i; + + /* + * Update process environment, making this change visible to child + * processes and to CRTs initializing in the future. Do this before the + * _putenv() loop, for the benefit of any CRT that initializes during this + * pgwin32_putenv() execution, after the loop checks that CRT. + * + * Need a copy of the string so we can modify it. + */ + envcpy = strdup(envval); + if (!envcpy) + return -1; + cp = strchr(envcpy, '='); + if (cp == NULL) + { + free(envcpy); + return -1; + } + *cp = '\0'; + cp++; + if (*cp) + { + /* + * Only call SetEnvironmentVariable() when we are adding a variable, + * not when removing it. Calling it on both crashes on at least + * certain versions of MinGW. + */ + if (!SetEnvironmentVariable(envcpy, cp)) + { + free(envcpy); + return -1; + } + } + free(envcpy); + + /* + * Each CRT has its own _putenv() symbol and copy of the environment. + * Update the environment in each CRT module currently loaded, so every + * third-party library sees this change regardless of the CRT it links + * against. Addresses within these modules may become invalid the moment + * we call FreeLibrary(), so don't cache them. + */ + for (i = 0; modulenames[i]; i++) + { + HMODULE hmodule = NULL; + BOOL res = GetModuleHandleEx(0, modulenames[i], &hmodule); + + if (res != 0 && hmodule != NULL) + { + PUTENVPROC putenvFunc; + + putenvFunc = (PUTENVPROC) (pg_funcptr_t) GetProcAddress(hmodule, "_putenv"); + if (putenvFunc) + putenvFunc(envval); + FreeLibrary(hmodule); + } + } + + /* + * Finally, update our "own" cache. This is redundant with the loop + * above, except when PostgreSQL itself links to a CRT not listed above. + * Ideally, the loop does visit all possible CRTs, making this redundant. + */ + return _putenv(envval); +} + +int +pgwin32_setenv(const char *name, const char *value, int overwrite) +{ + int res; + char *envstr; + + /* Error conditions, per POSIX */ + if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL || + value == NULL) + { + errno = EINVAL; + return -1; + } + + /* No work if variable exists and we're not to replace it */ + if (overwrite == 0 && getenv(name) != NULL) + return 0; + + envstr = (char *) malloc(strlen(name) + strlen(value) + 2); + if (!envstr) /* not much we can do if no memory */ + return -1; + + sprintf(envstr, "%s=%s", name, value); + + res = pgwin32_putenv(envstr); + free(envstr); + return res; +} + +int +pgwin32_unsetenv(const char *name) +{ + int res; + char *envbuf; + + envbuf = (char *) malloc(strlen(name) + 2); + if (!envbuf) + return -1; + + sprintf(envbuf, "%s=", name); + res = pgwin32_putenv(envbuf); + free(envbuf); + return res; +} diff --git a/src/port/win32error.c b/src/port/win32error.c new file mode 100644 index 0000000..d7c3048 --- /dev/null +++ b/src/port/win32error.c @@ -0,0 +1,214 @@ +/*------------------------------------------------------------------------- + * + * win32error.c + * Map win32 error codes to errno values + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32error.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +static const struct +{ + DWORD winerr; + int doserr; +} doserrors[] = + +{ + { + ERROR_INVALID_FUNCTION, EINVAL + }, + { + ERROR_FILE_NOT_FOUND, ENOENT + }, + { + ERROR_PATH_NOT_FOUND, ENOENT + }, + { + ERROR_TOO_MANY_OPEN_FILES, EMFILE + }, + { + ERROR_ACCESS_DENIED, EACCES + }, + { + ERROR_INVALID_HANDLE, EBADF + }, + { + ERROR_ARENA_TRASHED, ENOMEM + }, + { + ERROR_NOT_ENOUGH_MEMORY, ENOMEM + }, + { + ERROR_INVALID_BLOCK, ENOMEM + }, + { + ERROR_BAD_ENVIRONMENT, E2BIG + }, + { + ERROR_BAD_FORMAT, ENOEXEC + }, + { + ERROR_INVALID_ACCESS, EINVAL + }, + { + ERROR_INVALID_DATA, EINVAL + }, + { + ERROR_INVALID_DRIVE, ENOENT + }, + { + ERROR_CURRENT_DIRECTORY, EACCES + }, + { + ERROR_NOT_SAME_DEVICE, EXDEV + }, + { + ERROR_NO_MORE_FILES, ENOENT + }, + { + ERROR_LOCK_VIOLATION, EACCES + }, + { + ERROR_SHARING_VIOLATION, EACCES + }, + { + ERROR_BAD_NETPATH, ENOENT + }, + { + ERROR_NETWORK_ACCESS_DENIED, EACCES + }, + { + ERROR_BAD_NET_NAME, ENOENT + }, + { + ERROR_FILE_EXISTS, EEXIST + }, + { + ERROR_CANNOT_MAKE, EACCES + }, + { + ERROR_FAIL_I24, EACCES + }, + { + ERROR_INVALID_PARAMETER, EINVAL + }, + { + ERROR_NO_PROC_SLOTS, EAGAIN + }, + { + ERROR_DRIVE_LOCKED, EACCES + }, + { + ERROR_BROKEN_PIPE, EPIPE + }, + { + ERROR_DISK_FULL, ENOSPC + }, + { + ERROR_INVALID_TARGET_HANDLE, EBADF + }, + { + ERROR_INVALID_HANDLE, EINVAL + }, + { + ERROR_WAIT_NO_CHILDREN, ECHILD + }, + { + ERROR_CHILD_NOT_COMPLETE, ECHILD + }, + { + ERROR_DIRECT_ACCESS_HANDLE, EBADF + }, + { + ERROR_NEGATIVE_SEEK, EINVAL + }, + { + ERROR_SEEK_ON_DEVICE, EACCES + }, + { + ERROR_DIR_NOT_EMPTY, ENOTEMPTY + }, + { + ERROR_NOT_LOCKED, EACCES + }, + { + ERROR_BAD_PATHNAME, ENOENT + }, + { + ERROR_MAX_THRDS_REACHED, EAGAIN + }, + { + ERROR_LOCK_FAILED, EACCES + }, + { + ERROR_ALREADY_EXISTS, EEXIST + }, + { + ERROR_FILENAME_EXCED_RANGE, ENOENT + }, + { + ERROR_NESTING_NOT_ALLOWED, EAGAIN + }, + { + ERROR_NOT_ENOUGH_QUOTA, ENOMEM + }, + { + ERROR_DELETE_PENDING, ENOENT + }, + { + ERROR_INVALID_NAME, ENOENT + }, + { + ERROR_CANT_RESOLVE_FILENAME, ENOENT + } +}; + +void +_dosmaperr(unsigned long e) +{ + int i; + + if (e == 0) + { + errno = 0; + return; + } + + for (i = 0; i < lengthof(doserrors); i++) + { + if (doserrors[i].winerr == e) + { + int doserr = doserrors[i].doserr; + +#ifndef FRONTEND + ereport(DEBUG5, + (errmsg_internal("mapped win32 error code %lu to %d", + e, doserr))); +#elif defined(FRONTEND_DEBUG) + fprintf(stderr, "mapped win32 error code %lu to %d", e, doserr); +#endif + errno = doserr; + return; + } + } + +#ifndef FRONTEND + ereport(LOG, + (errmsg_internal("unrecognized win32 error code: %lu", + e))); +#else + fprintf(stderr, "unrecognized win32 error code: %lu", e); +#endif + + errno = EINVAL; +} diff --git a/src/port/win32fdatasync.c b/src/port/win32fdatasync.c new file mode 100644 index 0000000..ad03880 --- /dev/null +++ b/src/port/win32fdatasync.c @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * win32fdatasync.c + * Win32 fdatasync() replacement + * + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * src/port/win32fdatasync.c + * + *------------------------------------------------------------------------- + */ + +#ifdef FRONTEND +#include "postgres_fe.h" +#else +#include "postgres.h" +#endif + +#include "port/win32ntdll.h" + +int +fdatasync(int fd) +{ + IO_STATUS_BLOCK iosb; + NTSTATUS status; + HANDLE handle; + + handle = (HANDLE) _get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + if (initialize_ntdll() < 0) + return -1; + + memset(&iosb, 0, sizeof(iosb)); + status = pg_NtFlushBuffersFileEx(handle, + FLUSH_FLAGS_FILE_DATA_SYNC_ONLY, + NULL, + 0, + &iosb); + + if (NT_SUCCESS(status)) + return 0; + + _dosmaperr(pg_RtlNtStatusToDosError(status)); + return -1; +} diff --git a/src/port/win32fseek.c b/src/port/win32fseek.c new file mode 100644 index 0000000..985313c --- /dev/null +++ b/src/port/win32fseek.c @@ -0,0 +1,75 @@ +/*------------------------------------------------------------------------- + * + * win32fseek.c + * Replacements for fseeko() and ftello(). + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32fseek.c + * + *------------------------------------------------------------------------- + */ + +#ifdef FRONTEND +#include "postgres_fe.h" +#else +#include "postgres.h" +#endif + +#if defined(WIN32) && defined(_MSC_VER) + +/* + * _pgfseeko64 + * + * Calling fseek() on a handle to a non-seeking device such as a pipe or + * a communications device is not supported, and fseek() may not return + * an error. This wrapper relies on the file type to check which cases + * are supported. + */ +int +_pgfseeko64(FILE *stream, pgoff_t offset, int origin) +{ + DWORD fileType; + HANDLE hFile = (HANDLE) _get_osfhandle(_fileno(stream)); + + fileType = pgwin32_get_file_type(hFile); + if (errno != 0) + return -1; + + if (fileType == FILE_TYPE_DISK) + return _fseeki64(stream, offset, origin); + else if (fileType == FILE_TYPE_CHAR || fileType == FILE_TYPE_PIPE) + errno = ESPIPE; + else + errno = EINVAL; + + return -1; +} + +/* + * _pgftello64 + * + * Same as _pgfseeko64(). + */ +pgoff_t +_pgftello64(FILE *stream) +{ + DWORD fileType; + HANDLE hFile = (HANDLE) _get_osfhandle(_fileno(stream)); + + fileType = pgwin32_get_file_type(hFile); + if (errno != 0) + return -1; + + if (fileType == FILE_TYPE_DISK) + return _ftelli64(stream); + else if (fileType == FILE_TYPE_CHAR || fileType == FILE_TYPE_PIPE) + errno = ESPIPE; + else + errno = EINVAL; + + return -1; +} + +#endif /* defined(WIN32) && defined(_MSC_VER) */ diff --git a/src/port/win32getrusage.c b/src/port/win32getrusage.c new file mode 100644 index 0000000..ce28b9b --- /dev/null +++ b/src/port/win32getrusage.c @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- + * + * win32getrusage.c + * get information about resource utilisation + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/win32getrusage.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <sys/resource.h> + +int +getrusage(int who, struct rusage *rusage) +{ + FILETIME starttime; + FILETIME exittime; + FILETIME kerneltime; + FILETIME usertime; + ULARGE_INTEGER li; + + if (who != RUSAGE_SELF) + { + /* Only RUSAGE_SELF is supported in this implementation for now */ + errno = EINVAL; + return -1; + } + + if (rusage == (struct rusage *) NULL) + { + errno = EFAULT; + return -1; + } + memset(rusage, 0, sizeof(struct rusage)); + if (GetProcessTimes(GetCurrentProcess(), + &starttime, &exittime, &kerneltime, &usertime) == 0) + { + _dosmaperr(GetLastError()); + return -1; + } + + /* Convert FILETIMEs (0.1 us) to struct timeval */ + memcpy(&li, &kerneltime, sizeof(FILETIME)); + li.QuadPart /= 10L; /* Convert to microseconds */ + rusage->ru_stime.tv_sec = li.QuadPart / 1000000L; + rusage->ru_stime.tv_usec = li.QuadPart % 1000000L; + + memcpy(&li, &usertime, sizeof(FILETIME)); + li.QuadPart /= 10L; /* Convert to microseconds */ + rusage->ru_utime.tv_sec = li.QuadPart / 1000000L; + rusage->ru_utime.tv_usec = li.QuadPart % 1000000L; + + return 0; +} diff --git a/src/port/win32gettimeofday.c b/src/port/win32gettimeofday.c new file mode 100644 index 0000000..1e00f7e --- /dev/null +++ b/src/port/win32gettimeofday.c @@ -0,0 +1,75 @@ +/* + * win32gettimeofday.c + * Win32 gettimeofday() replacement + * + * src/port/win32gettimeofday.c + * + * Copyright (c) 2003 SRA, Inc. + * Copyright (c) 2003 SKC, Inc. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose, without fee, and without a + * written agreement is hereby granted, provided that the above + * copyright notice and this paragraph and the following two + * paragraphs appear in all copies. + * + * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS + * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS + * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "c.h" + +#include <sysinfoapi.h> + +#include <sys/time.h> + +/* FILETIME of Jan 1 1970 00:00:00, the PostgreSQL epoch */ +static const unsigned __int64 epoch = UINT64CONST(116444736000000000); + +/* + * FILETIME represents the number of 100-nanosecond intervals since + * January 1, 1601 (UTC). + */ +#define FILETIME_UNITS_PER_SEC 10000000L +#define FILETIME_UNITS_PER_USEC 10 + + +/* + * timezone information is stored outside the kernel so tzp isn't used anymore. + * + * Note: this function is not for Win32 high precision timing purposes. See + * elapsed_time(). + */ +int +gettimeofday(struct timeval *tp, void *tzp) +{ + FILETIME file_time; + ULARGE_INTEGER ularge; + + /* + * POSIX declines to define what tzp points to, saying "If tzp is not a + * null pointer, the behavior is unspecified". Let's take this + * opportunity to verify that noplace in Postgres tries to use any + * unportable behavior. + */ + Assert(tzp == NULL); + + GetSystemTimePreciseAsFileTime(&file_time); + ularge.LowPart = file_time.dwLowDateTime; + ularge.HighPart = file_time.dwHighDateTime; + + tp->tv_sec = (long) ((ularge.QuadPart - epoch) / FILETIME_UNITS_PER_SEC); + tp->tv_usec = (long) (((ularge.QuadPart - epoch) % FILETIME_UNITS_PER_SEC) + / FILETIME_UNITS_PER_USEC); + + return 0; +} diff --git a/src/port/win32link.c b/src/port/win32link.c new file mode 100644 index 0000000..d1442ee --- /dev/null +++ b/src/port/win32link.c @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * win32link.c + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/win32link.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +int +link(const char *src, const char *dst) +{ + /* + * CreateHardLinkA returns zero for failure + * https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createhardlinka + */ + if (CreateHardLinkA(dst, src, NULL) == 0) + { + _dosmaperr(GetLastError()); + return -1; + } + else + return 0; +} diff --git a/src/port/win32ntdll.c b/src/port/win32ntdll.c new file mode 100644 index 0000000..3b38cdb --- /dev/null +++ b/src/port/win32ntdll.c @@ -0,0 +1,71 @@ +/*------------------------------------------------------------------------- + * + * win32ntdll.c + * Dynamically loaded Windows NT functions. + * + * Portions Copyright (c) 2021-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/win32ntdll.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include "port/win32ntdll.h" + +RtlGetLastNtStatus_t pg_RtlGetLastNtStatus; +RtlNtStatusToDosError_t pg_RtlNtStatusToDosError; +NtFlushBuffersFileEx_t pg_NtFlushBuffersFileEx; + +typedef struct NtDllRoutine +{ + const char *name; + pg_funcptr_t *address; +} NtDllRoutine; + +static const NtDllRoutine routines[] = { + {"RtlGetLastNtStatus", (pg_funcptr_t *) &pg_RtlGetLastNtStatus}, + {"RtlNtStatusToDosError", (pg_funcptr_t *) &pg_RtlNtStatusToDosError}, + {"NtFlushBuffersFileEx", (pg_funcptr_t *) &pg_NtFlushBuffersFileEx} +}; + +static bool initialized; + +int +initialize_ntdll(void) +{ + HMODULE module; + + if (initialized) + return 0; + + if (!(module = LoadLibraryEx("ntdll.dll", NULL, 0))) + { + _dosmaperr(GetLastError()); + return -1; + } + + for (int i = 0; i < lengthof(routines); ++i) + { + pg_funcptr_t address; + + address = (pg_funcptr_t) GetProcAddress(module, routines[i].name); + if (!address) + { + _dosmaperr(GetLastError()); + FreeLibrary(module); + + return -1; + } + + *(pg_funcptr_t *) routines[i].address = address; + } + + initialized = true; + + return 0; +} diff --git a/src/port/win32pread.c b/src/port/win32pread.c new file mode 100644 index 0000000..905cf9f --- /dev/null +++ b/src/port/win32pread.c @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * win32pread.c + * Implementation of pread(2) for Windows. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32pread.c + * + *------------------------------------------------------------------------- + */ + + +#include "c.h" + +#include <windows.h> + +ssize_t +pg_pread(int fd, void *buf, size_t size, off_t offset) +{ + OVERLAPPED overlapped = {0}; + HANDLE handle; + DWORD result; + + handle = (HANDLE) _get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + /* Note that this changes the file position, despite not using it. */ + overlapped.Offset = offset; + if (!ReadFile(handle, buf, size, &result, &overlapped)) + { + if (GetLastError() == ERROR_HANDLE_EOF) + return 0; + + _dosmaperr(GetLastError()); + return -1; + } + + return result; +} diff --git a/src/port/win32pwrite.c b/src/port/win32pwrite.c new file mode 100644 index 0000000..5dd1082 --- /dev/null +++ b/src/port/win32pwrite.c @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * win32pwrite.c + * Implementation of pwrite(2) for Windows. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32pwrite.c + * + *------------------------------------------------------------------------- + */ + + +#include "c.h" + +#include <windows.h> + +ssize_t +pg_pwrite(int fd, const void *buf, size_t size, off_t offset) +{ + OVERLAPPED overlapped = {0}; + HANDLE handle; + DWORD result; + + handle = (HANDLE) _get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + /* Note that this changes the file position, despite not using it. */ + overlapped.Offset = offset; + if (!WriteFile(handle, buf, size, &result, &overlapped)) + { + _dosmaperr(GetLastError()); + return -1; + } + + return result; +} diff --git a/src/port/win32security.c b/src/port/win32security.c new file mode 100644 index 0000000..4d9d28c --- /dev/null +++ b/src/port/win32security.c @@ -0,0 +1,190 @@ +/*------------------------------------------------------------------------- + * + * win32security.c + * Microsoft Windows Win32 Security Support Functions + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32security.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +static void log_error(const char *fmt,...) pg_attribute_printf(1, 2); + + +/* + * Utility wrapper for frontend and backend when reporting an error + * message. + */ +static void +log_error(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); +#ifndef FRONTEND + write_stderr(fmt, ap); +#else + fprintf(stderr, fmt, ap); +#endif + va_end(ap); +} + +/* + * Returns nonzero if the current user has administrative privileges, + * or zero if not. + * + * Note: this cannot use ereport() because it's called too early during + * startup. + */ +int +pgwin32_is_admin(void) +{ + PSID AdministratorsSid; + PSID PowerUsersSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + BOOL IsAdministrators; + BOOL IsPowerUsers; + + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, + 0, &AdministratorsSid)) + { + log_error(_("could not get SID for Administrators group: error code %lu\n"), + GetLastError()); + exit(1); + } + + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, + 0, &PowerUsersSid)) + { + log_error(_("could not get SID for PowerUsers group: error code %lu\n"), + GetLastError()); + exit(1); + } + + if (!CheckTokenMembership(NULL, AdministratorsSid, &IsAdministrators) || + !CheckTokenMembership(NULL, PowerUsersSid, &IsPowerUsers)) + { + log_error(_("could not check access token membership: error code %lu\n"), + GetLastError()); + exit(1); + } + + FreeSid(AdministratorsSid); + FreeSid(PowerUsersSid); + + if (IsAdministrators || IsPowerUsers) + return 1; + else + return 0; +} + +/* + * We consider ourselves running as a service if one of the following is + * true: + * + * 1) Standard error is not valid (always the case for services, and pg_ctl + * running as a service "passes" that down to postgres, + * c.f. CreateRestrictedProcess()) + * 2) We are running as LocalSystem (only used by services) + * 3) Our token contains SECURITY_SERVICE_RID (automatically added to the + * process token by the SCM when starting a service) + * + * The check for LocalSystem is needed, because surprisingly, if a service + * is running as LocalSystem, it does not have SECURITY_SERVICE_RID in its + * process token. + * + * Return values: + * 0 = Not service + * 1 = Service + * -1 = Error + * + * Note: we can't report errors via either ereport (we're called too early + * in the backend) or write_stderr (because that calls this). We are + * therefore reduced to writing directly on stderr, which sucks, but we + * have few alternatives. + */ +int +pgwin32_is_service(void) +{ + static int _is_service = -1; + BOOL IsMember; + PSID ServiceSid; + PSID LocalSystemSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + HANDLE stderr_handle; + + /* Only check the first time */ + if (_is_service != -1) + return _is_service; + + /* Check if standard error is not valid */ + stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + if (stderr_handle != INVALID_HANDLE_VALUE && stderr_handle != NULL) + { + _is_service = 0; + return _is_service; + } + + /* Check if running as LocalSystem */ + if (!AllocateAndInitializeSid(&NtAuthority, 1, + SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, + &LocalSystemSid)) + { + fprintf(stderr, "could not get SID for local system account\n"); + return -1; + } + + if (!CheckTokenMembership(NULL, LocalSystemSid, &IsMember)) + { + fprintf(stderr, "could not check access token membership: error code %lu\n", + GetLastError()); + FreeSid(LocalSystemSid); + return -1; + } + FreeSid(LocalSystemSid); + + if (IsMember) + { + _is_service = 1; + return _is_service; + } + + /* Check for service group membership */ + if (!AllocateAndInitializeSid(&NtAuthority, 1, + SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, + &ServiceSid)) + { + fprintf(stderr, "could not get SID for service group: error code %lu\n", + GetLastError()); + return -1; + } + + if (!CheckTokenMembership(NULL, ServiceSid, &IsMember)) + { + fprintf(stderr, "could not check access token membership: error code %lu\n", + GetLastError()); + FreeSid(ServiceSid); + return -1; + } + FreeSid(ServiceSid); + + if (IsMember) + _is_service = 1; + else + _is_service = 0; + + return _is_service; +} diff --git a/src/port/win32setlocale.c b/src/port/win32setlocale.c new file mode 100644 index 0000000..e2c85b0 --- /dev/null +++ b/src/port/win32setlocale.c @@ -0,0 +1,193 @@ +/*------------------------------------------------------------------------- + * + * win32setlocale.c + * Wrapper to work around bugs in Windows setlocale() implementation + * + * Copyright (c) 2011-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32setlocale.c + * + * + * The setlocale() function in Windows is broken in two ways. First, it + * has a problem with locale names that have a dot in the country name. For + * example: + * + * "Chinese (Traditional)_Hong Kong S.A.R..950" + * + * For some reason, setlocale() doesn't accept that as argument, even though + * setlocale(LC_ALL, NULL) returns exactly that. Fortunately, it accepts + * various alternative names for such countries, so to work around the broken + * setlocale() function, we map the troublemaking locale names to accepted + * aliases, before calling setlocale(). + * + * The second problem is that the locale name for "Norwegian (Bokmål)" + * contains a non-ASCII character. That's problematic, because it's not clear + * what encoding the locale name itself is supposed to be in, when you + * haven't yet set a locale. Also, it causes problems when the cluster + * contains databases with different encodings, as the locale name is stored + * in the pg_database system catalog. To work around that, when setlocale() + * returns that locale name, map it to a pure-ASCII alias for the same + * locale. + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#undef setlocale + +struct locale_map +{ + /* + * String in locale name to replace. Can be a single string (end is NULL), + * or separate start and end strings. If two strings are given, the locale + * name must contain both of them, and everything between them is + * replaced. This is used for a poor-man's regexp search, allowing + * replacement of "start.*end". + */ + const char *locale_name_start; + const char *locale_name_end; + + const char *replacement; /* string to replace the match with */ +}; + +/* + * Mappings applied before calling setlocale(), to the argument. + */ +static const struct locale_map locale_map_argument[] = { + /* + * "HKG" is listed here: + * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx + * (Country/Region Strings). + * + * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the + * above list, but seems to work anyway. + */ + {"Hong Kong S.A.R.", NULL, "HKG"}, + {"U.A.E.", NULL, "ARE"}, + + /* + * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't + * seem to recognize that. And Macau isn't listed in the table of accepted + * abbreviations linked above. Fortunately, "ZHM" seems to be accepted as + * an alias for "Chinese (Traditional)_Macau S.A.R..950". I'm not sure + * where "ZHM" comes from, must be some legacy naming scheme. But hey, it + * works. + * + * Note that unlike HKG and ARE, ZHM is an alias for the *whole* locale + * name, not just the country part. + * + * Some versions of Windows spell it "Macau", others "Macao". + */ + {"Chinese (Traditional)_Macau S.A.R..950", NULL, "ZHM"}, + {"Chinese_Macau S.A.R..950", NULL, "ZHM"}, + {"Chinese (Traditional)_Macao S.A.R..950", NULL, "ZHM"}, + {"Chinese_Macao S.A.R..950", NULL, "ZHM"}, + {NULL, NULL, NULL} +}; + +/* + * Mappings applied after calling setlocale(), to its return value. + */ +static const struct locale_map locale_map_result[] = { + /* + * "Norwegian (Bokmål)" locale name contains the a-ring character. + * Map it to a pure-ASCII alias. + * + * It's not clear what encoding setlocale() uses when it returns the + * locale name, so to play it safe, we search for "Norwegian (Bok*l)". + * + * Just to make life even more complicated, some versions of Windows spell + * the locale name without parentheses. Translate that too. + */ + {"Norwegian (Bokm", "l)_Norway", "Norwegian_Norway"}, + {"Norwegian Bokm", "l_Norway", "Norwegian_Norway"}, + {NULL, NULL, NULL} +}; + +#define MAX_LOCALE_NAME_LEN 100 + +static const char * +map_locale(const struct locale_map *map, const char *locale) +{ + static char aliasbuf[MAX_LOCALE_NAME_LEN]; + int i; + + /* Check if the locale name matches any of the problematic ones. */ + for (i = 0; map[i].locale_name_start != NULL; i++) + { + const char *needle_start = map[i].locale_name_start; + const char *needle_end = map[i].locale_name_end; + const char *replacement = map[i].replacement; + char *match; + char *match_start = NULL; + char *match_end = NULL; + + match = strstr(locale, needle_start); + if (match) + { + /* + * Found a match for the first part. If this was a two-part + * replacement, find the second part. + */ + match_start = match; + if (needle_end) + { + match = strstr(match_start + strlen(needle_start), needle_end); + if (match) + match_end = match + strlen(needle_end); + else + match_start = NULL; + } + else + match_end = match_start + strlen(needle_start); + } + + if (match_start) + { + /* Found a match. Replace the matched string. */ + int matchpos = match_start - locale; + int replacementlen = strlen(replacement); + char *rest = match_end; + int restlen = strlen(rest); + + /* check that the result fits in the static buffer */ + if (matchpos + replacementlen + restlen + 1 > MAX_LOCALE_NAME_LEN) + return NULL; + + memcpy(&aliasbuf[0], &locale[0], matchpos); + memcpy(&aliasbuf[matchpos], replacement, replacementlen); + /* includes null terminator */ + memcpy(&aliasbuf[matchpos + replacementlen], rest, restlen + 1); + + return aliasbuf; + } + } + + /* no match, just return the original string */ + return locale; +} + +char * +pgwin32_setlocale(int category, const char *locale) +{ + const char *argument; + char *result; + + if (locale == NULL) + argument = NULL; + else + argument = map_locale(locale_map_argument, locale); + + /* Call the real setlocale() function */ + result = setlocale(category, argument); + + /* + * setlocale() is specified to return a "char *" that the caller is + * forbidden to modify, so casting away the "const" is innocuous. + */ + if (result) + result = unconstify(char *, map_locale(locale_map_result, result)); + + return result; +} diff --git a/src/port/win32stat.c b/src/port/win32stat.c new file mode 100644 index 0000000..aa3a0c1 --- /dev/null +++ b/src/port/win32stat.c @@ -0,0 +1,306 @@ +/*------------------------------------------------------------------------- + * + * win32stat.c + * Replacements for <sys/stat.h> functions using GetFileInformationByHandle + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/win32stat.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 + +#include "c.h" +#include "port/win32ntdll.h" + +#include <windows.h> + +/* + * Convert a FILETIME struct into a 64 bit time_t. + */ +static __time64_t +filetime_to_time(const FILETIME *ft) +{ + ULARGE_INTEGER unified_ft = {0}; + static const uint64 EpochShift = UINT64CONST(116444736000000000); + + unified_ft.LowPart = ft->dwLowDateTime; + unified_ft.HighPart = ft->dwHighDateTime; + + if (unified_ft.QuadPart < EpochShift) + return -1; + + unified_ft.QuadPart -= EpochShift; + unified_ft.QuadPart /= 10 * 1000 * 1000; + + return unified_ft.QuadPart; +} + +/* + * Convert WIN32 file attributes to a Unix-style mode. + * + * Only owner permissions are set. + */ +static unsigned short +fileattr_to_unixmode(int attr) +{ + unsigned short uxmode = 0; + + uxmode |= (unsigned short) ((attr & FILE_ATTRIBUTE_DIRECTORY) ? + (_S_IFDIR) : (_S_IFREG)); + + uxmode |= (unsigned short) ((attr & FILE_ATTRIBUTE_READONLY) ? + (_S_IREAD) : (_S_IREAD | _S_IWRITE)); + + /* there is no need to simulate _S_IEXEC using CMD's PATHEXT extensions */ + uxmode |= _S_IEXEC; + + return uxmode; +} + +/* + * Convert WIN32 file information (from a HANDLE) to a struct stat. + */ +static int +fileinfo_to_stat(HANDLE hFile, struct stat *buf) +{ + BY_HANDLE_FILE_INFORMATION fiData; + + memset(buf, 0, sizeof(*buf)); + + /* + * GetFileInformationByHandle minimum supported version: Windows XP and + * Windows Server 2003, so it exists everywhere we care about. + */ + if (!GetFileInformationByHandle(hFile, &fiData)) + { + _dosmaperr(GetLastError()); + return -1; + } + + if (fiData.ftLastWriteTime.dwLowDateTime || + fiData.ftLastWriteTime.dwHighDateTime) + buf->st_mtime = filetime_to_time(&fiData.ftLastWriteTime); + + if (fiData.ftLastAccessTime.dwLowDateTime || + fiData.ftLastAccessTime.dwHighDateTime) + buf->st_atime = filetime_to_time(&fiData.ftLastAccessTime); + else + buf->st_atime = buf->st_mtime; + + if (fiData.ftCreationTime.dwLowDateTime || + fiData.ftCreationTime.dwHighDateTime) + buf->st_ctime = filetime_to_time(&fiData.ftCreationTime); + else + buf->st_ctime = buf->st_mtime; + + buf->st_mode = fileattr_to_unixmode(fiData.dwFileAttributes); + buf->st_nlink = fiData.nNumberOfLinks; + + buf->st_size = ((((uint64) fiData.nFileSizeHigh) << 32) | + fiData.nFileSizeLow); + + return 0; +} + +/* + * Windows implementation of lstat(). + */ +int +_pglstat64(const char *name, struct stat *buf) +{ + /* + * Our open wrapper will report STATUS_DELETE_PENDING as ENOENT. We + * request FILE_FLAG_BACKUP_SEMANTICS so that we can open directories too, + * for limited purposes. We use the private handle-based version, so we + * don't risk running out of fds. + */ + HANDLE hFile; + int ret; + + hFile = pgwin32_open_handle(name, O_RDONLY, true); + 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; + } + 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)) || hFile == INVALID_HANDLE_VALUE) + { + char next[MAXPGPATH]; + ssize_t size; + + /* + * POSIX says we need to put the length of the target path into + * st_size. Use readlink() to get it, or learn that this is not a + * junction point. + */ + size = readlink(name, next, sizeof(next)); + if (size < 0) + { + if (errno == EACCES && + pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING) + { + /* Unlinked underneath us. */ + errno = ENOENT; + ret = -1; + } + else if (errno == EINVAL) + { + /* It's not a junction point, nothing to do. */ + } + else + { + /* Some other failure. */ + ret = -1; + } + } + else + { + /* It's a junction point, so report it as a symlink. */ + buf->st_mode &= ~S_IFDIR; + buf->st_mode |= S_IFLNK; + buf->st_size = size; + ret = 0; + } + } + + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + return ret; +} + +/* + * Windows implementation of stat(). + */ +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)? */ + 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(curr, next, sizeof(next)); + if (size < 0) + { + if (errno == EACCES && + pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING) + { + /* Unlinked underneath us. */ + errno = ENOENT; + } + return -1; + } + if (size >= sizeof(next)) + { + errno = ENAMETOOLONG; + return -1; + } + next[size] = 0; + + ret = _pglstat64(next, buf); + strcpy(curr, next); + } + + return ret; +} + +/* + * Windows implementation of fstat(). + */ +int +_pgfstat64(int fileno, struct stat *buf) +{ + HANDLE hFile = (HANDLE) _get_osfhandle(fileno); + DWORD fileType = FILE_TYPE_UNKNOWN; + unsigned short st_mode; + + if (buf == NULL) + { + errno = EINVAL; + return -1; + } + + fileType = pgwin32_get_file_type(hFile); + if (errno != 0) + return -1; + + switch (fileType) + { + /* The specified file is a disk file */ + case FILE_TYPE_DISK: + return fileinfo_to_stat(hFile, buf); + + /* + * The specified file is a socket, a named pipe, or an anonymous + * pipe. + */ + case FILE_TYPE_PIPE: + st_mode = _S_IFIFO; + break; + /* The specified file is a character file */ + case FILE_TYPE_CHAR: + st_mode = _S_IFCHR; + break; + /* Unused flag and unknown file type */ + case FILE_TYPE_REMOTE: + case FILE_TYPE_UNKNOWN: + default: + errno = EINVAL; + return -1; + } + + memset(buf, 0, sizeof(*buf)); + buf->st_mode = st_mode; + buf->st_dev = fileno; + buf->st_rdev = fileno; + buf->st_nlink = 1; + return 0; +} + +#endif /* WIN32 */ diff --git a/src/port/win32ver.rc b/src/port/win32ver.rc new file mode 100644 index 0000000..05237f1 --- /dev/null +++ b/src/port/win32ver.rc @@ -0,0 +1,35 @@ +#include <winver.h> +#include "pg_config.h" + +// https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PG_MAJORVERSION_NUM,0,PG_MINORVERSION_NUM,0 + PRODUCTVERSION PG_MAJORVERSION_NUM,0,PG_MINORVERSION_NUM,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0x0L + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" // U.S. English, Unicode + BEGIN + VALUE "CompanyName", "PostgreSQL Global Development Group" + VALUE "FileDescription", FILEDESC + VALUE "FileVersion", PG_VERSION + VALUE "InternalName", _INTERNAL_NAME_ + VALUE "LegalCopyright", "Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group. Portions Copyright (c) 1994, Regents of the University of California." + VALUE "OriginalFileName", _ORIGINAL_NAME_ + VALUE "ProductName", "PostgreSQL" + VALUE "ProductVersion", PG_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 // U.S. English, Unicode + END +END + +_ICO_ |