/*------------------------------------------------------------------------- * * copydir.c * copies a directory * * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * While "xcopy /e /i /q" works fine for copying directories, on Windows XP * it requires a Window handle which prevents it from working when invoked * as a service. * * IDENTIFICATION * $PostgreSQL: pgsql/src/port/copydir.c,v 1.20 2007/07/25 12:22:54 mha Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> #include "storage/fd.h" /* * On Windows, call non-macro versions of palloc; we can't reference * CurrentMemoryContext in this file because of PGDLLIMPORT conflict. */ #if defined(WIN32) || defined(__CYGWIN__) #undef palloc #undef pstrdup #define palloc(sz) pgport_palloc(sz) #define pstrdup(str) pgport_pstrdup(str) #endif static void copy_file(char *fromfile, char *tofile); /* * copydir: copy a directory * * If recurse is false, subdirectories are ignored. Anything that's not * a directory or a regular file is ignored. */ void copydir(char *fromdir, char *todir, bool recurse) { DIR *xldir; struct dirent *xlde; char fromfile[MAXPGPATH]; char tofile[MAXPGPATH]; if (mkdir(todir, S_IRUSR | S_IWUSR | S_IXUSR) != 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 (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 (fst.st_mode & S_IFDIR) { /* recurse to handle subdirectories */ if (recurse) copydir(fromfile, tofile, true); } else if (fst.st_mode & S_IFREG) copy_file(fromfile, tofile); } FreeDir(xldir); } /* * copy one file */ static void copy_file(char *fromfile, char *tofile) { char *buffer; int srcfd; int dstfd; int nbytes; /* Use palloc to ensure we get a maxaligned buffer */ #define COPY_BUF_SIZE (8 * BLCKSZ) buffer = palloc(COPY_BUF_SIZE); /* * 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. */ for (;;) { 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))); } } /* * Be paranoid here to ensure we catch problems. */ if (pg_fsync(dstfd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", tofile))); if (close(dstfd)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tofile))); close(srcfd); pfree(buffer); }