Header And Logo

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

copydir.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * copydir.c
00004  *    copies a directory
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *  While "xcopy /e /i /q" works fine for copying directories, on Windows XP
00010  *  it requires a Window handle which prevents it from working when invoked
00011  *  as a service.
00012  *
00013  * IDENTIFICATION
00014  *    src/backend/storage/file/copydir.c
00015  *
00016  *-------------------------------------------------------------------------
00017  */
00018 
00019 #include "postgres.h"
00020 
00021 #include <fcntl.h>
00022 #include <unistd.h>
00023 #include <sys/stat.h>
00024 
00025 #include "storage/copydir.h"
00026 #include "storage/fd.h"
00027 #include "miscadmin.h"
00028 
00029 
00030 static void fsync_fname(char *fname, bool isdir);
00031 
00032 
00033 /*
00034  * copydir: copy a directory
00035  *
00036  * If recurse is false, subdirectories are ignored.  Anything that's not
00037  * a directory or a regular file is ignored.
00038  */
00039 void
00040 copydir(char *fromdir, char *todir, bool recurse)
00041 {
00042     DIR        *xldir;
00043     struct dirent *xlde;
00044     char        fromfile[MAXPGPATH];
00045     char        tofile[MAXPGPATH];
00046 
00047     if (mkdir(todir, S_IRWXU) != 0)
00048         ereport(ERROR,
00049                 (errcode_for_file_access(),
00050                  errmsg("could not create directory \"%s\": %m", todir)));
00051 
00052     xldir = AllocateDir(fromdir);
00053     if (xldir == NULL)
00054         ereport(ERROR,
00055                 (errcode_for_file_access(),
00056                  errmsg("could not open directory \"%s\": %m", fromdir)));
00057 
00058     while ((xlde = ReadDir(xldir, fromdir)) != NULL)
00059     {
00060         struct stat fst;
00061 
00062         /* If we got a cancel signal during the copy of the directory, quit */
00063         CHECK_FOR_INTERRUPTS();
00064 
00065         if (strcmp(xlde->d_name, ".") == 0 ||
00066             strcmp(xlde->d_name, "..") == 0)
00067             continue;
00068 
00069         snprintf(fromfile, MAXPGPATH, "%s/%s", fromdir, xlde->d_name);
00070         snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
00071 
00072         if (lstat(fromfile, &fst) < 0)
00073             ereport(ERROR,
00074                     (errcode_for_file_access(),
00075                      errmsg("could not stat file \"%s\": %m", fromfile)));
00076 
00077         if (S_ISDIR(fst.st_mode))
00078         {
00079             /* recurse to handle subdirectories */
00080             if (recurse)
00081                 copydir(fromfile, tofile, true);
00082         }
00083         else if (S_ISREG(fst.st_mode))
00084             copy_file(fromfile, tofile);
00085     }
00086     FreeDir(xldir);
00087 
00088     /*
00089      * Be paranoid here and fsync all files to ensure the copy is really done.
00090      * But if fsync is disabled, we're done.
00091      */
00092     if (!enableFsync)
00093         return;
00094 
00095     xldir = AllocateDir(todir);
00096     if (xldir == NULL)
00097         ereport(ERROR,
00098                 (errcode_for_file_access(),
00099                  errmsg("could not open directory \"%s\": %m", todir)));
00100 
00101     while ((xlde = ReadDir(xldir, todir)) != NULL)
00102     {
00103         struct stat fst;
00104 
00105         if (strcmp(xlde->d_name, ".") == 0 ||
00106             strcmp(xlde->d_name, "..") == 0)
00107             continue;
00108 
00109         snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
00110 
00111         /*
00112          * We don't need to sync subdirectories here since the recursive
00113          * copydir will do it before it returns
00114          */
00115         if (lstat(tofile, &fst) < 0)
00116             ereport(ERROR,
00117                     (errcode_for_file_access(),
00118                      errmsg("could not stat file \"%s\": %m", tofile)));
00119 
00120         if (S_ISREG(fst.st_mode))
00121             fsync_fname(tofile, false);
00122     }
00123     FreeDir(xldir);
00124 
00125     /*
00126      * It's important to fsync the destination directory itself as individual
00127      * file fsyncs don't guarantee that the directory entry for the file is
00128      * synced. Recent versions of ext4 have made the window much wider but
00129      * it's been true for ext3 and other filesystems in the past.
00130      */
00131     fsync_fname(todir, true);
00132 }
00133 
00134 /*
00135  * copy one file
00136  */
00137 void
00138 copy_file(char *fromfile, char *tofile)
00139 {
00140     char       *buffer;
00141     int         srcfd;
00142     int         dstfd;
00143     int         nbytes;
00144     off_t       offset;
00145 
00146     /* Use palloc to ensure we get a maxaligned buffer */
00147 #define COPY_BUF_SIZE (8 * BLCKSZ)
00148 
00149     buffer = palloc(COPY_BUF_SIZE);
00150 
00151     /*
00152      * Open the files
00153      */
00154     srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY, 0);
00155     if (srcfd < 0)
00156         ereport(ERROR,
00157                 (errcode_for_file_access(),
00158                  errmsg("could not open file \"%s\": %m", fromfile)));
00159 
00160     dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
00161                               S_IRUSR | S_IWUSR);
00162     if (dstfd < 0)
00163         ereport(ERROR,
00164                 (errcode_for_file_access(),
00165                  errmsg("could not create file \"%s\": %m", tofile)));
00166 
00167     /*
00168      * Do the data copying.
00169      */
00170     for (offset = 0;; offset += nbytes)
00171     {
00172         /* If we got a cancel signal during the copy of the file, quit */
00173         CHECK_FOR_INTERRUPTS();
00174 
00175         nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
00176         if (nbytes < 0)
00177             ereport(ERROR,
00178                     (errcode_for_file_access(),
00179                      errmsg("could not read file \"%s\": %m", fromfile)));
00180         if (nbytes == 0)
00181             break;
00182         errno = 0;
00183         if ((int) write(dstfd, buffer, nbytes) != nbytes)
00184         {
00185             /* if write didn't set errno, assume problem is no disk space */
00186             if (errno == 0)
00187                 errno = ENOSPC;
00188             ereport(ERROR,
00189                     (errcode_for_file_access(),
00190                      errmsg("could not write to file \"%s\": %m", tofile)));
00191         }
00192 
00193         /*
00194          * We fsync the files later but first flush them to avoid spamming the
00195          * cache and hopefully get the kernel to start writing them out before
00196          * the fsync comes.  Ignore any error, since it's only a hint.
00197          */
00198         (void) pg_flush_data(dstfd, offset, nbytes);
00199     }
00200 
00201     if (CloseTransientFile(dstfd))
00202         ereport(ERROR,
00203                 (errcode_for_file_access(),
00204                  errmsg("could not close file \"%s\": %m", tofile)));
00205 
00206     CloseTransientFile(srcfd);
00207 
00208     pfree(buffer);
00209 }
00210 
00211 
00212 /*
00213  * fsync a file
00214  *
00215  * Try to fsync directories but ignore errors that indicate the OS
00216  * just doesn't allow/require fsyncing directories.
00217  */
00218 static void
00219 fsync_fname(char *fname, bool isdir)
00220 {
00221     int         fd;
00222     int         returncode;
00223 
00224     /*
00225      * Some OSs require directories to be opened read-only whereas other
00226      * systems don't allow us to fsync files opened read-only; so we need both
00227      * cases here
00228      */
00229     if (!isdir)
00230         fd = OpenTransientFile(fname,
00231                                O_RDWR | PG_BINARY,
00232                                S_IRUSR | S_IWUSR);
00233     else
00234         fd = OpenTransientFile(fname,
00235                                O_RDONLY | PG_BINARY,
00236                                S_IRUSR | S_IWUSR);
00237 
00238     /*
00239      * Some OSs don't allow us to open directories at all (Windows returns
00240      * EACCES)
00241      */
00242     if (fd < 0 && isdir && (errno == EISDIR || errno == EACCES))
00243         return;
00244 
00245     else if (fd < 0)
00246         ereport(ERROR,
00247                 (errcode_for_file_access(),
00248                  errmsg("could not open file \"%s\": %m", fname)));
00249 
00250     returncode = pg_fsync(fd);
00251 
00252     /* Some OSs don't allow us to fsync directories at all */
00253     if (returncode != 0 && isdir && errno == EBADF)
00254     {
00255         CloseTransientFile(fd);
00256         return;
00257     }
00258 
00259     if (returncode != 0)
00260         ereport(ERROR,
00261                 (errcode_for_file_access(),
00262                  errmsg("could not fsync file \"%s\": %m", fname)));
00263 
00264     CloseTransientFile(fd);
00265 }