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