diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 19033ed54a555d070b55685b55ea48216570b774..040cbdfab01f386fee89d5f755d098551b4ac898 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -40,7 +40,6 @@ * * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California - * Portions taken from FreeBSD. * * src/bin/initdb/initdb.c * @@ -152,11 +151,9 @@ static char **filter_lines_with_token(char **lines, const char *token); static char **readfile(const char *path); static void writefile(char *path, char **lines); static FILE *popen_check(const char *command, const char *mode); -static int mkdir_p(char *path, mode_t omode); static void exit_nicely(void); static char *get_id(void); static char *get_encoding_id(char *encoding_name); -static int check_data_dir(char *dir); static bool mkdatadir(const char *subdir); static void set_input(char **dest, char *filename); static void check_input(char *path); @@ -470,110 +467,6 @@ popen_check(const char *command, const char *mode) return cmdfd; } -/* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */ - -/* - * this tries to build all the elements of a path to a directory a la mkdir -p - * we assume the path is in canonical form, i.e. uses / as the separator - * we also assume it isn't null. - * - * note that on failure, the path arg has been modified to show the particular - * directory level we had problems with. - */ -static int -mkdir_p(char *path, mode_t omode) -{ - struct stat sb; - mode_t numask, - oumask; - int first, - last, - retval; - char *p; - - p = path; - oumask = 0; - retval = 0; - -#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) - return 1; - } - else if (p[1] == ':' && - ((p[0] >= 'a' && p[0] <= 'z') || - (p[0] >= 'A' && p[0] <= 'Z'))) - { - /* local drive */ - p += 2; - } - } -#endif - - if (p[0] == '/') /* Skip leading '/'. */ - ++p; - for (first = 1, 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 (first) - { - /* - * POSIX 1003.2: For each dir operand that does not name an - * existing directory, effects equivalent to those caused by the - * following command shall occcur: - * - * 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. - */ - oumask = umask(0); - numask = oumask & ~(S_IWUSR | S_IXUSR); - (void) umask(numask); - first = 0; - } - if (last) - (void) umask(oumask); - - /* check for pre-existing directory; ok if it's a parent */ - 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 = '/'; - } - if (!first && !last) - (void) umask(oumask); - return retval; -} - /* * clean up any files we created on failure * if we created the data directory remove it too @@ -801,59 +694,6 @@ find_matching_ts_config(const char *lc_type) } -/* - * make sure the directory either doesn't exist or is empty - * - * Returns 0 if nonexistent, 1 if exists and empty, 2 if not empty, - * or -1 if trouble accessing directory - */ -static int -check_data_dir(char *dir) -{ - DIR *chkdir; - struct dirent *file; - int result = 1; - - errno = 0; - - chkdir = opendir(dir); - - if (!chkdir) - 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; - } - else - { - result = 2; /* 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? */ - - return result; -} - /* * make the data directory (or one of its subdirectories if subdir is not NULL) */ @@ -870,7 +710,7 @@ mkdatadir(const char *subdir) else strcpy(path, pg_data); - if (mkdir_p(path, S_IRWXU) == 0) + if (pg_mkdir_p(path, S_IRWXU) == 0) return true; fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"), @@ -2929,7 +2769,7 @@ main(int argc, char *argv[]) pqsignal(SIGPIPE, SIG_IGN); #endif - switch (check_data_dir(pg_data)) + switch (pg_check_dir(pg_data)) { case 0: /* PGDATA not there, must create it */ @@ -2995,8 +2835,8 @@ main(int argc, char *argv[]) exit_nicely(); } - /* check if the specified xlog directory is empty */ - switch (check_data_dir(xlog_dir)) + /* check if the specified xlog directory exists/is empty */ + switch (pg_check_dir(xlog_dir)) { case 0: /* xlog directory not there, must create it */ @@ -3004,7 +2844,7 @@ main(int argc, char *argv[]) xlog_dir); fflush(stdout); - if (mkdir_p(xlog_dir, S_IRWXU) != 0) + if (pg_mkdir_p(xlog_dir, S_IRWXU) != 0) { fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"), progname, xlog_dir, strerror(errno)); @@ -3015,6 +2855,7 @@ main(int argc, char *argv[]) made_new_xlogdir = true; break; + case 1: /* Present but empty, fix permissions and use it */ printf(_("fixing permissions on existing directory %s ... "), @@ -3032,6 +2873,7 @@ main(int argc, char *argv[]) found_existing_xlogdir = true; break; + case 2: /* Present and not empty */ fprintf(stderr, diff --git a/src/include/port.h b/src/include/port.h index 0cfbed83d45ef0f17cf00750ea8e36d9db26b566..0dbc322392acce85e7c90affb0fc6370a25e6859 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -441,4 +441,10 @@ extern int pg_get_encoding_from_locale(const char *ctype); 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); + #endif /* PG_PORT_H */ diff --git a/src/port/Makefile b/src/port/Makefile index 711f63308925e9f036afc1b25035816dad4bd93c..fc65e4bd3e9636de30b14eb3a176fdf7f192ba63 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -14,8 +14,8 @@ # libpgport_srv.a - contains object files without FRONTEND defined, # for use only by the backend binaries # -# 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. +# 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 @@ -30,8 +30,10 @@ include $(top_builddir)/src/Makefile.global override CPPFLAGS := -I$(top_builddir)/src/port -DFRONTEND $(CPPFLAGS) LIBS += $(PTHREAD_LIBS) -OBJS = $(LIBOBJS) chklocale.o dirmod.o exec.o inet_net_ntop.o noblock.o path.o \ - pgsleep.o pgstrcasecmp.o qsort.o qsort_arg.o sprompt.o thread.o +OBJS = $(LIBOBJS) chklocale.o dirmod.o exec.o inet_net_ntop.o noblock.o \ + path.o pgcheckdir.o pgmkdirp.o pgsleep.o pgstrcasecmp.o \ + qsort.o qsort_arg.o sprompt.o thread.o + ifneq (,$(filter $(PORTNAME),cygwin win32)) OBJS += pipe.o endif diff --git a/src/port/pgcheckdir.c b/src/port/pgcheckdir.c new file mode 100644 index 0000000000000000000000000000000000000000..12ec8a6b7399c7114f13c04923a10007f195564d --- /dev/null +++ b/src/port/pgcheckdir.c @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------- + * + * 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-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include + + +/* + * 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; + + 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; + } + else + { + result = 2; /* 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? */ + + return result; +} diff --git a/src/port/pgmkdirp.c b/src/port/pgmkdirp.c new file mode 100644 index 0000000000000000000000000000000000000000..b65db5e9695aaff1b66054f0eace324e42ab0f54 --- /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 + + +/* + * 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 occcur: + * + * 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; +}