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