#include "postgres.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "storage/copydir.h"
#include "storage/fd.h"
#include "miscadmin.h"
Go to the source code of this file.
Defines | |
#define | COPY_BUF_SIZE (8 * BLCKSZ) |
Functions | |
static void | fsync_fname (char *fname, bool isdir) |
void | copydir (char *fromdir, char *todir, bool recurse) |
void | copy_file (char *fromfile, char *tofile) |
#define COPY_BUF_SIZE (8 * BLCKSZ) |
void copy_file | ( | char * | fromfile, | |
char * | tofile | |||
) |
Definition at line 138 of file copydir.c.
References CHECK_FOR_INTERRUPTS, CloseTransientFile(), COPY_BUF_SIZE, ereport, errcode_for_file_access(), errmsg(), ERROR, OpenTransientFile(), palloc(), pfree(), PG_BINARY, pg_flush_data(), read, and write.
{ char *buffer; int srcfd; int dstfd; int nbytes; off_t offset; /* Use palloc to ensure we get a maxaligned buffer */ #define COPY_BUF_SIZE (8 * BLCKSZ) buffer = palloc(COPY_BUF_SIZE); /* * Open the files */ srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY, 0); if (srcfd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", fromfile))); dstfd = OpenTransientFile(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 (offset = 0;; offset += nbytes) { /* If we got a cancel signal during the copy of the file, quit */ CHECK_FOR_INTERRUPTS(); nbytes = read(srcfd, buffer, COPY_BUF_SIZE); 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) { /* 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 write to file \"%s\": %m", tofile))); } /* * We fsync the files later but first flush them to avoid spamming the * cache and hopefully get the kernel to start writing them out before * the fsync comes. Ignore any error, since it's only a hint. */ (void) pg_flush_data(dstfd, offset, nbytes); } if (CloseTransientFile(dstfd)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tofile))); CloseTransientFile(srcfd); pfree(buffer); }
void copydir | ( | char * | fromdir, | |
char * | todir, | |||
bool | recurse | |||
) |
Definition at line 40 of file copydir.c.
References AllocateDir(), CHECK_FOR_INTERRUPTS, copy_file(), copydir(), dirent::d_name, enableFsync, ereport, errcode_for_file_access(), errmsg(), ERROR, FreeDir(), fsync_fname(), lstat, MAXPGPATH, mkdir, NULL, ReadDir(), and snprintf().
Referenced by copydir(), createdb(), dbase_redo(), and movedb().
{ DIR *xldir; struct dirent *xlde; char fromfile[MAXPGPATH]; char tofile[MAXPGPATH]; if (mkdir(todir, S_IRWXU) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", todir))); xldir = AllocateDir(fromdir); if (xldir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fromdir))); while ((xlde = ReadDir(xldir, fromdir)) != NULL) { struct stat fst; /* If we got a cancel signal during the copy of the directory, quit */ CHECK_FOR_INTERRUPTS(); 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 (lstat(fromfile, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", fromfile))); if (S_ISDIR(fst.st_mode)) { /* recurse to handle subdirectories */ if (recurse) copydir(fromfile, tofile, true); } else if (S_ISREG(fst.st_mode)) copy_file(fromfile, tofile); } FreeDir(xldir); /* * Be paranoid here and fsync all files to ensure the copy is really done. * But if fsync is disabled, we're done. */ if (!enableFsync) return; xldir = AllocateDir(todir); if (xldir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", todir))); while ((xlde = ReadDir(xldir, todir)) != NULL) { struct stat fst; if (strcmp(xlde->d_name, ".") == 0 || strcmp(xlde->d_name, "..") == 0) continue; snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name); /* * We don't need to sync subdirectories here since the recursive * copydir will do it before it returns */ if (lstat(tofile, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", tofile))); if (S_ISREG(fst.st_mode)) fsync_fname(tofile, false); } FreeDir(xldir); /* * It's important to fsync the destination directory itself as individual * file fsyncs don't guarantee that the directory entry for the file is * synced. Recent versions of ext4 have made the window much wider but * it's been true for ext3 and other filesystems in the past. */ fsync_fname(todir, true); }
static void fsync_fname | ( | char * | fname, | |
bool | isdir | |||
) | [static] |
Definition at line 219 of file copydir.c.
References CloseTransientFile(), ereport, errcode_for_file_access(), errmsg(), ERROR, OpenTransientFile(), PG_BINARY, and pg_fsync().
Referenced by copydir().
{ int fd; int returncode; /* * Some OSs require directories to be opened read-only whereas other * systems don't allow us to fsync files opened read-only; so we need both * cases here */ if (!isdir) fd = OpenTransientFile(fname, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR); else fd = OpenTransientFile(fname, O_RDONLY | PG_BINARY, S_IRUSR | S_IWUSR); /* * Some OSs don't allow us to open directories at all (Windows returns * EACCES) */ if (fd < 0 && isdir && (errno == EISDIR || errno == EACCES)) return; else if (fd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", fname))); returncode = pg_fsync(fd); /* Some OSs don't allow us to fsync directories at all */ if (returncode != 0 && isdir && errno == EBADF) { CloseTransientFile(fd); return; } if (returncode != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", fname))); CloseTransientFile(fd); }