aboutsummaryrefslogtreecommitdiff
path: root/src/port/copydir.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-08-02 19:02:32 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-08-02 19:02:32 +0000
commit558730ac6bbc668a75c7a7619beae5a1b34d965f (patch)
treed8928d1a5e9e5aefa53a6f96fd7d9237d40e872e /src/port/copydir.c
parent0001e98d54f3d81c2ff413e4aec4933bd1378963 (diff)
downloadpostgresql-558730ac6bbc668a75c7a7619beae5a1b34d965f.tar.gz
postgresql-558730ac6bbc668a75c7a7619beae5a1b34d965f.zip
Clean up CREATE DATABASE processing to make it more robust and get rid
of special case for Windows port. Put a PG_TRY around most of createdb() to ensure that we remove copied subdirectories on failure, even if the failure happens while creating the pg_database row. (I think this explains Oliver Siegmar's recent report.) Having done that, there's no need for the fragile assumption that copydir() mustn't ereport(ERROR), so simplify its API. Eliminate the old code that used system("cp ...") to copy subdirectories, in favor of using copydir() on all platforms. This not only should allow much better error reporting, but allows us to fsync the created files before trusting that the copy has succeeded.
Diffstat (limited to 'src/port/copydir.c')
-rw-r--r--src/port/copydir.c146
1 files changed, 101 insertions, 45 deletions
diff --git a/src/port/copydir.c b/src/port/copydir.c
index 6062da96a84..a9339e79f90 100644
--- a/src/port/copydir.c
+++ b/src/port/copydir.c
@@ -11,84 +11,140 @@
* as a service.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/port/copydir.c,v 1.11 2005/03/24 02:11:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/port/copydir.c,v 1.12 2005/08/02 19:02:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
#include "storage/fd.h"
-#undef mkdir /* no reason to use that macro because we
- * ignore the 2nd arg */
+
+static void copy_file(char *fromfile, char *tofile);
/*
- * copydir: copy a directory (we only need to go one level deep)
- *
- * Return 0 on success, nonzero on failure.
+ * copydir: copy a directory
*
- * NB: do not elog(ERROR) on failure. Return to caller so it can try to
- * clean up.
+ * If recurse is false, subdirectories are ignored. Anything that's not
+ * a directory or a regular file is ignored.
*/
-int
-copydir(char *fromdir, char *todir)
+void
+copydir(char *fromdir, char *todir, bool recurse)
{
DIR *xldir;
struct dirent *xlde;
- char fromfl[MAXPGPATH];
- char tofl[MAXPGPATH];
+ char fromfile[MAXPGPATH];
+ char tofile[MAXPGPATH];
- if (mkdir(todir) != 0)
- {
- ereport(WARNING,
+ if (mkdir(todir, S_IRUSR | S_IWUSR | S_IXUSR) != 0)
+ ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create directory \"%s\": %m", todir)));
- return -1;
- }
+
xldir = AllocateDir(fromdir);
if (xldir == NULL)
- {
- ereport(WARNING,
+ ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open directory \"%s\": %m", fromdir)));
- return -1;
+
+ while ((xlde = ReadDir(xldir, fromdir)) != NULL)
+ {
+ struct stat fst;
+
+ if (strcmp(xlde->d_name, ".") == 0 ||
+ strcmp(xlde->d_name, "..") == 0)
+ continue;
+
+ snprintf(fromfile, MAXPGPATH, "%s/%s", fromdir, xlde->d_name);
+ snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
+
+ if (stat(fromfile, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat \"%s\": %m", fromfile)));
+
+ if (fst.st_mode & S_IFDIR)
+ {
+ /* recurse to handle subdirectories */
+ if (recurse)
+ copydir(fromfile, tofile, true);
+ }
+ else if (fst.st_mode & S_IFREG)
+ copy_file(fromfile, tofile);
}
- errno = 0;
- while ((xlde = readdir(xldir)) != NULL)
+ FreeDir(xldir);
+}
+
+/*
+ * copy one file
+ */
+static void
+copy_file(char *fromfile, char *tofile)
+{
+ char buffer[8 * BLCKSZ];
+ int srcfd;
+ int dstfd;
+ int nbytes;
+
+ /*
+ * Open the files
+ */
+ srcfd = BasicOpenFile(fromfile, O_RDONLY | PG_BINARY, 0);
+ if (srcfd < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\": %m", fromfile)));
+
+ dstfd = BasicOpenFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
+ S_IRUSR | S_IWUSR);
+ if (dstfd < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not create file \"%s\": %m", tofile)));
+
+ /*
+ * Do the data copying.
+ */
+ for (;;)
{
- snprintf(fromfl, MAXPGPATH, "%s/%s", fromdir, xlde->d_name);
- snprintf(tofl, MAXPGPATH, "%s/%s", todir, xlde->d_name);
- if (CopyFile(fromfl, tofl, TRUE) < 0)
+ nbytes = read(srcfd, buffer, sizeof(buffer));
+ if (nbytes < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", fromfile)));
+ if (nbytes == 0)
+ break;
+ errno = 0;
+ if ((int) write(dstfd, buffer, nbytes) != nbytes)
{
- ereport(WARNING,
+ /* if write didn't set errno, assume problem is no disk space */
+ if (errno == 0)
+ errno = ENOSPC;
+ ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not copy file \"%s\": %m", fromfl)));
- FreeDir(xldir);
- return -1;
+ errmsg("could not write to file \"%s\": %m", tofile)));
}
- errno = 0;
}
-#ifdef WIN32
/*
- * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but
- * not in released version
+ * Be paranoid here to ensure we catch problems.
*/
- if (GetLastError() == ERROR_NO_MORE_FILES)
- errno = 0;
-#endif
- if (errno)
- {
- ereport(WARNING,
+ if (pg_fsync(dstfd) != 0)
+ ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not read directory \"%s\": %m", fromdir)));
- FreeDir(xldir);
- return -1;
- }
+ errmsg("could not fsync file \"%s\": %m", tofile)));
- FreeDir(xldir);
- return 0;
+ if (close(dstfd))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close file \"%s\": %m", tofile)));
+
+ close(srcfd);
}