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