diff options
Diffstat (limited to 'src/bin/pg_upgrade/file.c')
-rw-r--r-- | src/bin/pg_upgrade/file.c | 250 |
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; +} |