2003-11-12 00:52:45 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* copydir.c
|
|
|
|
* copies a directory
|
|
|
|
*
|
2010-01-02 17:58:17 +01:00
|
|
|
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
2003-11-12 00:52:45 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
2003-05-15 19:59:17 +02:00
|
|
|
* While "xcopy /e /i /q" works fine for copying directories, on Windows XP
|
2003-09-10 22:12:01 +02:00
|
|
|
* it requires a Window handle which prevents it from working when invoked
|
2003-05-15 19:59:17 +02:00
|
|
|
* as a service.
|
2003-09-10 22:12:01 +02:00
|
|
|
*
|
2003-11-12 00:52:45 +01:00
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/storage/file/copydir.c
|
2003-11-12 00:52:45 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
2003-05-15 19:59:17 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2005-08-02 21:02:32 +02:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2010-11-12 22:39:53 +01:00
|
|
|
#include "storage/copydir.h"
|
2004-02-24 00:03:10 +01:00
|
|
|
#include "storage/fd.h"
|
2010-07-01 22:12:40 +02:00
|
|
|
#include "miscadmin.h"
|
2004-02-24 00:03:10 +01:00
|
|
|
|
2005-09-03 17:55:00 +02:00
|
|
|
/*
|
|
|
|
* On Windows, call non-macro versions of palloc; we can't reference
|
2007-07-25 14:22:54 +02:00
|
|
|
* CurrentMemoryContext in this file because of PGDLLIMPORT conflict.
|
2005-09-03 17:55:00 +02:00
|
|
|
*/
|
|
|
|
#if defined(WIN32) || defined(__CYGWIN__)
|
|
|
|
#undef palloc
|
|
|
|
#undef pstrdup
|
|
|
|
#define palloc(sz) pgport_palloc(sz)
|
|
|
|
#define pstrdup(str) pgport_pstrdup(str)
|
|
|
|
#endif
|
|
|
|
|
2005-08-02 21:02:32 +02:00
|
|
|
|
|
|
|
static void copy_file(char *fromfile, char *tofile);
|
2010-02-28 22:05:30 +01:00
|
|
|
static void fsync_fname(char *fname, bool isdir);
|
2003-05-16 03:57:52 +02:00
|
|
|
|
|
|
|
|
2003-09-10 22:12:01 +02:00
|
|
|
/*
|
2005-08-02 21:02:32 +02:00
|
|
|
* copydir: copy a directory
|
2003-09-10 22:12:01 +02:00
|
|
|
*
|
2005-08-02 21:02:32 +02:00
|
|
|
* If recurse is false, subdirectories are ignored. Anything that's not
|
|
|
|
* a directory or a regular file is ignored.
|
2003-09-10 22:12:01 +02:00
|
|
|
*/
|
2005-08-02 21:02:32 +02:00
|
|
|
void
|
|
|
|
copydir(char *fromdir, char *todir, bool recurse)
|
2003-05-15 19:59:17 +02:00
|
|
|
{
|
|
|
|
DIR *xldir;
|
|
|
|
struct dirent *xlde;
|
2005-08-02 21:02:32 +02:00
|
|
|
char fromfile[MAXPGPATH];
|
|
|
|
char tofile[MAXPGPATH];
|
2003-05-15 19:59:17 +02:00
|
|
|
|
2010-12-10 23:35:33 +01:00
|
|
|
if (mkdir(todir, S_IRWXU) != 0)
|
2005-08-02 21:02:32 +02:00
|
|
|
ereport(ERROR,
|
2003-07-27 19:10:07 +02:00
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not create directory \"%s\": %m", todir)));
|
2005-08-02 21:02:32 +02:00
|
|
|
|
2004-02-24 00:03:10 +01:00
|
|
|
xldir = AllocateDir(fromdir);
|
2003-05-15 19:59:17 +02:00
|
|
|
if (xldir == NULL)
|
2005-08-02 21:02:32 +02:00
|
|
|
ereport(ERROR,
|
2003-07-27 19:10:07 +02:00
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not open directory \"%s\": %m", fromdir)));
|
2005-08-02 21:02:32 +02:00
|
|
|
|
|
|
|
while ((xlde = ReadDir(xldir, fromdir)) != NULL)
|
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
struct stat fst;
|
2005-08-02 21:02:32 +02:00
|
|
|
|
2010-07-06 21:19:02 +02:00
|
|
|
/* If we got a cancel signal during the copy of the directory, quit */
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
2010-07-01 22:12:40 +02:00
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
if (strcmp(xlde->d_name, ".") == 0 ||
|
2005-08-02 21:02:32 +02:00
|
|
|
strcmp(xlde->d_name, "..") == 0)
|
2005-10-15 04:49:52 +02:00
|
|
|
continue;
|
2005-08-02 21:02:32 +02:00
|
|
|
|
|
|
|
snprintf(fromfile, MAXPGPATH, "%s/%s", fromdir, xlde->d_name);
|
|
|
|
snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
|
|
|
|
|
2006-07-19 00:36:46 +02:00
|
|
|
if (lstat(fromfile, &fst) < 0)
|
2005-08-02 21:02:32 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
2005-10-29 02:31:52 +02:00
|
|
|
errmsg("could not stat file \"%s\": %m", fromfile)));
|
2005-08-02 21:02:32 +02:00
|
|
|
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
if (S_ISDIR(fst.st_mode))
|
2005-08-02 21:02:32 +02:00
|
|
|
{
|
|
|
|
/* recurse to handle subdirectories */
|
|
|
|
if (recurse)
|
|
|
|
copydir(fromfile, tofile, true);
|
|
|
|
}
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
else if (S_ISREG(fst.st_mode))
|
2005-08-02 21:02:32 +02:00
|
|
|
copy_file(fromfile, tofile);
|
2003-05-15 19:59:17 +02:00
|
|
|
}
|
2010-02-22 03:50:10 +01:00
|
|
|
FreeDir(xldir);
|
2003-05-15 19:59:17 +02:00
|
|
|
|
2010-02-14 18:50:52 +01:00
|
|
|
/*
|
2010-02-22 03:50:10 +01:00
|
|
|
* Be paranoid here and fsync all files to ensure the copy is really done.
|
2010-02-14 18:50:52 +01:00
|
|
|
*/
|
2010-02-22 03:50:10 +01:00
|
|
|
xldir = AllocateDir(todir);
|
2010-02-15 01:50:57 +01:00
|
|
|
if (xldir == NULL)
|
2010-02-14 18:50:52 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
2010-02-22 03:50:10 +01:00
|
|
|
errmsg("could not open directory \"%s\": %m", todir)));
|
2010-02-15 01:50:57 +01:00
|
|
|
|
2010-02-22 03:50:10 +01:00
|
|
|
while ((xlde = ReadDir(xldir, todir)) != NULL)
|
2010-02-15 01:50:57 +01:00
|
|
|
{
|
2010-02-15 12:40:49 +01:00
|
|
|
struct stat fst;
|
|
|
|
|
2010-02-15 01:50:57 +01:00
|
|
|
if (strcmp(xlde->d_name, ".") == 0 ||
|
|
|
|
strcmp(xlde->d_name, "..") == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
|
2010-02-15 12:40:49 +01:00
|
|
|
|
2010-02-22 03:50:10 +01:00
|
|
|
/*
|
|
|
|
* We don't need to sync subdirectories here since the recursive
|
|
|
|
* copydir will do it before it returns
|
|
|
|
*/
|
|
|
|
if (lstat(tofile, &fst) < 0)
|
2010-02-15 12:40:49 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
2010-02-22 03:50:10 +01:00
|
|
|
errmsg("could not stat file \"%s\": %m", tofile)));
|
|
|
|
|
2010-02-15 12:40:49 +01:00
|
|
|
if (S_ISREG(fst.st_mode))
|
2010-02-28 22:05:30 +01:00
|
|
|
fsync_fname(tofile, false);
|
2010-02-15 01:50:57 +01:00
|
|
|
}
|
|
|
|
FreeDir(xldir);
|
|
|
|
|
2010-02-22 03:50:10 +01:00
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* 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.
|
2010-02-15 01:50:57 +01:00
|
|
|
*/
|
2010-02-28 22:05:30 +01:00
|
|
|
fsync_fname(todir, true);
|
2005-08-02 21:02:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* copy one file
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
copy_file(char *fromfile, char *tofile)
|
|
|
|
{
|
2005-09-02 20:55:32 +02:00
|
|
|
char *buffer;
|
2005-08-02 21:02:32 +02:00
|
|
|
int srcfd;
|
|
|
|
int dstfd;
|
|
|
|
int nbytes;
|
2010-02-15 01:50:57 +01:00
|
|
|
off_t offset;
|
2005-08-02 21:02:32 +02:00
|
|
|
|
2005-09-02 20:55:32 +02:00
|
|
|
/* Use palloc to ensure we get a maxaligned buffer */
|
|
|
|
#define COPY_BUF_SIZE (8 * BLCKSZ)
|
|
|
|
|
|
|
|
buffer = palloc(COPY_BUF_SIZE);
|
|
|
|
|
2005-08-02 21:02:32 +02:00
|
|
|
/*
|
|
|
|
* Open the files
|
|
|
|
*/
|
|
|
|
srcfd = BasicOpenFile(fromfile, O_RDONLY | PG_BINARY, 0);
|
|
|
|
if (srcfd < 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not open file \"%s\": %m", fromfile)));
|
|
|
|
|
|
|
|
dstfd = BasicOpenFile(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.
|
|
|
|
*/
|
2010-02-26 03:01:40 +01:00
|
|
|
for (offset = 0;; offset += nbytes)
|
2003-05-15 19:59:17 +02:00
|
|
|
{
|
2010-07-06 21:19:02 +02:00
|
|
|
/* If we got a cancel signal during the copy of the file, quit */
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
2010-07-01 22:12:40 +02:00
|
|
|
|
2005-09-02 20:55:32 +02:00
|
|
|
nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
|
2005-08-02 21:02:32 +02:00
|
|
|
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)
|
2003-08-04 02:43:34 +02:00
|
|
|
{
|
2005-08-02 21:02:32 +02:00
|
|
|
/* if write didn't set errno, assume problem is no disk space */
|
|
|
|
if (errno == 0)
|
|
|
|
errno = ENOSPC;
|
|
|
|
ereport(ERROR,
|
2003-08-04 02:43:34 +02:00
|
|
|
(errcode_for_file_access(),
|
2005-08-02 21:02:32 +02:00
|
|
|
errmsg("could not write to file \"%s\": %m", tofile)));
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
2005-03-24 03:11:20 +01:00
|
|
|
|
2010-02-15 01:50:57 +01:00
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* 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.
|
2010-02-15 01:50:57 +01:00
|
|
|
*/
|
|
|
|
pg_flush_data(dstfd, offset, nbytes);
|
|
|
|
}
|
2003-05-15 19:59:17 +02:00
|
|
|
|
2005-08-02 21:02:32 +02:00
|
|
|
if (close(dstfd))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not close file \"%s\": %m", tofile)));
|
|
|
|
|
|
|
|
close(srcfd);
|
2005-09-02 20:55:32 +02:00
|
|
|
|
|
|
|
pfree(buffer);
|
2003-05-15 19:59:17 +02:00
|
|
|
}
|
2010-02-15 01:50:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fsync a file
|
2010-02-28 22:05:30 +01:00
|
|
|
*
|
|
|
|
* Try to fsync directories but ignore errors that indicate the OS
|
|
|
|
* just doesn't allow/require fsyncing directories.
|
2010-02-15 01:50:57 +01:00
|
|
|
*/
|
|
|
|
static void
|
2010-02-28 22:05:30 +01:00
|
|
|
fsync_fname(char *fname, bool isdir)
|
2010-02-15 01:50:57 +01:00
|
|
|
{
|
2010-02-28 22:05:30 +01:00
|
|
|
int fd;
|
2010-07-06 21:19:02 +02:00
|
|
|
int returncode;
|
2010-02-15 01:50:57 +01:00
|
|
|
|
2010-03-01 15:54:00 +01:00
|
|
|
/*
|
2010-07-06 21:19:02 +02:00
|
|
|
* 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
|
2010-02-28 22:05:30 +01:00
|
|
|
*/
|
|
|
|
if (!isdir)
|
|
|
|
fd = BasicOpenFile(fname,
|
|
|
|
O_RDWR | PG_BINARY,
|
|
|
|
S_IRUSR | S_IWUSR);
|
|
|
|
else
|
|
|
|
fd = BasicOpenFile(fname,
|
|
|
|
O_RDONLY | PG_BINARY,
|
|
|
|
S_IRUSR | S_IWUSR);
|
|
|
|
|
2010-03-01 15:54:00 +01:00
|
|
|
/*
|
2010-07-06 21:19:02 +02:00
|
|
|
* Some OSs don't allow us to open directories at all (Windows returns
|
|
|
|
* EACCES)
|
2010-03-01 01:04:06 +01:00
|
|
|
*/
|
2010-03-01 15:54:00 +01:00
|
|
|
if (fd < 0 && isdir && (errno == EISDIR || errno == EACCES))
|
2010-02-28 22:05:30 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
else if (fd < 0)
|
2010-02-15 01:50:57 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not open file \"%s\": %m", fname)));
|
|
|
|
|
2010-02-28 22:05:30 +01:00
|
|
|
returncode = pg_fsync(fd);
|
2010-07-06 21:19:02 +02:00
|
|
|
|
2010-02-28 22:05:30 +01:00
|
|
|
/* Some OSs don't allow us to fsync directories at all */
|
|
|
|
if (returncode != 0 && isdir && errno == EBADF)
|
|
|
|
{
|
|
|
|
close(fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (returncode != 0)
|
2010-02-15 01:50:57 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not fsync file \"%s\": %m", fname)));
|
2010-02-22 16:29:46 +01:00
|
|
|
|
2010-02-15 01:50:57 +01:00
|
|
|
close(fd);
|
|
|
|
}
|