summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/Makefile71
-rw-r--r--src/common/exec.c793
-rw-r--r--src/common/fe_memutils.c128
-rw-r--r--src/common/pgfnames.c107
-rw-r--r--src/common/psprintf.c190
-rw-r--r--src/common/relpath.c251
-rw-r--r--src/common/rmtree.c131
-rw-r--r--src/common/username.c87
-rw-r--r--src/common/wait_error.c86
9 files changed, 1844 insertions, 0 deletions
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644
index 0000000000..7edbaaa2c1
--- /dev/null
+++ b/src/common/Makefile
@@ -0,0 +1,71 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+# Makefile for src/common
+#
+# This makefile generates two outputs:
+#
+# libpgcommon.a - contains object files with FRONTEND defined,
+# for use by client application and libraries
+#
+# libpgcommon_srv.a - contains object files without FRONTEND defined,
+# for use only by the backend binaries
+#
+# IDENTIFICATION
+# src/common/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/common
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+
+override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
+LIBS += $(PTHREAD_LIBS)
+
+OBJS_COMMON = exec.o pgfnames.o psprintf.o relpath.o rmtree.o username.o wait_error.o
+
+OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o
+
+OBJS_SRV = $(OBJS_COMMON:%.o=%_srv.o)
+
+all: libpgcommon.a libpgcommon_srv.a
+
+# libpgcommon is needed by some contrib
+install: all installdirs
+ $(INSTALL_STLIB) libpgcommon.a '$(DESTDIR)$(libdir)/libpgcommon.a'
+
+installdirs:
+ $(MKDIR_P) '$(DESTDIR)$(libdir)'
+
+uninstall:
+ rm -f '$(DESTDIR)$(libdir)/libpgcommon.a'
+
+libpgcommon.a: $(OBJS_FRONTEND)
+ $(AR) $(AROPT) $@ $^
+
+#
+# Server versions of object files
+#
+
+libpgcommon_srv.a: $(OBJS_SRV)
+ $(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.
+%_srv.o: %.c %.o
+ $(CC) $(CFLAGS) $(subst -DFRONTEND,, $(CPPFLAGS)) -c $< -o $@
+
+$(OBJS_SRV): | submake-errcodes
+
+.PHONY: submake-errcodes
+
+submake-errcodes:
+ $(MAKE) -C ../backend submake-errcodes
+
+clean distclean maintainer-clean:
+ rm -f libpgcommon.a libpgcommon_srv.a $(OBJS_FRONTEND) $(OBJS_SRV)
diff --git a/src/common/exec.c b/src/common/exec.c
new file mode 100644
index 0000000000..037bef2210
--- /dev/null
+++ b/src/common/exec.c
@@ -0,0 +1,793 @@
+/*-------------------------------------------------------------------------
+ *
+ * exec.c
+ * Functions for finding and validating executable files
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/common/exec.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#ifndef FRONTEND
+/* We use only 3- and 4-parameter elog calls in this file, for simplicity */
+/* NOTE: caller must provide gettext call around str! */
+#define log_error(str, param) elog(LOG, str, param)
+#define log_error4(str, param, arg1) elog(LOG, str, param, arg1)
+#else
+#define log_error(str, param) (fprintf(stderr, str, param), fputc('\n', stderr))
+#define log_error4(str, param, arg1) (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
+#endif
+
+#ifdef WIN32_ONLY_COMPILER
+#define getcwd(cwd,len) GetCurrentDirectory(len, cwd)
+#endif
+
+static int validate_exec(const char *path);
+static int resolve_symlinks(char *path);
+static char *pipe_read_line(char *cmd, char *line, int maxsize);
+
+#ifdef WIN32
+static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
+#endif
+
+/*
+ * validate_exec -- validate "path" as an executable file
+ *
+ * returns 0 if the file is found and no error is encountered.
+ * -1 if the regular file "path" does not exist or cannot be executed.
+ * -2 if the file is otherwise valid but cannot be read.
+ */
+static int
+validate_exec(const char *path)
+{
+ struct stat buf;
+ int is_r;
+ int is_x;
+
+#ifdef WIN32
+ char path_exe[MAXPGPATH + sizeof(".exe") - 1];
+
+ /* Win32 requires a .exe suffix for stat() */
+ if (strlen(path) >= strlen(".exe") &&
+ pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
+ {
+ strlcpy(path_exe, path, sizeof(path_exe) - 4);
+ strcat(path_exe, ".exe");
+ path = path_exe;
+ }
+#endif
+
+ /*
+ * Ensure that the file exists and is a regular file.
+ *
+ * XXX if you have a broken system where stat() looks at the symlink
+ * instead of the underlying file, you lose.
+ */
+ if (stat(path, &buf) < 0)
+ return -1;
+
+ if (!S_ISREG(buf.st_mode))
+ return -1;
+
+ /*
+ * Ensure that the file is both executable and readable (required for
+ * dynamic loading).
+ */
+#ifndef WIN32
+ is_r = (access(path, R_OK) == 0);
+ is_x = (access(path, X_OK) == 0);
+#else
+ is_r = buf.st_mode & S_IRUSR;
+ is_x = buf.st_mode & S_IXUSR;
+#endif
+ return is_x ? (is_r ? 0 : -2) : -1;
+}
+
+
+/*
+ * find_my_exec -- find an absolute path to a valid executable
+ *
+ * argv0 is the name passed on the command line
+ * retpath is the output area (must be of size MAXPGPATH)
+ * Returns 0 if OK, -1 if error.
+ *
+ * The reason we have to work so hard to find an absolute path is that
+ * on some platforms we can't do dynamic loading unless we know the
+ * executable's location. Also, we need a full path not a relative
+ * path because we will later change working directory. Finally, we want
+ * a true path not a symlink location, so that we can locate other files
+ * that are part of our installation relative to the executable.
+ */
+int
+find_my_exec(const char *argv0, char *retpath)
+{
+ char cwd[MAXPGPATH],
+ test_path[MAXPGPATH];
+ char *path;
+
+ if (!getcwd(cwd, MAXPGPATH))
+ {
+ log_error(_("could not identify current directory: %s"),
+ strerror(errno));
+ return -1;
+ }
+
+ /*
+ * If argv0 contains a separator, then PATH wasn't used.
+ */
+ if (first_dir_separator(argv0) != NULL)
+ {
+ if (is_absolute_path(argv0))
+ StrNCpy(retpath, argv0, MAXPGPATH);
+ else
+ join_path_components(retpath, cwd, argv0);
+ canonicalize_path(retpath);
+
+ if (validate_exec(retpath) == 0)
+ return resolve_symlinks(retpath);
+
+ log_error(_("invalid binary \"%s\""), retpath);
+ return -1;
+ }
+
+#ifdef WIN32
+ /* Win32 checks the current directory first for names without slashes */
+ join_path_components(retpath, cwd, argv0);
+ if (validate_exec(retpath) == 0)
+ return resolve_symlinks(retpath);
+#endif
+
+ /*
+ * Since no explicit path was supplied, the user must have been relying on
+ * PATH. We'll search the same PATH.
+ */
+ if ((path = getenv("PATH")) && *path)
+ {
+ char *startp = NULL,
+ *endp = NULL;
+
+ do
+ {
+ if (!startp)
+ startp = path;
+ else
+ startp = endp + 1;
+
+ endp = first_path_var_separator(startp);
+ if (!endp)
+ endp = startp + strlen(startp); /* point to end */
+
+ StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
+
+ if (is_absolute_path(test_path))
+ join_path_components(retpath, test_path, argv0);
+ else
+ {
+ join_path_components(retpath, cwd, test_path);
+ join_path_components(retpath, retpath, argv0);
+ }
+ canonicalize_path(retpath);
+
+ switch (validate_exec(retpath))
+ {
+ case 0: /* found ok */
+ return resolve_symlinks(retpath);
+ case -1: /* wasn't even a candidate, keep looking */
+ break;
+ case -2: /* found but disqualified */
+ log_error(_("could not read binary \"%s\""),
+ retpath);
+ break;
+ }
+ } while (*endp);
+ }
+
+ log_error(_("could not find a \"%s\" to execute"), argv0);
+ return -1;
+}
+
+
+/*
+ * resolve_symlinks - resolve symlinks to the underlying file
+ *
+ * Replace "path" by the absolute path to the referenced file.
+ *
+ * Returns 0 if OK, -1 if error.
+ *
+ * Note: we are not particularly tense about producing nice error messages
+ * because we are not really expecting error here; we just determined that
+ * the symlink does point to a valid executable.
+ */
+static int
+resolve_symlinks(char *path)
+{
+#ifdef HAVE_READLINK
+ struct stat buf;
+ char orig_wd[MAXPGPATH],
+ link_buf[MAXPGPATH];
+ char *fname;
+
+ /*
+ * To resolve a symlink properly, we have to chdir into its directory and
+ * then chdir to where the symlink points; otherwise we may fail to
+ * resolve relative links correctly (consider cases involving mount
+ * points, for example). After following the final symlink, we use
+ * getcwd() to figure out where the heck we're at.
+ *
+ * One might think we could skip all this if path doesn't point to a
+ * symlink to start with, but that's wrong. We also want to get rid of
+ * any directory symlinks that are present in the given path. We expect
+ * getcwd() to give us an accurate, symlink-free path.
+ */
+ if (!getcwd(orig_wd, MAXPGPATH))
+ {
+ log_error(_("could not identify current directory: %s"),
+ strerror(errno));
+ return -1;
+ }
+
+ for (;;)
+ {
+ char *lsep;
+ int rllen;
+
+ lsep = last_dir_separator(path);
+ if (lsep)
+ {
+ *lsep = '\0';
+ if (chdir(path) == -1)
+ {
+ log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
+ return -1;
+ }
+ fname = lsep + 1;
+ }
+ else
+ fname = path;
+
+ if (lstat(fname, &buf) < 0 ||
+ !S_ISLNK(buf.st_mode))
+ break;
+
+ rllen = readlink(fname, link_buf, sizeof(link_buf));
+ if (rllen < 0 || rllen >= sizeof(link_buf))
+ {
+ log_error(_("could not read symbolic link \"%s\""), fname);
+ return -1;
+ }
+ link_buf[rllen] = '\0';
+ strcpy(path, link_buf);
+ }
+
+ /* must copy final component out of 'path' temporarily */
+ strlcpy(link_buf, fname, sizeof(link_buf));
+
+ if (!getcwd(path, MAXPGPATH))
+ {
+ log_error(_("could not identify current directory: %s"),
+ strerror(errno));
+ return -1;
+ }
+ join_path_components(path, path, link_buf);
+ canonicalize_path(path);
+
+ if (chdir(orig_wd) == -1)
+ {
+ log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
+ return -1;
+ }
+#endif /* HAVE_READLINK */
+
+ return 0;
+}
+
+
+/*
+ * Find another program in our binary's directory,
+ * then make sure it is the proper version.
+ */
+int
+find_other_exec(const char *argv0, const char *target,
+ const char *versionstr, char *retpath)
+{
+ char cmd[MAXPGPATH];
+ char line[100];
+
+ if (find_my_exec(argv0, retpath) < 0)
+ return -1;
+
+ /* Trim off program name and keep just directory */
+ *last_dir_separator(retpath) = '\0';
+ canonicalize_path(retpath);
+
+ /* Now append the other program's name */
+ snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
+ "/%s%s", target, EXE);
+
+ if (validate_exec(retpath) != 0)
+ return -1;
+
+ snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
+
+ if (!pipe_read_line(cmd, line, sizeof(line)))
+ return -1;
+
+ if (strcmp(line, versionstr) != 0)
+ return -2;
+
+ return 0;
+}
+
+
+/*
+ * The runtime library's popen() on win32 does not work when being
+ * called from a service when running on windows <= 2000, because
+ * there is no stdin/stdout/stderr.
+ *
+ * Executing a command in a pipe and reading the first line from it
+ * is all we need.
+ */
+static char *
+pipe_read_line(char *cmd, char *line, int maxsize)
+{
+#ifndef WIN32
+ FILE *pgver;
+
+ /* flush output buffers in case popen does not... */
+ fflush(stdout);
+ fflush(stderr);
+
+ errno = 0;
+ if ((pgver = popen(cmd, "r")) == NULL)
+ {
+ perror("popen failure");
+ return NULL;
+ }
+
+ errno = 0;
+ if (fgets(line, maxsize, pgver) == NULL)
+ {
+ if (feof(pgver))
+ fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
+ else
+ perror("fgets failure");
+ pclose(pgver); /* no error checking */
+ return NULL;
+ }
+
+ if (pclose_check(pgver))
+ return NULL;
+
+ return line;
+#else /* WIN32 */
+
+ SECURITY_ATTRIBUTES sattr;
+ HANDLE childstdoutrd,
+ childstdoutwr,
+ childstdoutrddup;
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ char *retval = NULL;
+
+ sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sattr.bInheritHandle = TRUE;
+ sattr.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
+ return NULL;
+
+ if (!DuplicateHandle(GetCurrentProcess(),
+ childstdoutrd,
+ GetCurrentProcess(),
+ &childstdoutrddup,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ CloseHandle(childstdoutrd);
+ CloseHandle(childstdoutwr);
+ return NULL;
+ }
+
+ CloseHandle(childstdoutrd);
+
+ ZeroMemory(&pi, sizeof(pi));
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdError = childstdoutwr;
+ si.hStdOutput = childstdoutwr;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+
+ if (CreateProcess(NULL,
+ cmd,
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &si,
+ &pi))
+ {
+ /* Successfully started the process */
+ char *lineptr;
+
+ ZeroMemory(line, maxsize);
+
+ /* Try to read at least one line from the pipe */
+ /* This may require more than one wait/read attempt */
+ for (lineptr = line; lineptr < line + maxsize - 1;)
+ {
+ DWORD bytesread = 0;
+
+ /* Let's see if we can read */
+ if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
+ break; /* Timeout, but perhaps we got a line already */
+
+ if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
+ &bytesread, NULL))
+ break; /* Error, but perhaps we got a line already */
+
+ lineptr += strlen(lineptr);
+
+ if (!bytesread)
+ break; /* EOF */
+
+ if (strchr(line, '\n'))
+ break; /* One or more lines read */
+ }
+
+ if (lineptr != line)
+ {
+ /* OK, we read some data */
+ int len;
+
+ /* If we got more than one line, cut off after the first \n */
+ lineptr = strchr(line, '\n');
+ if (lineptr)
+ *(lineptr + 1) = '\0';
+
+ len = strlen(line);
+
+ /*
+ * If EOL is \r\n, convert to just \n. Because stdout is a
+ * text-mode stream, the \n output by the child process is
+ * received as \r\n, so we convert it to \n. The server main.c
+ * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
+ * disabling \n to \r\n expansion for stdout.
+ */
+ if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
+ {
+ line[len - 2] = '\n';
+ line[len - 1] = '\0';
+ len--;
+ }
+
+ /*
+ * We emulate fgets() behaviour. So if there is no newline at the
+ * end, we add one...
+ */
+ if (len == 0 || line[len - 1] != '\n')
+ strcat(line, "\n");
+
+ retval = line;
+ }
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+
+ CloseHandle(childstdoutwr);
+ CloseHandle(childstdoutrddup);
+
+ return retval;
+#endif /* WIN32 */
+}
+
+
+/*
+ * pclose() plus useful error reporting
+ */
+int
+pclose_check(FILE *stream)
+{
+ int exitstatus;
+ char *reason;
+
+ exitstatus = pclose(stream);
+
+ if (exitstatus == 0)
+ return 0; /* all is well */
+
+ if (exitstatus == -1)
+ {
+ /* pclose() itself failed, and hopefully set errno */
+ log_error(_("pclose failed: %s"), strerror(errno));
+ }
+ else
+ {
+ reason = wait_result_to_str(exitstatus);
+ log_error("%s", reason);
+#ifdef FRONTEND
+ free(reason);
+#else
+ pfree(reason);
+#endif
+ }
+ return exitstatus;
+}
+
+/*
+ * set_pglocale_pgservice
+ *
+ * Set application-specific locale and service directory
+ *
+ * This function takes the value of argv[0] rather than a full path.
+ *
+ * (You may be wondering why this is in exec.c. It requires this module's
+ * services and doesn't introduce any new dependencies, so this seems as
+ * good as anyplace.)
+ */
+void
+set_pglocale_pgservice(const char *argv0, const char *app)
+{
+ char path[MAXPGPATH];
+ char my_exec_path[MAXPGPATH];
+ char env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")]; /* longer than
+ * PGLOCALEDIR */
+
+ /* don't set LC_ALL in the backend */
+ if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
+ setlocale(LC_ALL, "");
+
+ if (find_my_exec(argv0, my_exec_path) < 0)
+ return;
+
+#ifdef ENABLE_NLS
+ get_locale_path(my_exec_path, path);
+ bindtextdomain(app, path);
+ textdomain(app);
+
+ if (getenv("PGLOCALEDIR") == NULL)
+ {
+ /* set for libpq to use */
+ snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
+ canonicalize_path(env_path + 12);
+ putenv(strdup(env_path));
+ }
+#endif
+
+ if (getenv("PGSYSCONFDIR") == NULL)
+ {
+ get_etc_path(my_exec_path, path);
+
+ /* set for libpq to use */
+ snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
+ canonicalize_path(env_path + 13);
+ putenv(strdup(env_path));
+ }
+}
+
+#ifdef WIN32
+
+/*
+ * AddUserToTokenDacl(HANDLE hToken)
+ *
+ * This function adds the current user account to the restricted
+ * token used when we create a restricted process.
+ *
+ * This is required because of some security changes in Windows
+ * that appeared in patches to XP/2K3 and in Vista/2008.
+ *
+ * On these machines, the Administrator account is not included in
+ * the default DACL - you just get Administrators + System. For
+ * regular users you get User + System. Because we strip Administrators
+ * when we create the restricted token, we are left with only System
+ * in the DACL which leads to access denied errors for later CreatePipe()
+ * and CreateProcess() calls when running as Administrator.
+ *
+ * This function fixes this problem by modifying the DACL of the
+ * token the process will use, and explicitly re-adding the current
+ * user account. This is still secure because the Administrator account
+ * inherits its privileges from the Administrators group - it doesn't
+ * have any of its own.
+ */
+BOOL
+AddUserToTokenDacl(HANDLE hToken)
+{
+ int i;
+ ACL_SIZE_INFORMATION asi;
+ ACCESS_ALLOWED_ACE *pace;
+ DWORD dwNewAclSize;
+ DWORD dwSize = 0;
+ DWORD dwTokenInfoLength = 0;
+ PACL pacl = NULL;
+ PTOKEN_USER pTokenUser = NULL;
+ TOKEN_DEFAULT_DACL tddNew;
+ TOKEN_DEFAULT_DACL *ptdd = NULL;
+ TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
+ BOOL ret = FALSE;
+
+ /* Figure out the buffer size for the DACL info */
+ if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
+ if (ptdd == NULL)
+ {
+ log_error("could not allocate %lu bytes of memory", dwSize);
+ goto cleanup;
+ }
+
+ if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
+ {
+ log_error("could not get token information: error code %lu", GetLastError());
+ goto cleanup;
+ }
+ }
+ else
+ {
+ log_error("could not get token information buffer size: error code %lu", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Get the ACL info */
+ if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
+ (DWORD) sizeof(ACL_SIZE_INFORMATION),
+ AclSizeInformation))
+ {
+ log_error("could not get ACL information: error code %lu", GetLastError());
+ goto cleanup;
+ }
+
+ /*
+ * Get the user token for the current user, which provides us with the SID
+ * that is needed for creating the ACL.
+ */
+ if (!GetTokenUser(hToken, &pTokenUser))
+ {
+ log_error("could not get user token: error code %lu", GetLastError());
+ goto cleanup;
+ }
+
+ /* Figure out the size of the new ACL */
+ dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
+ GetLengthSid(pTokenUser->User.Sid) -sizeof(DWORD);
+
+ /* Allocate the ACL buffer & initialize it */
+ pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
+ if (pacl == NULL)
+ {
+ log_error("could not allocate %lu bytes of memory", dwNewAclSize);
+ goto cleanup;
+ }
+
+ if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
+ {
+ log_error("could not initialize ACL: error code %lu", GetLastError());
+ goto cleanup;
+ }
+
+ /* Loop through the existing ACEs, and build the new ACL */
+ for (i = 0; i < (int) asi.AceCount; i++)
+ {
+ if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
+ {
+ log_error("could not get ACE: error code %lu", GetLastError());
+ goto cleanup;
+ }
+
+ if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
+ {
+ log_error("could not add ACE: error code %lu", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ /* Add the new ACE for the current user */
+ if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
+ {
+ log_error("could not add access allowed ACE: error code %lu", GetLastError());
+ goto cleanup;
+ }
+
+ /* Set the new DACL in the token */
+ tddNew.DefaultDacl = pacl;
+
+ if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
+ {
+ log_error("could not set token information: error code %lu", GetLastError());
+ goto cleanup;
+ }
+
+ ret = TRUE;
+
+cleanup:
+ if (pTokenUser)
+ LocalFree((HLOCAL) pTokenUser);
+
+ if (pacl)
+ LocalFree((HLOCAL) pacl);
+
+ if (ptdd)
+ LocalFree((HLOCAL) ptdd);
+
+ return ret;
+}
+
+/*
+ * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
+ *
+ * Get the users token information from a process token.
+ *
+ * The caller of this function is responsible for calling LocalFree() on the
+ * returned TOKEN_USER memory.
+ */
+static BOOL
+GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
+{
+ DWORD dwLength;
+
+ *ppTokenUser = NULL;
+
+ if (!GetTokenInformation(hToken,
+ TokenUser,
+ NULL,
+ 0,
+ &dwLength))
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
+
+ if (*ppTokenUser == NULL)
+ {
+ log_error("could not allocate %lu bytes of memory", dwLength);
+ return FALSE;
+ }
+ }
+ else
+ {
+ log_error("could not get token information buffer size: error code %lu", GetLastError());
+ return FALSE;
+ }
+ }
+
+ if (!GetTokenInformation(hToken,
+ TokenUser,
+ *ppTokenUser,
+ dwLength,
+ &dwLength))
+ {
+ LocalFree(*ppTokenUser);
+ *ppTokenUser = NULL;
+
+ log_error("could not get token information: error code %lu", GetLastError());
+ return FALSE;
+ }
+
+ /* Memory in *ppTokenUser is LocalFree():d by the caller */
+ return TRUE;
+}
+
+#endif
diff --git a/src/common/fe_memutils.c b/src/common/fe_memutils.c
new file mode 100644
index 0000000000..f90ae64796
--- /dev/null
+++ b/src/common/fe_memutils.c
@@ -0,0 +1,128 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe_memutils.c
+ * memory management support for frontend code
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/common/fe_memutils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#error "This file is not expected to be compiled for backend code"
+#endif
+
+#include "postgres_fe.h"
+
+void *
+pg_malloc(size_t size)
+{
+ void *tmp;
+
+ /* Avoid unportable behavior of malloc(0) */
+ if (size == 0)
+ size = 1;
+ tmp = malloc(size);
+ if (!tmp)
+ {
+ fprintf(stderr, _("out of memory\n"));
+ exit(EXIT_FAILURE);
+ }
+ return tmp;
+}
+
+void *
+pg_malloc0(size_t size)
+{
+ void *tmp;
+
+ tmp = pg_malloc(size);
+ MemSet(tmp, 0, size);
+ return tmp;
+}
+
+void *
+pg_realloc(void *ptr, size_t size)
+{
+ void *tmp;
+
+ /* Avoid unportable behavior of realloc(NULL, 0) */
+ if (ptr == NULL && size == 0)
+ size = 1;
+ tmp = realloc(ptr, size);
+ if (!tmp)
+ {
+ fprintf(stderr, _("out of memory\n"));
+ exit(EXIT_FAILURE);
+ }
+ return tmp;
+}
+
+/*
+ * "Safe" wrapper around strdup().
+ */
+char *
+pg_strdup(const char *in)
+{
+ char *tmp;
+
+ if (!in)
+ {
+ fprintf(stderr,
+ _("cannot duplicate null pointer (internal error)\n"));
+ exit(EXIT_FAILURE);
+ }
+ tmp = strdup(in);
+ if (!tmp)
+ {
+ fprintf(stderr, _("out of memory\n"));
+ exit(EXIT_FAILURE);
+ }
+ return tmp;
+}
+
+void
+pg_free(void *ptr)
+{
+ if (ptr != NULL)
+ free(ptr);
+}
+
+/*
+ * Frontend emulation of backend memory management functions. Useful for
+ * programs that compile backend files.
+ */
+void *
+palloc(Size size)
+{
+ return pg_malloc(size);
+}
+
+void *
+palloc0(Size size)
+{
+ return pg_malloc0(size);
+}
+
+void
+pfree(void *pointer)
+{
+ pg_free(pointer);
+}
+
+char *
+pstrdup(const char *in)
+{
+ return pg_strdup(in);
+}
+
+void *
+repalloc(void *pointer, Size size)
+{
+ return pg_realloc(pointer, size);
+}
diff --git a/src/common/pgfnames.c b/src/common/pgfnames.c
new file mode 100644
index 0000000000..016da32514
--- /dev/null
+++ b/src/common/pgfnames.c
@@ -0,0 +1,107 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgfnames.c
+ * directory handling functions
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/common/pgfnames.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <dirent.h>
+
+/*
+ * pgfnames
+ *
+ * return a list of the names of objects in the argument directory. Caller
+ * must call pgfnames_cleanup later to free the memory allocated by this
+ * function.
+ */
+char **
+pgfnames(const char *path)
+{
+ DIR *dir;
+ struct dirent *file;
+ char **filenames;
+ int numnames = 0;
+ int fnsize = 200; /* enough for many small dbs */
+
+ dir = opendir(path);
+ if (dir == NULL)
+ {
+#ifndef FRONTEND
+ elog(WARNING, "could not open directory \"%s\": %m", path);
+#else
+ fprintf(stderr, _("could not open directory \"%s\": %s\n"),
+ path, strerror(errno));
+#endif
+ return NULL;
+ }
+
+ filenames = (char **) palloc(fnsize * sizeof(char *));
+
+ while (errno = 0, (file = readdir(dir)) != NULL)
+ {
+ if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
+ {
+ if (numnames + 1 >= fnsize)
+ {
+ fnsize *= 2;
+ filenames = (char **) repalloc(filenames,
+ fnsize * sizeof(char *));
+ }
+ filenames[numnames++] = pstrdup(file->d_name);
+ }
+ }
+
+ if (errno)
+ {
+#ifndef FRONTEND
+ elog(WARNING, "could not read directory \"%s\": %m", path);
+#else
+ fprintf(stderr, _("could not read directory \"%s\": %s\n"),
+ path, strerror(errno));
+#endif
+ }
+
+ filenames[numnames] = NULL;
+
+ if (closedir(dir))
+ {
+#ifndef FRONTEND
+ elog(WARNING, "could not close directory \"%s\": %m", path);
+#else
+ fprintf(stderr, _("could not close directory \"%s\": %s\n"),
+ path, strerror(errno));
+#endif
+ }
+
+ return filenames;
+}
+
+
+/*
+ * pgfnames_cleanup
+ *
+ * deallocate memory used for filenames
+ */
+void
+pgfnames_cleanup(char **filenames)
+{
+ char **fn;
+
+ for (fn = filenames; *fn; fn++)
+ pfree(*fn);
+
+ pfree(filenames);
+}
diff --git a/src/common/psprintf.c b/src/common/psprintf.c
new file mode 100644
index 0000000000..e1320a6db0
--- /dev/null
+++ b/src/common/psprintf.c
@@ -0,0 +1,190 @@
+/*-------------------------------------------------------------------------
+ *
+ * psprintf.c
+ * sprintf into an allocated-on-demand buffer
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/common/psprintf.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+
+#include "postgres.h"
+
+#include "utils/memutils.h"
+
+#else
+
+#include "postgres_fe.h"
+
+/* It's possible we could use a different value for this in frontend code */
+#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
+
+#endif
+
+
+/*
+ * psprintf
+ *
+ * Format text data under the control of fmt (an sprintf-style format string)
+ * and return it in an allocated-on-demand buffer. The buffer is allocated
+ * with palloc in the backend, or malloc in frontend builds. Caller is
+ * responsible to free the buffer when no longer needed, if appropriate.
+ *
+ * Errors are not returned to the caller, but are reported via elog(ERROR)
+ * in the backend, or printf-to-stderr-and-exit() in frontend builds.
+ * One should therefore think twice about using this in libpq.
+ */
+char *
+psprintf(const char *fmt,...)
+{
+ size_t len = 128; /* initial assumption about buffer size */
+
+ for (;;)
+ {
+ char *result;
+ va_list args;
+ size_t newlen;
+
+ /*
+ * Allocate result buffer. Note that in frontend this maps to malloc
+ * with exit-on-error.
+ */
+ result = (char *) palloc(len);
+
+ /* Try to format the data. */
+ va_start(args, fmt);
+ newlen = pvsnprintf(result, len, fmt, args);
+ va_end(args);
+
+ if (newlen < len)
+ return result; /* success */
+
+ /* Release buffer and loop around to try again with larger len. */
+ pfree(result);
+ len = newlen;
+ }
+}
+
+/*
+ * pvsnprintf
+ *
+ * Attempt to format text data under the control of fmt (an sprintf-style
+ * format string) and insert it into buf (which has length len, len > 0).
+ *
+ * If successful, return the number of bytes emitted, not counting the
+ * trailing zero byte. This will always be strictly less than len.
+ *
+ * If there's not enough space in buf, return an estimate of the buffer size
+ * needed to succeed (this *must* be more than the given len, else callers
+ * might loop infinitely).
+ *
+ * Other error cases do not return, but exit via elog(ERROR) or exit().
+ * Hence, this shouldn't be used inside libpq.
+ *
+ * This function exists mainly to centralize our workarounds for
+ * non-C99-compliant vsnprintf implementations. Generally, any call that
+ * pays any attention to the return value should go through here rather
+ * than calling snprintf or vsnprintf directly.
+ *
+ * Note that the semantics of the return value are not exactly C99's.
+ * First, we don't promise that the estimated buffer size is exactly right;
+ * callers must be prepared to loop multiple times to get the right size.
+ * Second, we return the recommended buffer size, not one less than that;
+ * this lets overflow concerns be handled here rather than in the callers.
+ */
+size_t
+pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
+{
+ int nprinted;
+
+ Assert(len > 0);
+
+ errno = 0;
+
+ /*
+ * Assert check here is to catch buggy vsnprintf that overruns the
+ * specified buffer length. Solaris 7 in 64-bit mode is an example of a
+ * platform with such a bug.
+ */
+#ifdef USE_ASSERT_CHECKING
+ buf[len - 1] = '\0';
+#endif
+
+ nprinted = vsnprintf(buf, len, fmt, args);
+
+ Assert(buf[len - 1] == '\0');
+
+ /*
+ * If vsnprintf reports an error other than ENOMEM, fail. The possible
+ * causes of this are not user-facing errors, so elog should be enough.
+ */
+ if (nprinted < 0 && errno != 0 && errno != ENOMEM)
+ {
+#ifndef FRONTEND
+ elog(ERROR, "vsnprintf failed: %m");
+#else
+ fprintf(stderr, "vsnprintf failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+#endif
+ }
+
+ /*
+ * Note: some versions of vsnprintf return the number of chars actually
+ * stored, not the total space needed as C99 specifies. And at least one
+ * returns -1 on failure. Be conservative about believing whether the
+ * print worked.
+ */
+ if (nprinted >= 0 && (size_t) nprinted < len - 1)
+ {
+ /* Success. Note nprinted does not include trailing null. */
+ return (size_t) nprinted;
+ }
+
+ if (nprinted >= 0 && (size_t) nprinted > len)
+ {
+ /*
+ * This appears to be a C99-compliant vsnprintf, so believe its
+ * estimate of the required space. (If it's wrong, the logic will
+ * still work, but we may loop multiple times.) Note that the space
+ * needed should be only nprinted+1 bytes, but we'd better allocate
+ * one more than that so that the test above will succeed next time.
+ *
+ * In the corner case where the required space just barely overflows,
+ * fall through so that we'll error out below (possibly after
+ * looping).
+ */
+ if ((size_t) nprinted <= MaxAllocSize - 2)
+ return nprinted + 2;
+ }
+
+ /*
+ * Buffer overrun, and we don't know how much space is needed. Estimate
+ * twice the previous buffer size, but not more than MaxAllocSize; if we
+ * are already at MaxAllocSize, choke. Note we use this palloc-oriented
+ * overflow limit even when in frontend.
+ */
+ if (len >= MaxAllocSize)
+ {
+#ifndef FRONTEND
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("out of memory")));
+#else
+ fprintf(stderr, _("out of memory\n"));
+ exit(EXIT_FAILURE);
+#endif
+ }
+
+ if (len >= MaxAllocSize / 2)
+ return MaxAllocSize;
+
+ return len * 2;
+}
diff --git a/src/common/relpath.c b/src/common/relpath.c
new file mode 100644
index 0000000000..e4c64f685d
--- /dev/null
+++ b/src/common/relpath.c
@@ -0,0 +1,251 @@
+/*-------------------------------------------------------------------------
+ * relpath.c
+ * Shared frontend/backend code to compute pathnames of relation files
+ *
+ * This module also contains some logic associated with fork names.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/common/relpath.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "catalog/catalog.h"
+#include "catalog/pg_tablespace.h"
+#include "common/relpath.h"
+#include "storage/backendid.h"
+
+#ifdef PGXC
+#include "pgxc/pgxc.h"
+#endif
+
+/*
+ * Lookup table of fork name by fork number.
+ *
+ * If you add a new entry, remember to update the errhint in
+ * forkname_to_number() below, and update the SGML documentation for
+ * pg_relation_size().
+ */
+const char *const forkNames[] = {
+ "main", /* MAIN_FORKNUM */
+ "fsm", /* FSM_FORKNUM */
+ "vm", /* VISIBILITYMAP_FORKNUM */
+ "init" /* INIT_FORKNUM */
+};
+
+/*
+ * forkname_to_number - look up fork number by name
+ *
+ * In backend, we throw an error for no match; in frontend, we just
+ * return InvalidForkNumber.
+ */
+ForkNumber
+forkname_to_number(const char *forkName)
+{
+ ForkNumber forkNum;
+
+ for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
+ if (strcmp(forkNames[forkNum], forkName) == 0)
+ return forkNum;
+
+#ifndef FRONTEND
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid fork name"),
+ errhint("Valid fork names are \"main\", \"fsm\", "
+ "\"vm\", and \"init\".")));
+#endif
+
+ return InvalidForkNumber;
+}
+
+/*
+ * forkname_chars
+ * We use this to figure out whether a filename could be a relation
+ * fork (as opposed to an oddly named stray file that somehow ended
+ * up in the database directory). If the passed string begins with
+ * a fork name (other than the main fork name), we return its length,
+ * and set *fork (if not NULL) to the fork number. If not, we return 0.
+ *
+ * Note that the present coding assumes that there are no fork names which
+ * are prefixes of other fork names.
+ */
+int
+forkname_chars(const char *str, ForkNumber *fork)
+{
+ ForkNumber forkNum;
+
+ for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
+ {
+ int len = strlen(forkNames[forkNum]);
+
+ if (strncmp(forkNames[forkNum], str, len) == 0)
+ {
+ if (fork)
+ *fork = forkNum;
+ return len;
+ }
+ }
+ if (fork)
+ *fork = InvalidForkNumber;
+ return 0;
+}
+
+
+/*
+ * GetDatabasePath - construct path to a database directory
+ *
+ * Result is a palloc'd string.
+ *
+ * XXX this must agree with GetRelationPath()!
+ */
+char *
+GetDatabasePath(Oid dbNode, Oid spcNode)
+{
+ if (spcNode == GLOBALTABLESPACE_OID)
+ {
+ /* Shared system relations live in {datadir}/global */
+ Assert(dbNode == 0);
+ return pstrdup("global");
+ }
+ else if (spcNode == DEFAULTTABLESPACE_OID)
+ {
+ /* The default tablespace is {datadir}/base */
+ return psprintf("base/%u", dbNode);
+ }
+ else
+ {
+ /* All other tablespaces are accessed via symlinks */
+#ifdef PGXC
+ return psprintf("pg_tblspc/%u/%s_%s/%u",
+ spcNode, TABLESPACE_VERSION_DIRECTORY,
+ PGXCNodeName,
+ dbNode);
+#else
+ return psprintf("pg_tblspc/%u/%s/%u",
+ spcNode, TABLESPACE_VERSION_DIRECTORY, dbNode);
+#endif
+ }
+}
+
+/*
+ * GetRelationPath - construct path to a relation's file
+ *
+ * Result is a palloc'd string.
+ *
+ * Note: ideally, backendId would be declared as type BackendId, but relpath.h
+ * would have to include a backend-only header to do that; doesn't seem worth
+ * the trouble considering BackendId is just int anyway.
+ */
+char *
+GetRelationPath(Oid dbNode, Oid spcNode, Oid relNode,
+ int backendId, ForkNumber forkNumber)
+{
+ char *path;
+
+ if (spcNode == GLOBALTABLESPACE_OID)
+ {
+ /* Shared system relations live in {datadir}/global */
+ Assert(dbNode == 0);
+ Assert(backendId == InvalidBackendId);
+ if (forkNumber != MAIN_FORKNUM)
+ path = psprintf("global/%u_%s",
+ relNode, forkNames[forkNumber]);
+ else
+ path = psprintf("global/%u", relNode);
+ }
+ else if (spcNode == DEFAULTTABLESPACE_OID)
+ {
+ /* The default tablespace is {datadir}/base */
+ if (backendId == InvalidBackendId)
+ {
+ if (forkNumber != MAIN_FORKNUM)
+ path = psprintf("base/%u/%u_%s",
+ dbNode, relNode,
+ forkNames[forkNumber]);
+ else
+ path = psprintf("base/%u/%u",
+ dbNode, relNode);
+ }
+ else
+ {
+ if (forkNumber != MAIN_FORKNUM)
+ path = psprintf("base/%u/t%d_%u_%s",
+ dbNode, backendId, relNode,
+ forkNames[forkNumber]);
+ else
+ path = psprintf("base/%u/t%d_%u",
+ dbNode, backendId, relNode);
+ }
+ }
+ else
+ {
+ /* All other tablespaces are accessed via symlinks */
+ if (backendId == InvalidBackendId)
+ {
+ if (forkNumber != MAIN_FORKNUM)
+#ifdef PGXC
+ path = psprintf("pg_tblspc/%u/%s_%s/%u/%u_%s",
+#else
+ path = psprintf("pg_tblspc/%u/%s/%u/%u_%s",
+#endif
+ spcNode, TABLESPACE_VERSION_DIRECTORY,
+#ifdef PGXC
+ /* Postgres-XC tablespaces include node name */
+ PGXCNodeName,
+#endif
+ dbNode,
+ relNode,
+ forkNames[forkNumber]);
+ else
+#ifdef PGXC
+ path = psprintf("pg_tblspc/%u/%s_%s/%u/%u",
+#else
+ path = psprintf("pg_tblspc/%u/%s/%u/%u",
+#endif
+ spcNode, TABLESPACE_VERSION_DIRECTORY,
+#ifdef PGXC
+ /* Postgres-XC tablespaces include node name */
+ PGXCNodeName,
+#endif
+ dbNode, relNode);
+ }
+ else
+ {
+ if (forkNumber != MAIN_FORKNUM)
+#ifdef PGXC
+ path = psprintf("pg_tblspc/%u/%s_%s/%u/t%d_%u_%s",
+#else
+ path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u_%s",
+#endif
+ spcNode, TABLESPACE_VERSION_DIRECTORY,
+#ifdef PGXC
+ /* Postgres-XC tablespaces include node name */
+ PGXCNodeName,
+#endif
+ dbNode, backendId, relNode,
+ forkNames[forkNumber]);
+ else
+#ifdef PGXC
+ path = psprintf("pg_tblspc/%u/%s_%s/%u/t%d_%u",
+#else
+ path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u",
+#endif
+ spcNode, TABLESPACE_VERSION_DIRECTORY,
+#ifdef PGXC
+ /* Postgres-XC tablespaces include node name */
+ PGXCNodeName,
+#endif
+ dbNode, backendId, relNode);
+ }
+ }
+ return path;
+}
diff --git a/src/common/rmtree.c b/src/common/rmtree.c
new file mode 100644
index 0000000000..fa43c20871
--- /dev/null
+++ b/src/common/rmtree.c
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------------------------
+ *
+ * rmtree.c
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/common/rmtree.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+/*
+ * rmtree
+ *
+ * Delete a directory tree recursively.
+ * Assumes path points to a valid directory.
+ * Deletes everything under path.
+ * If rmtopdir is true deletes the directory too.
+ * Returns true if successful, false if there was any problem.
+ * (The details of the problem are reported already, so caller
+ * doesn't really have to say anything more, but most do.)
+ */
+bool
+rmtree(const char *path, bool rmtopdir)
+{
+ bool result = true;
+ char pathbuf[MAXPGPATH];
+ char **filenames;
+ char **filename;
+ struct stat statbuf;
+
+ /*
+ * we copy all the names out of the directory before we start modifying
+ * it.
+ */
+ filenames = pgfnames(path);
+
+ if (filenames == NULL)
+ return false;
+
+ /* now we have the names we can start removing things */
+ for (filename = filenames; *filename; filename++)
+ {
+ snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename);
+
+ /*
+ * It's ok if the file is not there anymore; we were just about to
+ * delete it anyway.
+ *
+ * This is not an academic possibility. One scenario where this
+ * happens is when bgwriter has a pending unlink request for a file in
+ * a database that's being dropped. In dropdb(), we call
+ * ForgetDatabaseFsyncRequests() to flush out any such pending unlink
+ * requests, but because that's asynchronous, it's not guaranteed that
+ * the bgwriter receives the message in time.
+ */
+ if (lstat(pathbuf, &statbuf) != 0)
+ {
+ if (errno != ENOENT)
+ {
+#ifndef FRONTEND
+ elog(WARNING, "could not stat file or directory \"%s\": %m",
+ pathbuf);
+#else
+ fprintf(stderr, _("could not stat file or directory \"%s\": %s\n"),
+ pathbuf, strerror(errno));
+#endif
+ result = false;
+ }
+ continue;
+ }
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ /* call ourselves recursively for a directory */
+ if (!rmtree(pathbuf, true))
+ {
+ /* we already reported the error */
+ result = false;
+ }
+ }
+ else
+ {
+ if (unlink(pathbuf) != 0)
+ {
+ if (errno != ENOENT)
+ {
+#ifndef FRONTEND
+ elog(WARNING, "could not remove file or directory \"%s\": %m",
+ pathbuf);
+#else
+ fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
+ pathbuf, strerror(errno));
+#endif
+ result = false;
+ }
+ }
+ }
+ }
+
+ if (rmtopdir)
+ {
+ if (rmdir(path) != 0)
+ {
+#ifndef FRONTEND
+ elog(WARNING, "could not remove file or directory \"%s\": %m",
+ path);
+#else
+ fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
+ path, strerror(errno));
+#endif
+ result = false;
+ }
+ }
+
+ pgfnames_cleanup(filenames);
+
+ return result;
+}
diff --git a/src/common/username.c b/src/common/username.c
new file mode 100644
index 0000000000..24c5b47627
--- /dev/null
+++ b/src/common/username.c
@@ -0,0 +1,87 @@
+/*-------------------------------------------------------------------------
+ *
+ * username.c
+ * get user name
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/common/username.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "common/username.h"
+
+/*
+ * Returns the current user name in a static buffer, or NULL on error and
+ * sets errstr
+ */
+const char *
+get_user_name(char **errstr)
+{
+#ifndef WIN32
+ struct passwd *pw;
+ uid_t user_id = geteuid();
+
+ *errstr = NULL;
+
+ errno = 0; /* clear errno before call */
+ pw = getpwuid(user_id);
+ if (!pw)
+ {
+ *errstr = psprintf(_("failed to look up effective user id %ld: %s"),
+ (long) user_id,
+ errno ? strerror(errno) : _("user does not exist"));
+ return NULL;
+ }
+
+ return pw->pw_name;
+#else
+ /* UNLEN = 256, 'static' variable remains after function exit */
+ static char username[256 + 1];
+ DWORD len = sizeof(username) - 1;
+
+ *errstr = NULL;
+
+ if (!GetUserName(username, &len))
+ {
+ *errstr = psprintf(_("user name lookup failure: %s"), strerror(errno));
+ return NULL;
+ }
+
+ return username;
+#endif
+}
+
+
+/*
+ * Returns the current user name in a static buffer or exits
+ */
+const char *
+get_user_name_or_exit(const char *progname)
+{
+ const char *user_name;
+ char *errstr;
+
+ user_name = get_user_name(&errstr);
+
+ if (!user_name)
+ {
+ fprintf(stderr, "%s: %s\n", progname, errstr);
+ exit(1);
+ }
+ return user_name;
+}
diff --git a/src/common/wait_error.c b/src/common/wait_error.c
new file mode 100644
index 0000000000..082bb8eb93
--- /dev/null
+++ b/src/common/wait_error.c
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * wait_error.c
+ * Convert a wait/waitpid(2) result code to a human-readable string
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/common/wait_error.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/wait.h>
+
+/*
+ * Return a human-readable string explaining the reason a child process
+ * terminated. The argument is a return code returned by wait(2) or
+ * waitpid(2). The result is a translated, palloc'd or malloc'd string.
+ */
+char *
+wait_result_to_str(int exitstatus)
+{
+ char str[512];
+
+ if (WIFEXITED(exitstatus))
+ {
+ /*
+ * Give more specific error message for some common exit codes that
+ * have a special meaning in shells.
+ */
+ switch (WEXITSTATUS(exitstatus))
+ {
+ case 126:
+ snprintf(str, sizeof(str), _("command not executable"));
+ break;
+
+ case 127:
+ snprintf(str, sizeof(str), _("command not found"));
+ break;
+
+ default:
+ snprintf(str, sizeof(str),
+ _("child process exited with exit code %d"),
+ WEXITSTATUS(exitstatus));
+ }
+ }
+ else if (WIFSIGNALED(exitstatus))
+#if defined(WIN32)
+ snprintf(str, sizeof(str),
+ _("child process was terminated by exception 0x%X"),
+ WTERMSIG(exitstatus));
+#elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
+ {
+ char str2[256];
+
+ snprintf(str2, sizeof(str2), "%d: %s", WTERMSIG(exitstatus),
+ WTERMSIG(exitstatus) < NSIG ?
+ sys_siglist[WTERMSIG(exitstatus)] : "(unknown)");
+ snprintf(str, sizeof(str),
+ _("child process was terminated by signal %s"), str2);
+ }
+#else
+ snprintf(str, sizeof(str),
+ _("child process was terminated by signal %d"),
+ WTERMSIG(exitstatus));
+#endif
+ else
+ snprintf(str, sizeof(str),
+ _("child process exited with unrecognized status %d"),
+ exitstatus);
+
+ return pstrdup(str);
+}