Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * copydir.c
4 : * copies a directory
5 : *
6 : * Portions Copyright (c) 1996-2024, 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 236 : 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 236 : if (MakePGDirectory(todir) != 0)
45 0 : ereport(ERROR,
46 : (errcode_for_file_access(),
47 : errmsg("could not create directory \"%s\": %m", todir)));
48 :
49 236 : xldir = AllocateDir(fromdir);
50 :
51 71600 : 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 71364 : CHECK_FOR_INTERRUPTS();
57 :
58 71364 : if (strcmp(xlde->d_name, ".") == 0 ||
59 71128 : strcmp(xlde->d_name, "..") == 0)
60 472 : continue;
61 :
62 70892 : snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
63 70892 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
64 :
65 70892 : xlde_type = get_dirent_type(fromfile, xlde, false, ERROR);
66 :
67 70892 : if (xlde_type == PGFILETYPE_DIR)
68 : {
69 : /* recurse to handle subdirectories */
70 0 : if (recurse)
71 0 : copydir(fromfile, tofile, true);
72 : }
73 70892 : else if (xlde_type == PGFILETYPE_REG)
74 70892 : copy_file(fromfile, tofile);
75 : }
76 236 : 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 236 : if (!enableFsync)
83 236 : 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 70910 : 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 70910 : buffer = palloc(COPY_BUF_SIZE);
143 :
144 : /*
145 : * Open the files
146 : */
147 70910 : srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
148 70910 : if (srcfd < 0)
149 0 : ereport(ERROR,
150 : (errcode_for_file_access(),
151 : errmsg("could not open file \"%s\": %m", fromfile)));
152 :
153 70910 : dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
154 70910 : 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 70910 : flush_offset = 0;
163 142312 : for (offset = 0;; offset += nbytes)
164 : {
165 : /* If we got a cancel signal during the copy of the file, quit */
166 142312 : 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 142312 : 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 142312 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
180 142312 : nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
181 142312 : pgstat_report_wait_end();
182 142312 : if (nbytes < 0)
183 0 : ereport(ERROR,
184 : (errcode_for_file_access(),
185 : errmsg("could not read file \"%s\": %m", fromfile)));
186 142312 : if (nbytes == 0)
187 70910 : break;
188 71402 : errno = 0;
189 71402 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
190 71402 : 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 71402 : pgstat_report_wait_end();
200 : }
201 :
202 70910 : if (offset > flush_offset)
203 58638 : pg_flush_data(dstfd, flush_offset, offset - flush_offset);
204 :
205 70910 : if (CloseTransientFile(dstfd) != 0)
206 0 : ereport(ERROR,
207 : (errcode_for_file_access(),
208 : errmsg("could not close file \"%s\": %m", tofile)));
209 :
210 70910 : if (CloseTransientFile(srcfd) != 0)
211 0 : ereport(ERROR,
212 : (errcode_for_file_access(),
213 : errmsg("could not close file \"%s\": %m", fromfile)));
214 :
215 70910 : pfree(buffer);
216 70910 : }
|