~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/storage/file/copydir.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * copydir.c
 
4
 *        copies a directory
 
5
 *
 
6
 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994, Regents of the University of California
 
8
 *
 
9
 *      While "xcopy /e /i /q" works fine for copying directories, on Windows XP
 
10
 *      it requires a Window handle which prevents it from working when invoked
 
11
 *      as a service.
 
12
 *
 
13
 * IDENTIFICATION
 
14
 *        src/backend/storage/file/copydir.c
 
15
 *
 
16
 *-------------------------------------------------------------------------
 
17
 */
 
18
 
 
19
#include "postgres.h"
 
20
 
 
21
#include <fcntl.h>
 
22
#include <unistd.h>
 
23
#include <sys/stat.h>
 
24
 
 
25
#include "storage/copydir.h"
 
26
#include "storage/fd.h"
 
27
#include "miscadmin.h"
 
28
 
 
29
/*
 
30
 *      On Windows, call non-macro versions of palloc; we can't reference
 
31
 *      CurrentMemoryContext in this file because of PGDLLIMPORT conflict.
 
32
 */
 
33
#if defined(WIN32) || defined(__CYGWIN__)
 
34
#undef palloc
 
35
#undef pstrdup
 
36
#define palloc(sz)              pgport_palloc(sz)
 
37
#define pstrdup(str)    pgport_pstrdup(str)
 
38
#endif
 
39
 
 
40
 
 
41
static void fsync_fname(char *fname, bool isdir);
 
42
 
 
43
 
 
44
/*
 
45
 * copydir: copy a directory
 
46
 *
 
47
 * If recurse is false, subdirectories are ignored.  Anything that's not
 
48
 * a directory or a regular file is ignored.
 
49
 */
 
50
void
 
51
copydir(char *fromdir, char *todir, bool recurse)
 
52
{
 
53
        DIR                *xldir;
 
54
        struct dirent *xlde;
 
55
        char            fromfile[MAXPGPATH];
 
56
        char            tofile[MAXPGPATH];
 
57
 
 
58
        if (mkdir(todir, S_IRWXU) != 0)
 
59
                ereport(ERROR,
 
60
                                (errcode_for_file_access(),
 
61
                                 errmsg("could not create directory \"%s\": %m", todir)));
 
62
 
 
63
        xldir = AllocateDir(fromdir);
 
64
        if (xldir == NULL)
 
65
                ereport(ERROR,
 
66
                                (errcode_for_file_access(),
 
67
                                 errmsg("could not open directory \"%s\": %m", fromdir)));
 
68
 
 
69
        while ((xlde = ReadDir(xldir, fromdir)) != NULL)
 
70
        {
 
71
                struct stat fst;
 
72
 
 
73
                /* If we got a cancel signal during the copy of the directory, quit */
 
74
                CHECK_FOR_INTERRUPTS();
 
75
 
 
76
                if (strcmp(xlde->d_name, ".") == 0 ||
 
77
                        strcmp(xlde->d_name, "..") == 0)
 
78
                        continue;
 
79
 
 
80
                snprintf(fromfile, MAXPGPATH, "%s/%s", fromdir, xlde->d_name);
 
81
                snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
 
82
 
 
83
                if (lstat(fromfile, &fst) < 0)
 
84
                        ereport(ERROR,
 
85
                                        (errcode_for_file_access(),
 
86
                                         errmsg("could not stat file \"%s\": %m", fromfile)));
 
87
 
 
88
                if (S_ISDIR(fst.st_mode))
 
89
                {
 
90
                        /* recurse to handle subdirectories */
 
91
                        if (recurse)
 
92
                                copydir(fromfile, tofile, true);
 
93
                }
 
94
                else if (S_ISREG(fst.st_mode))
 
95
                        copy_file(fromfile, tofile);
 
96
        }
 
97
        FreeDir(xldir);
 
98
 
 
99
        /*
 
100
         * Be paranoid here and fsync all files to ensure the copy is really done.
 
101
         */
 
102
        xldir = AllocateDir(todir);
 
103
        if (xldir == NULL)
 
104
                ereport(ERROR,
 
105
                                (errcode_for_file_access(),
 
106
                                 errmsg("could not open directory \"%s\": %m", todir)));
 
107
 
 
108
        while ((xlde = ReadDir(xldir, todir)) != NULL)
 
109
        {
 
110
                struct stat fst;
 
111
 
 
112
                if (strcmp(xlde->d_name, ".") == 0 ||
 
113
                        strcmp(xlde->d_name, "..") == 0)
 
114
                        continue;
 
115
 
 
116
                snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
 
117
 
 
118
                /*
 
119
                 * We don't need to sync subdirectories here since the recursive
 
120
                 * copydir will do it before it returns
 
121
                 */
 
122
                if (lstat(tofile, &fst) < 0)
 
123
                        ereport(ERROR,
 
124
                                        (errcode_for_file_access(),
 
125
                                         errmsg("could not stat file \"%s\": %m", tofile)));
 
126
 
 
127
                if (S_ISREG(fst.st_mode))
 
128
                        fsync_fname(tofile, false);
 
129
        }
 
130
        FreeDir(xldir);
 
131
 
 
132
        /*
 
133
         * It's important to fsync the destination directory itself as individual
 
134
         * file fsyncs don't guarantee that the directory entry for the file is
 
135
         * synced. Recent versions of ext4 have made the window much wider but
 
136
         * it's been true for ext3 and other filesystems in the past.
 
137
         */
 
138
        fsync_fname(todir, true);
 
139
}
 
