1 /*-------------------------------------------------------------------------
2 *
3 * copydir.c
4 * copies a directory
5 *
6 * Portions Copyright (c) 1996-2016, 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 /*
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
copydir(char * fromdir,char * todir,bool recurse)37 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 if (mkdir(todir, S_IRWXU) != 0)
45 ereport(ERROR,
46 (errcode_for_file_access(),
47 errmsg("could not create directory \"%s\": %m", todir)));
48
49 xldir = AllocateDir(fromdir);
50 if (xldir == NULL)
51 ereport(ERROR,
52 (errcode_for_file_access(),
53 errmsg("could not open directory \"%s\": %m", fromdir)));
54
55 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 CHECK_FOR_INTERRUPTS();
61
62 if (strcmp(xlde->d_name, ".") == 0 ||
63 strcmp(xlde->d_name, "..") == 0)
64 continue;
65
66 snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
67 snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
68
69 if (lstat(fromfile, &fst) < 0)
70 ereport(ERROR,
71 (errcode_for_file_access(),
72 errmsg("could not stat file \"%s\": %m", fromfile)));
73
74 if (S_ISDIR(fst.st_mode))
75 {
76 /* recurse to handle subdirectories */
77 if (recurse)
78 copydir(fromfile, tofile, true);
79 }
80 else if (S_ISREG(fst.st_mode))
81 copy_file(fromfile, tofile);
82 }
83 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 if (!enableFsync)
90 return;
91
92 xldir = AllocateDir(todir);
93 if (xldir == NULL)
94 ereport(ERROR,
95 (errcode_for_file_access(),
96 errmsg("could not open directory \"%s\": %m", todir)));
97
98 while ((xlde = ReadDir(xldir, todir)) != NULL)
99 {
100 struct stat fst;
101
102 if (strcmp(xlde->d_name, ".") == 0 ||
103 strcmp(xlde->d_name, "..") == 0)
104 continue;
105
106 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 if (lstat(tofile, &fst) < 0)
113 ereport(ERROR,
114 (errcode_for_file_access(),
115 errmsg("could not stat file \"%s\": %m", tofile)));
116
117 if (S_ISREG(fst.st_mode))
118 fsync_fname(tofile, false);
119 }
120 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 fsync_fname(todir, true);
129 }
130
131 /*
132 * copy one file
133 */
134 void
copy_file(char * fromfile,char * tofile)135 copy_file(char *fromfile, char *tofile)
136 {
137 char *buffer;
138 int srcfd;
139 int dstfd;
140 int nbytes;
141 off_t offset;
142 off_t flush_offset;
143
144 /* Size of copy buffer (read and write requests) */
145 #define COPY_BUF_SIZE (8 * BLCKSZ)
146
147 /*
148 * Size of data flush requests. It seems beneficial on most platforms to
149 * do this every 1MB or so. But macOS, at least with early releases of
150 * APFS, is really unfriendly to small mmap/msync requests, so there do it
151 * only every 32MB.
152 */
153 #if defined(__darwin__)
154 #define FLUSH_DISTANCE (32 * 1024 * 1024)
155 #else
156 #define FLUSH_DISTANCE (1024 * 1024)
157 #endif
158
159 /* Use palloc to ensure we get a maxaligned buffer */
160 buffer = palloc(COPY_BUF_SIZE);
161
162 /*
163 * Open the files
164 */
165 srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY, 0);
166 if (srcfd < 0)
167 ereport(ERROR,
168 (errcode_for_file_access(),
169 errmsg("could not open file \"%s\": %m", fromfile)));
170
171 dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
172 S_IRUSR | S_IWUSR);
173 if (dstfd < 0)
174 ereport(ERROR,
175 (errcode_for_file_access(),
176 errmsg("could not create file \"%s\": %m", tofile)));
177
178 /*
179 * Do the data copying.
180 */
181 flush_offset = 0;
182 for (offset = 0;; offset += nbytes)
183 {
184 /* If we got a cancel signal during the copy of the file, quit */
185 CHECK_FOR_INTERRUPTS();
186
187 /*
188 * We fsync the files later, but during the copy, flush them every so
189 * often to avoid spamming the cache and hopefully get the kernel to
190 * start writing them out before the fsync comes.
191 */
192 if (offset - flush_offset >= FLUSH_DISTANCE)
193 {
194 pg_flush_data(dstfd, flush_offset, offset - flush_offset);
195 flush_offset = offset;
196 }
197
198 nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
199 if (nbytes < 0)
200 ereport(ERROR,
201 (errcode_for_file_access(),
202 errmsg("could not read file \"%s\": %m", fromfile)));
203 if (nbytes == 0)
204 break;
205 errno = 0;
206 if ((int) write(dstfd, buffer, nbytes) != nbytes)
207 {
208 /* if write didn't set errno, assume problem is no disk space */
209 if (errno == 0)
210 errno = ENOSPC;
211 ereport(ERROR,
212 (errcode_for_file_access(),
213 errmsg("could not write to file \"%s\": %m", tofile)));
214 }
215 }
216
217 if (offset > flush_offset)
218 pg_flush_data(dstfd, flush_offset, offset - flush_offset);
219
220 if (CloseTransientFile(dstfd))
221 ereport(ERROR,
222 (errcode_for_file_access(),
223 errmsg("could not close file \"%s\": %m", tofile)));
224
225 CloseTransientFile(srcfd);
226
227 pfree(buffer);
228 }
229