Header And Logo

PostgreSQL
| The world's most advanced open source database.

file.c

Go to the documentation of this file.
00001 /*
00002  *  file.c
00003  *
00004  *  file system operations
00005  *
00006  *  Copyright (c) 2010-2013, PostgreSQL Global Development Group
00007  *  contrib/pg_upgrade/file.c
00008  */
00009 
00010 #include "postgres_fe.h"
00011 
00012 #include "pg_upgrade.h"
00013 
00014 #include <fcntl.h>
00015 
00016 
00017 
00018 #ifndef WIN32
00019 static int  copy_file(const char *fromfile, const char *tofile, bool force);
00020 #else
00021 static int  win32_pghardlink(const char *src, const char *dst);
00022 #endif
00023 
00024 
00025 /*
00026  * copyAndUpdateFile()
00027  *
00028  *  Copies a relation file from src to dst.  If pageConverter is non-NULL, this function
00029  *  uses that pageConverter to do a page-by-page conversion.
00030  */
00031 const char *
00032 copyAndUpdateFile(pageCnvCtx *pageConverter,
00033                   const char *src, const char *dst, bool force)
00034 {
00035     if (pageConverter == NULL)
00036     {
00037         if (pg_copy_file(src, dst, force) == -1)
00038             return getErrorText(errno);
00039         else
00040             return NULL;
00041     }
00042     else
00043     {
00044         /*
00045          * We have a pageConverter object - that implies that the
00046          * PageLayoutVersion differs between the two clusters so we have to
00047          * perform a page-by-page conversion.
00048          *
00049          * If the pageConverter can convert the entire file at once, invoke
00050          * that plugin function, otherwise, read each page in the relation
00051          * file and call the convertPage plugin function.
00052          */
00053 
00054 #ifdef PAGE_CONVERSION
00055         if (pageConverter->convertFile)
00056             return pageConverter->convertFile(pageConverter->pluginData,
00057                                               dst, src);
00058         else
00059 #endif
00060         {
00061             int         src_fd;
00062             int         dstfd;
00063             char        buf[BLCKSZ];
00064             ssize_t     bytesRead;
00065             const char *msg = NULL;
00066 
00067             if ((src_fd = open(src, O_RDONLY, 0)) < 0)
00068                 return "could not open source file";
00069 
00070             if ((dstfd = open(dst, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
00071             {
00072                 close(src_fd);
00073                 return "could not create destination file";
00074             }
00075 
00076             while ((bytesRead = read(src_fd, buf, BLCKSZ)) == BLCKSZ)
00077             {
00078 #ifdef PAGE_CONVERSION
00079                 if ((msg = pageConverter->convertPage(pageConverter->pluginData, buf, buf)) != NULL)
00080                     break;
00081 #endif
00082                 if (write(dstfd, buf, BLCKSZ) != BLCKSZ)
00083                 {
00084                     msg = "could not write new page to destination";
00085                     break;
00086                 }
00087             }
00088 
00089             close(src_fd);
00090             close(dstfd);
00091 
00092             if (msg)
00093                 return msg;
00094             else if (bytesRead != 0)
00095                 return "found partial page in source file";
00096             else
00097                 return NULL;
00098         }
00099     }
00100 }
00101 
00102 
00103 /*
00104  * linkAndUpdateFile()
00105  *
00106  * Creates a hard link between the given relation files. We use
00107  * this function to perform a true in-place update. If the on-disk
00108  * format of the new cluster is bit-for-bit compatible with the on-disk
00109  * format of the old cluster, we can simply link each relation
00110  * instead of copying the data from the old cluster to the new cluster.
00111  */
00112 const char *
00113 linkAndUpdateFile(pageCnvCtx *pageConverter,
00114                   const char *src, const char *dst)
00115 {
00116     if (pageConverter != NULL)
00117         return "Cannot in-place update this cluster, page-by-page conversion is required";
00118 
00119     if (pg_link_file(src, dst) == -1)
00120         return getErrorText(errno);
00121     else
00122         return NULL;
00123 }
00124 
00125 
00126 #ifndef WIN32
00127 static int
00128 copy_file(const char *srcfile, const char *dstfile, bool force)
00129 {
00130 
00131 #define COPY_BUF_SIZE (50 * BLCKSZ)
00132 
00133     int         src_fd;
00134     int         dest_fd;
00135     char       *buffer;
00136     int         ret = 0;
00137     int         save_errno = 0;
00138 
00139     if ((srcfile == NULL) || (dstfile == NULL))
00140         return -1;
00141 
00142     if ((src_fd = open(srcfile, O_RDONLY, 0)) < 0)
00143         return -1;
00144 
00145     if ((dest_fd = open(dstfile, O_RDWR | O_CREAT | (force ? 0 : O_EXCL), S_IRUSR | S_IWUSR)) < 0)
00146     {
00147         if (src_fd != 0)
00148             close(src_fd);
00149 
00150         return -1;
00151     }
00152 
00153     buffer = (char *) pg_malloc(COPY_BUF_SIZE);
00154 
00155     /* perform data copying i.e read src source, write to destination */
00156     while (true)
00157     {
00158         ssize_t     nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
00159 
00160         if (nbytes < 0)
00161         {
00162             save_errno = errno;
00163             ret = -1;
00164             break;
00165         }
00166 
00167         if (nbytes == 0)
00168             break;
00169 
00170         errno = 0;
00171 
00172         if (write(dest_fd, buffer, nbytes) != nbytes)
00173         {
00174             save_errno = errno;
00175             ret = -1;
00176             break;
00177         }
00178     }
00179 
00180     pg_free(buffer);
00181 
00182     if (src_fd != 0)
00183         close(src_fd);
00184 
00185     if (dest_fd != 0)
00186         close(dest_fd);
00187 
00188     if (save_errno != 0)
00189         errno = save_errno;
00190 
00191     return ret;
00192 }
00193 #endif
00194 
00195 
00196 void
00197 check_hard_link(void)
00198 {
00199     char        existing_file[MAXPGPATH];
00200     char        new_link_file[MAXPGPATH];
00201 
00202     snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
00203     snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", new_cluster.pgdata);
00204     unlink(new_link_file);      /* might fail */
00205 
00206     if (pg_link_file(existing_file, new_link_file) == -1)
00207     {
00208         pg_log(PG_FATAL,
00209                "Could not create hard link between old and new data directories: %s\n"
00210                "In link mode the old and new data directories must be on the same file system volume.\n",
00211                getErrorText(errno));
00212     }
00213     unlink(new_link_file);
00214 }
00215 
00216 #ifdef WIN32
00217 static int
00218 win32_pghardlink(const char *src, const char *dst)
00219 {
00220     /*
00221      * CreateHardLinkA returns zero for failure
00222      * http://msdn.microsoft.com/en-us/library/aa363860(VS.85).aspx
00223      */
00224     if (CreateHardLinkA(dst, src, NULL) == 0)
00225         return -1;
00226     else
00227         return 0;
00228 }
00229 #endif
00230 
00231 
00232 /* fopen() file with no group/other permissions */
00233 FILE *
00234 fopen_priv(const char *path, const char *mode)
00235 {
00236     mode_t      old_umask = umask(S_IRWXG | S_IRWXO);
00237     FILE       *fp;
00238 
00239     fp = fopen(path, mode);
00240     umask(old_umask);
00241 
00242     return fp;
00243 }