140
 
 
141
/*
 
142
 * copy one file
 
143
 */
 
144
void
 
145
copy_file(char *fromfile, char *tofile)
 
146
{
 
147
        char       *buffer;
 
148
        int                     srcfd;
 
149
        int                     dstfd;
 
150
        int                     nbytes;
 
151
        off_t           offset;
 
152
 
 
153
        /* Use palloc to ensure we get a maxaligned buffer */
 
154
#define COPY_BUF_SIZE (8 * BLCKSZ)
 
155
 
 
156
        buffer = palloc(COPY_BUF_SIZE);
 
157
 
 
158
        /*
 
159
         * Open the files
 
160
         */
 
161
        srcfd = BasicOpenFile(fromfile, O_RDONLY | PG_BINARY, 0);
 
162
        if (srcfd < 0)
 
163
                ereport(ERROR,
 
164
                                (errcode_for_file_access(),
 
165
                                 errmsg("could not open file \"%s\": %m", fromfile)));
 
166
 
 
167
        dstfd = BasicOpenFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
 
168
                                                  S_IRUSR | S_IWUSR);
 
169
        if (dstfd < 0)
 
170
                ereport(ERROR,
 
171
                                (errcode_for_file_access(),
 
172
                                 errmsg("could not create file \"%s\": %m", tofile)));
 
173
 
 
174
        /*
 
175
         * Do the data copying.
 
176
         */
 
177
        for (offset = 0;; offset += nbytes)
 
178
        {
 
179
                /* If we got a cancel signal during the copy of the file, quit */
 
180
                CHECK_FOR_INTERRUPTS();
 
181
 
 
182
                nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
 
183
                if (nbytes < 0)
 
184
                        ereport(ERROR,
 
185
                                        (errcode_for_file_access(),
 
186
                                         errmsg("could not read file \"%s\": %m", fromfile)));
 
187
                if (nbytes == 0)
 
188
                        break;
 
189
                errno = 0;
 
190
                if ((int) write(dstfd, buffer, nbytes) != nbytes)
 
191
                {
 
192
                        /* if write didn't set errno, assume problem is no disk space */
 
193
                        if (errno == 0)
 
194
                                errno = ENOSPC;
 
195
                        ereport(ERROR,
 
196
                                        (errcode_for_file_access(),
 
197
                                         errmsg("could not write to file \"%s\": %m", tofile)));
 
198
                }
 
199
 
 
200
                /*
 
201
                 * We fsync the files later but first flush them to avoid spamming the
 
202
                 * cache and hopefully get the kernel to start writing them out before
 
203
                 * the fsync comes.
 
204
                 */
 
205
                pg_flush_data(dstfd, offset, nbytes);
 
206
        }
 
207
 
 
208
        if (close(dstfd))
 
209
                ereport(ERROR,
 
210
                                (errcode_for_file_access(),
 
211
                                 errmsg("could not close file \"%s\": %m", tofile)));
 
212
 
 
213
        close(srcfd);
 
214
 
 
215
        pfree(buffer);
 
216
}
 
217
 
 
218
 
 
219
/*
 
220
 * fsync a file
 
221
 *
 
222
 * Try to fsync directories but ignore errors that indicate the OS
 
223
 * just doesn't allow/require fsyncing directories.
 
224
 */
 
225
static void
 
226
fsync_fname(char *fname, bool isdir)
 
227
{
 
228
        int                     fd;
 
229
        int                     returncode;
 
230
 
 
231
        /*
 
232
         * Some OSs require directories to be opened read-only whereas other
 
233
         * systems don't allow us to fsync files opened read-only; so we need both
 
234
         * cases here
 
235
         */
 
236
        if (!isdir)
 
237
                fd = BasicOpenFile(fname,
 
238
                                                   O_RDWR | PG_BINARY,
 
239
                                                   S_IRUSR | S_IWUSR);
 
240
        else
 
241
                fd = BasicOpenFile(fname,
 
242
                                                   O_RDONLY | PG_BINARY,
 
243
                                                   S_IRUSR | S_IWUSR);
 
244
 
 
245
        /*
 
246
         * Some OSs don't allow us to open directories at all (Windows returns
 
247
         * EACCES)
 
248
         */
 
249
        if (fd < 0 && isdir && (errno == EISDIR || errno == EACCES))
 
250
                return;
 
251
 
 
252
        else if (fd < 0)
 
253
                ereport(ERROR,
 
254
                                (errcode_for_file_access(),
 
255
                                 errmsg("could not open file \"%s\": %m", fname)));
 
256
 
 
257
        returncode = pg_fsync(fd);
 
258
 
 
259
        /* Some OSs don't allow us to fsync directories at all */
 
260
        if (returncode != 0 && isdir && errno == EBADF)
 
261
        {
 
262
                close(fd);
 
263
                return;
 
264
        }
 
265
 
 
266
        if (returncode != 0)
 
267
                ereport(ERROR,
 
268
                                (errcode_for_file_access(),
 
269
                                 errmsg("could not fsync file \"%s\": %m", fname)));
 
270
 
 
271
        close(fd);
 
272
}