summaryrefslogtreecommitdiff
path: root/src/bin/pg_upgrade/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pg_upgrade/file.c')
-rw-r--r--src/bin/pg_upgrade/file.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
new file mode 100644
index 00000000000..79d9390216e
--- /dev/null
+++ b/src/bin/pg_upgrade/file.c
@@ -0,0 +1,250 @@
+/*
+ * file.c
+ *
+ * file system operations
+ *
+ * Copyright (c) 2010-2015, PostgreSQL Global Development Group
+ * src/bin/pg_upgrade/file.c
+ */
+
+#include "postgres_fe.h"
+
+#include "pg_upgrade.h"
+
+#include <fcntl.h>
+
+
+
+#ifndef WIN32
+static int copy_file(const char *fromfile, const char *tofile, bool force);
+#else
+static int win32_pghardlink(const char *src, const char *dst);
+#endif
+
+
+/*
+ * copyAndUpdateFile()
+ *
+ * Copies a relation file from src to dst. If pageConverter is non-NULL, this function
+ * uses that pageConverter to do a page-by-page conversion.
+ */
+const char *
+copyAndUpdateFile(pageCnvCtx *pageConverter,
+ const char *src, const char *dst, bool force)
+{
+ if (pageConverter == NULL)
+ {
+ if (pg_copy_file(src, dst, force) == -1)
+ return getErrorText(errno);
+ else
+ return NULL;
+ }
+ else
+ {
+ /*
+ * We have a pageConverter object - that implies that the
+ * PageLayoutVersion differs between the two clusters so we have to
+ * perform a page-by-page conversion.
+ *
+ * If the pageConverter can convert the entire file at once, invoke
+ * that plugin function, otherwise, read each page in the relation
+ * file and call the convertPage plugin function.
+ */
+
+#ifdef PAGE_CONVERSION
+ if (pageConverter->convertFile)
+ return pageConverter->convertFile(pageConverter->pluginData,
+ dst, src);
+ else
+#endif
+ {
+ int src_fd;
+ int dstfd;
+ char buf[BLCKSZ];
+ ssize_t bytesRead;
+ const char *msg = NULL;
+
+ if ((src_fd = open(src, O_RDONLY, 0)) < 0)
+ return "could not open source file";
+
+ if ((dstfd = open(dst, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
+ {
+ close(src_fd);
+ return "could not create destination file";
+ }
+
+ while ((bytesRead = read(src_fd, buf, BLCKSZ)) == BLCKSZ)
+ {
+#ifdef PAGE_CONVERSION
+ if ((msg = pageConverter->convertPage(pageConverter->pluginData, buf, buf)) != NULL)
+ break;
+#endif
+ if (write(dstfd, buf, BLCKSZ) != BLCKSZ)
+ {
+ msg = "could not write new page to destination";
+ break;
+ }
+ }
+
+ close(src_fd);
+ close(dstfd);
+
+ if (msg)
+ return msg;
+ else if (bytesRead != 0)
+ return "found partial page in source file";
+ else
+ return NULL;
+ }
+ }
+}
+
+
+/*
+ * linkAndUpdateFile()
+ *
+ * Creates a hard link between the given relation files. We use
+ * this function to perform a true in-place update. If the on-disk
+ * format of the new cluster is bit-for-bit compatible with the on-disk
+ * format of the old cluster, we can simply link each relation
+ * instead of copying the data from the old cluster to the new cluster.
+ */
+const char *
+linkAndUpdateFile(pageCnvCtx *pageConverter,
+ const char *src, const char *dst)
+{
+ if (pageConverter != NULL)
+ return "Cannot in-place update this cluster, page-by-page conversion is required";
+
+ if (pg_link_file(src, dst) == -1)
+ return getErrorText(errno);
+ else
+ return NULL;
+}
+
+
+#ifndef WIN32
+static int
+copy_file(const char *srcfile, const char *dstfile, bool force)
+{
+#define COPY_BUF_SIZE (50 * BLCKSZ)
+
+ int src_fd;
+ int dest_fd;
+ char *buffer;
+ int ret = 0;
+ int save_errno = 0;
+
+ if ((srcfile == NULL) || (dstfile == NULL))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((src_fd = open(srcfile, O_RDONLY, 0)) < 0)
+ return -1;
+
+ if ((dest_fd = open(dstfile, O_RDWR | O_CREAT | (force ? 0 : O_EXCL), S_IRUSR | S_IWUSR)) < 0)
+ {
+ save_errno = errno;
+
+ if (src_fd != 0)
+ close(src_fd);
+
+ errno = save_errno;
+ return -1;
+ }
+
+ buffer = (char *) pg_malloc(COPY_BUF_SIZE);
+
+ /* perform data copying i.e read src source, write to destination */
+ while (true)
+ {
+ ssize_t nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
+
+ if (nbytes < 0)
+ {
+ save_errno = errno;
+ ret = -1;
+ break;
+ }
+
+ if (nbytes == 0)
+ break;
+
+ errno = 0;
+
+ if (write(dest_fd, buffer, nbytes) != nbytes)
+ {
+ /* if write didn't set errno, assume problem is no disk space */
+ if (errno == 0)
+ errno = ENOSPC;
+ save_errno = errno;
+ ret = -1;
+ break;
+ }
+ }
+
+ pg_free(buffer);
+
+ if (src_fd != 0)
+ close(src_fd);
+
+ if (dest_fd != 0)
+ close(dest_fd);
+
+ if (save_errno != 0)
+ errno = save_errno;
+
+ return ret;
+}
+#endif
+
+
+void
+check_hard_link(void)
+{
+ char existing_file[MAXPGPATH];
+ char new_link_file[MAXPGPATH];
+
+ snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
+ snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", new_cluster.pgdata);
+ unlink(new_link_file); /* might fail */
+
+ if (pg_link_file(existing_file, new_link_file) == -1)
+ {
+ pg_fatal("Could not create hard link between old and new data directories: %s\n"
+ "In link mode the old and new data directories must be on the same file system volume.\n",
+ getErrorText(errno));
+ }
+ unlink(new_link_file);
+}
+
+#ifdef WIN32
+static int
+win32_pghardlink(const char *src, const char *dst)
+{
+ /*
+ * CreateHardLinkA returns zero for failure
+ * http://msdn.microsoft.com/en-us/library/aa363860(VS.85).aspx
+ */
+ if (CreateHardLinkA(dst, src, NULL) == 0)
+ return -1;
+ else
+ return 0;
+}
+#endif
+
+
+/* fopen() file with no group/other permissions */
+FILE *
+fopen_priv(const char *path, const char *mode)
+{
+ mode_t old_umask = umask(S_IRWXG | S_IRWXO);
+ FILE *fp;
+
+ fp = fopen(path, mode);
+ umask(old_umask);
+
+ return fp;
+}