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

Reply via email to