Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * copydir.c
4 : * copies a directory
5 : *
6 : * Portions Copyright (c) 1996-2026, 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 : #ifdef HAVE_COPYFILE_H
22 : #include <copyfile.h>
23 : #endif
24 : #include <fcntl.h>
25 : #include <unistd.h>
26 :
27 : #include "common/file_utils.h"
28 : #include "miscadmin.h"
29 : #include "pgstat.h"
30 : #include "storage/copydir.h"
31 : #include "storage/fd.h"
32 : #include "utils/wait_event.h"
33 :
34 : /* GUCs */
35 : int file_copy_method = FILE_COPY_METHOD_COPY;
36 :
37 : static void clone_file(const char *fromfile, const char *tofile);
38 :
39 : /*
40 : * copydir: copy a directory
41 : *
42 : * If recurse is false, subdirectories are ignored. Anything that's not
43 : * a directory or a regular file is ignored.
44 : *
45 : * This function uses the file_copy_method GUC. New uses of this function must
46 : * be documented in doc/src/sgml/config.sgml.
47 : */
48 : void
49 159 : copydir(const char *fromdir, const char *todir, bool recurse)
50 : {
51 : DIR *xldir;
52 : struct dirent *xlde;
53 : char fromfile[MAXPGPATH * 2];
54 : char tofile[MAXPGPATH * 2];
55 :
56 159 : if (MakePGDirectory(todir) != 0)
57 0 : ereport(ERROR,
58 : (errcode_for_file_access(),
59 : errmsg("could not create directory \"%s\": %m", todir)));
60 :
61 159 : xldir = AllocateDir(fromdir);
62 :
63 51423 : while ((xlde = ReadDir(xldir, fromdir)) != NULL)
64 : {
65 : PGFileType xlde_type;
66 :
67 : /* If we got a cancel signal during the copy of the directory, quit */
68 51264 : CHECK_FOR_INTERRUPTS();
69 :
70 51264 : if (strcmp(xlde->d_name, ".") == 0 ||
71 51105 : strcmp(xlde->d_name, "..") == 0)
72 318 : continue;
73 :
74 50946 : snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
75 50946 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
76 :
77 50946 : xlde_type = get_dirent_type(fromfile, xlde, false, ERROR);
78 :
79 50946 : if (xlde_type == PGFILETYPE_DIR)
80 : {
81 : /* recurse to handle subdirectories */
82 0 : if (recurse)
83 0 : copydir(fromfile, tofile, true);
84 : }
85 50946 : else if (xlde_type == PGFILETYPE_REG)
86 : {
87 50946 : if (file_copy_method == FILE_COPY_METHOD_CLONE)
88 0 : clone_file(fromfile, tofile);
89 : else
90 50946 : copy_file(fromfile, tofile);
91 : }
92 : }
93 159 : FreeDir(xldir);
94 :
95 : /*
96 : * Be paranoid here and fsync all files to ensure the copy is really done.
97 : * But if fsync is disabled, we're done.
98 : */
99 159 : if (!enableFsync)
100 159 : return;
101 :
102 0 : xldir = AllocateDir(todir);
103 :
104 0 : while ((xlde = ReadDir(xldir, todir)) != NULL)
105 : {
106 0 : if (strcmp(xlde->d_name, ".") == 0 ||
107 0 : strcmp(xlde->d_name, "..") == 0)
108 0 : continue;
109 :
110 0 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
111 :
112 : /*
113 : * We don't need to sync subdirectories here since the recursive
114 : * copydir will do it before it returns
115 : */
116 0 : if (get_dirent_type(tofile, xlde, false, ERROR) == PGFILETYPE_REG)
117 0 : fsync_fname(tofile, false);
118 : }
119 0 : FreeDir(xldir);
120 :
121 : /*
122 : * It's important to fsync the destination directory itself as individual
123 : * file fsyncs don't guarantee that the directory entry for the file is
124 : * synced. Recent versions of ext4 have made the window much wider but
125 : * it's been true for ext3 and other filesystems in the past.
126 : */
127 0 : fsync_fname(todir, true);
128 : }
129 :
130 : /*
131 : * copy one file
132 : */
133 : void
134 50955 : copy_file(const char *fromfile, const char *tofile)
135 : {
136 : char *buffer;
137 : int srcfd;
138 : int dstfd;
139 : int nbytes;
140 : off_t offset;
141 : off_t flush_offset;
142 :
143 : /* Size of copy buffer (read and write requests) */
144 : #define COPY_BUF_SIZE (8 * BLCKSZ)
145 :
146 : /*
147 : * Size of data flush requests. It seems beneficial on most platforms to
148 : * do this every 1MB or so. But macOS, at least with early releases of
149 : * APFS, is really unfriendly to small mmap/msync requests, so there do it
150 : * only every 32MB.
151 : */
152 : #if defined(__darwin__)
153 : #define FLUSH_DISTANCE (32 * 1024 * 1024)
154 : #else
155 : #define FLUSH_DISTANCE (1024 * 1024)
156 : #endif
157 :
158 : /* Use palloc to ensure we get a maxaligned buffer */
159 50955 : buffer = palloc(COPY_BUF_SIZE);
160 :
161 : /*
162 : * Open the files
163 : */
164 50955 : srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
165 50955 : if (srcfd < 0)
166 0 : ereport(ERROR,
167 : (errcode_for_file_access(),
168 : errmsg("could not open file \"%s\": %m", fromfile)));
169 :
170 50955 : dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
171 50955 : if (dstfd < 0)
172 0 : ereport(ERROR,
173 : (errcode_for_file_access(),
174 : errmsg("could not create file \"%s\": %m", tofile)));
175 :
176 : /*
177 : * Do the data copying.
178 : */
179 50955 : flush_offset = 0;
180 50955 : for (offset = 0;; offset += nbytes)
181 : {
182 : /* If we got a cancel signal during the copy of the file, quit */
183 101423 : CHECK_FOR_INTERRUPTS();
184 :
185 : /*
186 : * We fsync the files later, but during the copy, flush them every so
187 : * often to avoid spamming the cache and hopefully get the kernel to
188 : * start writing them out before the fsync comes.
189 : */
190 101423 : if (offset - flush_offset >= FLUSH_DISTANCE)
191 : {
192 32 : pg_flush_data(dstfd, flush_offset, offset - flush_offset);
193 32 : flush_offset = offset;
194 : }
195 :
196 101423 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
197 101423 : nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
198 101423 : pgstat_report_wait_end();
199 101423 : if (nbytes < 0)
200 0 : ereport(ERROR,
201 : (errcode_for_file_access(),
202 : errmsg("could not read file \"%s\": %m", fromfile)));
203 101423 : if (nbytes == 0)
204 50955 : break;
205 50468 : errno = 0;
206 50468 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
207 50468 : if ((int) write(dstfd, buffer, nbytes) != nbytes)
208 : {
209 : /* if write didn't set errno, assume problem is no disk space */
210 0 : if (errno == 0)
211 0 : errno = ENOSPC;
212 0 : ereport(ERROR,
213 : (errcode_for_file_access(),
214 : errmsg("could not write to file \"%s\": %m", tofile)));
215 : }
216 50468 : pgstat_report_wait_end();
217 : }
218 :
219 50955 : if (offset > flush_offset)
220 41566 : pg_flush_data(dstfd, flush_offset, offset - flush_offset);
221 :
222 50955 : if (CloseTransientFile(dstfd) != 0)
223 0 : ereport(ERROR,
224 : (errcode_for_file_access(),
225 : errmsg("could not close file \"%s\": %m", tofile)));
226 :
227 50955 : if (CloseTransientFile(srcfd) != 0)
228 0 : ereport(ERROR,
229 : (errcode_for_file_access(),
230 : errmsg("could not close file \"%s\": %m", fromfile)));
231 :
232 50955 : pfree(buffer);
233 50955 : }
234 :
235 : /*
236 : * clone one file
237 : */
238 : static void
239 0 : clone_file(const char *fromfile, const char *tofile)
240 : {
241 : #if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
242 : if (copyfile(fromfile, tofile, NULL, COPYFILE_CLONE_FORCE) < 0)
243 : ereport(ERROR,
244 : (errcode_for_file_access(),
245 : errmsg("could not clone file \"%s\" to \"%s\": %m",
246 : fromfile, tofile)));
247 : #elif defined(HAVE_COPY_FILE_RANGE)
248 : int srcfd;
249 : int dstfd;
250 : ssize_t nbytes;
251 :
252 0 : srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
253 0 : if (srcfd < 0)
254 0 : ereport(ERROR,
255 : (errcode_for_file_access(),
256 : errmsg("could not open file \"%s\": %m", fromfile)));
257 :
258 0 : dstfd = OpenTransientFile(tofile, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY);
259 0 : if (dstfd < 0)
260 0 : ereport(ERROR,
261 : (errcode_for_file_access(),
262 : errmsg("could not create file \"%s\": %m", tofile)));
263 :
264 : do
265 : {
266 : /*
267 : * Don't copy too much at once, so we can check for interrupts from
268 : * time to time if it falls back to a slow copy.
269 : */
270 0 : CHECK_FOR_INTERRUPTS();
271 0 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_COPY);
272 0 : nbytes = copy_file_range(srcfd, NULL, dstfd, NULL, 1024 * 1024, 0);
273 0 : if (nbytes < 0 && errno != EINTR)
274 0 : ereport(ERROR,
275 : (errcode_for_file_access(),
276 : errmsg("could not clone file \"%s\" to \"%s\": %m",
277 : fromfile, tofile)));
278 0 : pgstat_report_wait_end();
279 : }
280 0 : while (nbytes != 0);
281 :
282 0 : if (CloseTransientFile(dstfd) != 0)
283 0 : ereport(ERROR,
284 : (errcode_for_file_access(),
285 : errmsg("could not close file \"%s\": %m", tofile)));
286 :
287 0 : if (CloseTransientFile(srcfd) != 0)
288 0 : ereport(ERROR,
289 : (errcode_for_file_access(),
290 : errmsg("could not close file \"%s\": %m", fromfile)));
291 : #else
292 : /* If there is no CLONE support this function should not be called. */
293 : pg_unreachable();
294 : #endif
295 0 : }
|