Here's a patch to move src/port/pgcheckdir.c to src/common/checkdir.c and add the shareable part of postmaster's current checkDataDir code into it, as pg_check_dir_permissions. This is in the spirit of using it in pgstat to check the directory defined by stats_temp_directory.
(This is basically the same patch I posted earlier, except the file is now in src/common instead of src/port.) Barring objections I intend to push this to 9.3 and HEAD shortly. -- Álvaro Herrera http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/postmaster/postmaster.c --- b/src/backend/postmaster/postmaster.c *************** *** 95,100 **** --- 95,101 ---- #include "access/xlog.h" #include "bootstrap/bootstrap.h" #include "catalog/pg_control.h" + #include "common/checkdir.h" #include "lib/ilist.h" #include "libpq/auth.h" #include "libpq/ip.h" *************** *** 1313,1385 **** getInstallationPaths(const char *argv0) static void checkDataDir(void) { char path[MAXPGPATH]; FILE *fp; - struct stat stat_buf; Assert(DataDir); ! if (stat(DataDir, &stat_buf) != 0) { ! if (errno == ENOENT) ereport(FATAL, (errcode_for_file_access(), errmsg("data directory \"%s\" does not exist", DataDir))); ! else ereport(FATAL, (errcode_for_file_access(), ! errmsg("could not read permissions of directory \"%s\": %m", ! DataDir))); } - /* eventual chdir would fail anyway, but let's test ... */ - if (!S_ISDIR(stat_buf.st_mode)) - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("specified data directory \"%s\" is not a directory", - DataDir))); - - /* - * Check that the directory belongs to my userid; if not, reject. - * - * This check is an essential part of the interlock that prevents two - * postmasters from starting in the same directory (see CreateLockFile()). - * Do not remove or weaken it. - * - * XXX can we safely enable this check on Windows? - */ - #if !defined(WIN32) && !defined(__CYGWIN__) - if (stat_buf.st_uid != geteuid()) - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("data directory \"%s\" has wrong ownership", - DataDir), - errhint("The server must be started by the user that owns the data directory."))); - #endif - - /* - * Check if the directory has group or world access. If so, reject. - * - * It would be possible to allow weaker constraints (for example, allow - * group access) but we cannot make a general assumption that that is - * okay; for example there are platforms where nearly all users - * customarily belong to the same group. Perhaps this test should be - * configurable. - * - * XXX temporarily suppress check when on Windows, because there may not - * be proper support for Unix-y file permissions. Need to think of a - * reasonable check to apply on Windows. - */ - #if !defined(WIN32) && !defined(__CYGWIN__) - if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("data directory \"%s\" has group or world access", - DataDir), - errdetail("Permissions should be u=rwx (0700)."))); - #endif - /* Look for PG_VERSION before looking for pg_control */ ValidatePgVersion(DataDir); --- 1314,1364 ---- static void checkDataDir(void) { + CheckDirErrcode err; char path[MAXPGPATH]; FILE *fp; Assert(DataDir); ! err = pg_check_dir_permissions(DataDir); ! switch (err) { ! case CKDIR_OK: ! break; ! case CKDIR_NOT_EXISTS: ereport(FATAL, (errcode_for_file_access(), errmsg("data directory \"%s\" does not exist", DataDir))); ! break; ! case CKDIR_CANT_READ_PERMS: ereport(FATAL, (errcode_for_file_access(), ! errmsg("could not read permissions of data directory \"%s\": %m", ! DataDir))); ! break; ! case CKDIR_NOT_DIR: ! ereport(FATAL, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("specified data directory \"%s\" is not a directory", ! DataDir))); ! break; ! case CKDIR_WRONG_OWNER: ! ereport(FATAL, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("data directory \"%s\" has wrong ownership", ! DataDir), ! errhint("The server must be started by the user that owns the data directory."))); ! break; ! case CKDIR_TOO_ACCESSIBLE: ! ereport(FATAL, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("data directory \"%s\" has group or world access", ! DataDir), ! errdetail("Permissions should be u=rwx (0700)."))); ! break; } /* Look for PG_VERSION before looking for pg_control */ ValidatePgVersion(DataDir); *** a/src/bin/initdb/initdb.c --- b/src/bin/initdb/initdb.c *************** *** 56,61 **** --- 56,62 ---- #include <signal.h> #include <time.h> + #include "common/checkdir.h" #include "mb/pg_wchar.h" #include "getaddrinfo.h" #include "getopt_long.h" *** a/src/bin/pg_basebackup/pg_basebackup.c --- b/src/bin/pg_basebackup/pg_basebackup.c *************** *** 10,20 **** * src/bin/pg_basebackup/pg_basebackup.c *------------------------------------------------------------------------- */ - #include "postgres_fe.h" - #include "libpq-fe.h" - #include "pqexpbuffer.h" - #include "pgtar.h" #include <unistd.h> #include <dirent.h> --- 10,16 ---- *************** *** 27,34 **** #include <zlib.h> #endif #include "getopt_long.h" ! #include "receivelog.h" #include "streamutil.h" --- 23,33 ---- #include <zlib.h> #endif + #include "common/checkdir.h" #include "getopt_long.h" ! #include "libpq-fe.h" ! #include "pgtar.h" ! #include "pqexpbuffer.h" #include "receivelog.h" #include "streamutil.h" *** a/src/common/Makefile --- b/src/common/Makefile *************** *** 23,29 **** include $(top_builddir)/src/Makefile.global override CPPFLAGS := -DFRONTEND $(CPPFLAGS) LIBS += $(PTHREAD_LIBS) ! OBJS_COMMON = relpath.o OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o --- 23,29 ---- override CPPFLAGS := -DFRONTEND $(CPPFLAGS) LIBS += $(PTHREAD_LIBS) ! OBJS_COMMON = relpath.o checkdir.o OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o *** /dev/null --- b/src/common/checkdir.c *************** *** 0 **** --- 1,148 ---- + /*------------------------------------------------------------------------- + * + * src/common/checkdir.c + * Subroutines to deal with directory-related checks. Useful in both + * initdb and the backend. + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + *------------------------------------------------------------------------- + */ + + #include "c.h" + + #include <dirent.h> + #include <sys/types.h> + #include <sys/stat.h> + #include <unistd.h> + + #include "common/checkdir.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 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; + + errno = 0; + + chkdir = opendir(dir); + + if (chkdir == NULL) + return (errno == ENOENT) ? 0 : -1; + + while ((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; + } + else if (strcmp("lost+found", file->d_name) == 0) + { + result = 3; /* not empty, mount point */ + break; + } + #endif + else + { + result = 4; /* not empty */ + break; + } + } + + #ifdef WIN32 + + /* + * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in + * released version + */ + if (GetLastError() == ERROR_NO_MORE_FILES) + errno = 0; + #endif + + closedir(chkdir); + + if (errno != 0) + result = -1; /* some kind of I/O error? */ + + /* We report on dot-files if we _only_ find dot files */ + if (result == 1 && dot_found) + result = 2; + + return result; + } + + /* + * Verify permissions of a directory + */ + CheckDirErrcode + pg_check_dir_permissions(const char *directory) + { + struct stat stat_buf; + + if (stat(directory, &stat_buf) != 0) + { + if (errno == ENOENT) + return CKDIR_NOT_EXISTS; + else + return CKDIR_CANT_READ_PERMS; + } + + if (!S_ISDIR(stat_buf.st_mode)) + return CKDIR_NOT_DIR; + + /* + * Check that the directory belongs to my userid; if not, reject. + * + * This check is an essential part of the interlock that prevents two + * postmasters from starting in the same directory (see CreateLockFile()). + * Do not remove or weaken it. + * + * XXX can we safely enable this check on Windows? + */ + #if !defined(WIN32) && !defined(__CYGWIN__) + if (stat_buf.st_uid != geteuid()) + return CKDIR_WRONG_OWNER; + #endif + + /* + * Check if the directory has group or world access. If so, reject. + * + * It would be possible to allow weaker constraints (for example, allow + * group access) but we cannot make a general assumption that that is + * okay; for example there are platforms where nearly all users + * customarily belong to the same group. Perhaps this test should be + * configurable. + * + * XXX temporarily suppress check when on Windows, because there may not + * be proper support for Unix-y file permissions. Need to think of a + * reasonable check to apply on Windows. + */ + #if !defined(WIN32) && !defined(__CYGWIN__) + if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) + return CKDIR_TOO_ACCESSIBLE; + #endif + + return CKDIR_OK; + } *** a/src/include/port.h --- b/src/include/port.h *************** *** 460,468 **** extern int pg_codepage_to_encoding(UINT cp); extern char *inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size); - /* port/pgcheckdir.c */ - extern int pg_check_dir(const char *dir); - /* port/pgmkdirp.c */ extern int pg_mkdir_p(char *path, int omode); --- 460,465 ---- *** a/src/port/Makefile --- b/src/port/Makefile *************** *** 31,37 **** override CPPFLAGS := -I$(top_builddir)/src/port -DFRONTEND $(CPPFLAGS) LIBS += $(PTHREAD_LIBS) OBJS = $(LIBOBJS) chklocale.o dirmod.o erand48.o exec.o fls.o inet_net_ntop.o \ ! noblock.o path.o pgcheckdir.o pg_crc.o pgmkdirp.o pgsleep.o \ pgstrcasecmp.o pqsignal.o \ qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o \ wait_error.o --- 31,37 ---- LIBS += $(PTHREAD_LIBS) OBJS = $(LIBOBJS) chklocale.o dirmod.o erand48.o exec.o fls.o inet_net_ntop.o \ ! noblock.o path.o pg_crc.o pgmkdirp.o pgsleep.o \ pgstrcasecmp.o pqsignal.o \ qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o \ wait_error.o *** a/src/port/pgcheckdir.c --- /dev/null *************** *** 1,90 **** - /*------------------------------------------------------------------------- - * - * src/port/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-2013, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - *------------------------------------------------------------------------- - */ - - #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 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; - - errno = 0; - - chkdir = opendir(dir); - - if (chkdir == NULL) - return (errno == ENOENT) ? 0 : -1; - - while ((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; - } - else if (strcmp("lost+found", file->d_name) == 0) - { - result = 3; /* not empty, mount point */ - break; - } - #endif - else - { - result = 4; /* not empty */ - break; - } - } - - #ifdef WIN32 - - /* - * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in - * released version - */ - if (GetLastError() == ERROR_NO_MORE_FILES) - errno = 0; - #endif - - closedir(chkdir); - - if (errno != 0) - result = -1; /* some kind of I/O error? */ - - /* We report on dot-files if we _only_ find dot files */ - if (result == 1 && dot_found) - result = 2; - - return result; - } --- 0 ----
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers