Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * copydir.c
4 : * copies a directory
5 : *
6 : * Portions Copyright (c) 1996-2017, 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 : #include "pgstat.h"
29 :
30 : /*
31 : * copydir: copy a directory
32 : *
33 : * If recurse is false, subdirectories are ignored. Anything that's not
34 : * a directory or a regular file is ignored.
35 : */
36 : void
37 3 : copydir(char *fromdir, char *todir, bool recurse)
38 : {
39 : DIR *xldir;
40 : struct dirent *xlde;
41 : char fromfile[MAXPGPATH * 2];
42 : char tofile[MAXPGPATH * 2];
43 :
44 3 : if (mkdir(todir, S_IRWXU) != 0)
45 0 : ereport(ERROR,
46 : (errcode_for_file_access(),
47 : errmsg("could not create directory \"%s\": %m", todir)));
48 :
49 3 : xldir = AllocateDir(fromdir);
50 3 : if (xldir == NULL)
51 0 : ereport(ERROR,
52 : (errcode_for_file_access(),
53 : errmsg("could not open directory \"%s\": %m", fromdir)));
54 :
55 891 : while ((xlde = ReadDir(xldir, fromdir)) != NULL)
56 : {
57 : struct stat fst;
58 :
59 : /* If we got a cancel signal during the copy of the directory, quit */
60 885 : CHECK_FOR_INTERRUPTS();
61 :
62 1767 : if (strcmp(xlde->d_name, ".") == 0 ||
63 882 : strcmp(xlde->d_name, "..") == 0)
64 6 : continue;
65 :
66 879 : snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
67 879 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
68 :
69 879 : if (lstat(fromfile, &fst) < 0)
70 0 : ereport(ERROR,
71 : (errcode_for_file_access(),
72 : errmsg("could not stat file \"%s\": %m", fromfile)));
73 :
74 879 : if (S_ISDIR(fst.st_mode))
75 : {
76 : /* recurse to handle subdirectories */
77 0 : if (recurse)
78 0 : copydir(fromfile, tofile, true);
79 : }
80 879 : else if (S_ISREG(fst.st_mode))
81 879 : copy_file(fromfile, tofile);
82 : }
83 3 : FreeDir(xldir);
84 :
85 : /*
86 : * Be paranoid here and fsync all files to ensure the copy is really done.
87 : * But if fsync is disabled, we're done.
88 : */
89 3 : if (!enableFsync)
90 6 : return;
91 :
92 0 : xldir = AllocateDir(todir);
93 0 : if (xldir == NULL)
94 0 : ereport(ERROR,
95 : (errcode_for_file_access(),
96 : errmsg("could not open directory \"%s\": %m", todir)));
97 :
98 0 : while ((xlde = ReadDir(xldir, todir)) != NULL)
99 : {
100 : struct stat fst;
101 :
102 0 : if (strcmp(xlde->d_name, ".") == 0 ||
103 0 : strcmp(xlde->d_name, "..") == 0)
104 0 : continue;
105 :
106 0 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
107 :
108 : /*
109 : * We don't need to sync subdirectories here since the recursive
110 : * copydir will do it before it returns
111 : */
112 0 : if (lstat(tofile, &fst) < 0)
113 0 : ereport(ERROR,
114 : (errcode_for_file_access(),
115 : errmsg("could not stat file \"%s\": %m", tofile)));
116 :
117 0 : if (S_ISREG(fst.st_mode))
118 0 : fsync_fname(tofile, false);
119 : }
120 0 : FreeDir(xldir);
121 :
122 : /*
123 : * It's important to fsync the destination directory itself as individual
124 : * file fsyncs don't guarantee that the directory entry for the file is
125 : * synced. Recent versions of ext4 have made the window much wider but
126 : * it's been true for ext3 and other filesystems in the past.
127 : */
128 0 : fsync_fname(todir, true);
129 : }
130 :
131 : /*
132 : * copy one file
133 : */
134 : void
135 879 : copy_file(char *fromfile, char *tofile)
136 : {
137 : char *buffer;
138 : int srcfd;
139 : int dstfd;
140 : int nbytes;
141 : off_t offset;
142 :
143 : /* Use palloc to ensure we get a maxaligned buffer */
144 : #define COPY_BUF_SIZE (8 * BLCKSZ)
145 :
146 879 : buffer = palloc(COPY_BUF_SIZE);
147 :
148 : /*
149 : * Open the files
150 : */
151 879 : srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY, 0);
152 879 : if (srcfd < 0)
153 0 : ereport(ERROR,
154 : (errcode_for_file_access(),
155 : errmsg("could not open file \"%s\": %m", fromfile)));
156 :
157 879 : dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
158 : S_IRUSR | S_IWUSR);
159 879 : if (dstfd < 0)
160 0 : ereport(ERROR,
161 : (errcode_for_file_access(),
162 : errmsg("could not create file \"%s\": %m", tofile)));
163 :
164 : /*
165 : * Do the data copying.
166 : */
167 1719 : for (offset = 0;; offset += nbytes)
168 : {
169 : /* If we got a cancel signal during the copy of the file, quit */
170 1719 : CHECK_FOR_INTERRUPTS();
171 :
172 1719 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
173 1719 : nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
174 1719 : pgstat_report_wait_end();
175 1719 : if (nbytes < 0)
176 0 : ereport(ERROR,
177 : (errcode_for_file_access(),
178 : errmsg("could not read file \"%s\": %m", fromfile)));
179 1719 : if (nbytes == 0)
180 879 : break;
181 840 : errno = 0;
182 840 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
183 840 : if ((int) write(dstfd, buffer, nbytes) != nbytes)
184 : {
185 0 : pgstat_report_wait_end();
186 : /* if write didn't set errno, assume problem is no disk space */
187 0 : if (errno == 0)
188 0 : errno = ENOSPC;
189 0 : ereport(ERROR,
190 : (errcode_for_file_access(),
191 : errmsg("could not write to file \"%s\": %m", tofile)));
192 : }
193 840 : pgstat_report_wait_end();
194 :
195 : /*
196 : * We fsync the files later but first flush them to avoid spamming the
197 : * cache and hopefully get the kernel to start writing them out before
198 : * the fsync comes.
199 : */
200 840 : pg_flush_data(dstfd, offset, nbytes);
201 840 : }
202 :
203 879 : if (CloseTransientFile(dstfd))
204 0 : ereport(ERROR,
205 : (errcode_for_file_access(),
206 : errmsg("could not close file \"%s\": %m", tofile)));
207 :
208 879 : CloseTransientFile(srcfd);
209 :
210 879 : pfree(buffer);
211 879 : }
|