Index: src/backend/postmaster/pgstat.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/pgstat.c,v retrieving revision 1.187 diff -c -r1.187 pgstat.c *** src/backend/postmaster/pgstat.c 1 Jan 2009 17:23:46 -0000 1.187 --- src/backend/postmaster/pgstat.c 21 Apr 2009 06:32:54 -0000 *************** *** 111,116 **** --- 111,117 ---- bool pgstat_track_counts = false; int pgstat_track_functions = TRACK_FUNC_OFF; int pgstat_track_activity_query_size = 1024; + char *pgstat_temp_directory; /* ---------- * Built from GUC parameter *************** *** 2953,2958 **** --- 2954,2964 ---- const char *statfile = permanent?PGSTAT_STAT_PERMANENT_FILENAME:pgstat_stat_filename; /* + * Create temporary statistics directory if not present; ignore errors + */ + CreateDir(pgstat_temp_directory, 0700); + + /* * Open the statistics temp file to write out the current values. */ fpout = AllocateFile(tmpfile, PG_BINARY_W); Index: src/backend/storage/file/fd.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/storage/file/fd.c,v retrieving revision 1.148 diff -c -r1.148 fd.c *** src/backend/storage/file/fd.c 4 Mar 2009 09:12:49 -0000 1.148 --- src/backend/storage/file/fd.c 21 Apr 2009 06:32:54 -0000 *************** *** 1875,1877 **** --- 1875,2084 ---- FreeDir(temp_dir); } + + /* 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; + } + + /* Create the specified directory if not present; ignore errors */ + void + CreateDir(char *pathname, mode_t mode) + { + struct stat st; + char path[MAXPGPATH]; + char orig_wd[MAXPGPATH]; + char lnk_buf[MAXPGPATH]; + + /* Create the specified directory if not present */ + if (lstat(pathname, &st) < 0) + { + mkdir_p(pathname, mode); + return; + } + + /* Do nothing if the specified directory is present */ + if (S_ISDIR(st.st_mode)) + return; + + /* + * If the specified 'pathname' indicates the symlink, we pursue + * the chain of it and create the referenced directory. + */ + #ifdef HAVE_READLINK + StrNCpy(path, pathname, MAXPGPATH); + + /* + * 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)) + { + ereport(LOG, + (errmsg("could not identify current directory: %m"))); + return; + } + + for (;;) + { + char *lsep; + char *fname; + int rllen; + + /* + * Create the referenced directory when the chain of symlinks + * can not be pursued any longer. + */ + if (lstat(path, &st) < 0 || !S_ISLNK(st.st_mode)) + { + mkdir_p(path, mode); + break; + } + + lsep = last_dir_separator(path); + if (lsep) + { + *lsep = '\0'; + + if (chdir(path) == -1) + { + ereport(LOG, + (errmsg("could not change directory to \"%s\": %m", + path))); + break; + } + + fname = lsep + 1; + } + else + fname = path; + + rllen = readlink(fname, lnk_buf, MAXPGPATH); + if (rllen < 0 || rllen >= MAXPGPATH) + { + ereport(LOG, + (errmsg("could not read symbolic link \"%s\": %m", + fname))); + break; + } + + lnk_buf[rllen] = '\0'; + StrNCpy(path, lnk_buf, MAXPGPATH); + } + + if (chdir(orig_wd) == -1) + { + ereport(LOG, + (errmsg("could not change directory to \"%s\": %m"), + orig_wd)); + return; + } + #endif /* HAVE_READLINK */ + } Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.502 diff -c -r1.502 guc.c *** src/backend/utils/misc/guc.c 7 Apr 2009 23:27:34 -0000 1.502 --- src/backend/utils/misc/guc.c 21 Apr 2009 06:32:55 -0000 *************** *** 375,382 **** char *IdentFileName; char *external_pid_file; - char *pgstat_temp_directory; - int tcp_keepalives_idle; int tcp_keepalives_interval; int tcp_keepalives_count; --- 375,380 ---- Index: src/include/pgstat.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/pgstat.h,v retrieving revision 1.82 diff -c -r1.82 pgstat.h *** src/include/pgstat.h 4 Jan 2009 22:19:59 -0000 1.82 --- src/include/pgstat.h 21 Apr 2009 06:32:55 -0000 *************** *** 593,598 **** --- 593,599 ---- extern bool pgstat_track_counts; extern int pgstat_track_functions; extern PGDLLIMPORT int pgstat_track_activity_query_size; + extern PGDLLIMPORT char *pgstat_temp_directory; extern char *pgstat_stat_tmpname; extern char *pgstat_stat_filename; Index: src/include/storage/fd.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/storage/fd.h,v retrieving revision 1.64 diff -c -r1.64 fd.h *** src/include/storage/fd.h 12 Jan 2009 05:10:45 -0000 1.64 --- src/include/storage/fd.h 21 Apr 2009 06:32:55 -0000 *************** *** 98,103 **** --- 98,105 ---- extern int pg_fsync_writethrough(int fd); extern int pg_fdatasync(int fd); + extern void CreateDir(char *pathname, mode_t mode); + /* Filename components for OpenTemporaryFile */ #define PG_TEMP_FILES_DIR "pgsql_tmp" #define PG_TEMP_FILE_PREFIX "pgsql_tmp"