Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * copydir.c
4 : * copies a directory
5 : *
6 : * Portions Copyright (c) 1996-2021, 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 "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 1066 : 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 1066 : if (MakePGDirectory(todir) != 0)
45 0 : ereport(ERROR,
46 : (errcode_for_file_access(),
47 : errmsg("could not create directory \"%s\": %m", todir)));
48 :
49 1066 : xldir = AllocateDir(fromdir);
50 :
51 312342 : 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 311276 : CHECK_FOR_INTERRUPTS();
57 :
58 311276 : if (strcmp(xlde->d_name, ".") == 0 ||
59 310210 : strcmp(xlde->d_name, "..") == 0)
60 2132 : continue;
61 :
62 309144 : snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
63 309144 : snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
64 :
65 309144 : if (lstat(fromfile, &fst) < 0)
66 0 : ereport(ERROR,
67 : (errcode_for_file_access(),
68 : errmsg("could not stat file \"%s\": %m", fromfile)));
69 :
70 309144 : if (S_ISDIR(fst.st_mode))
71 : {
72 : /* recurse to handle subdirectories */
73 0 : if (recurse)
74 0 : copydir(fromfile, tofile, true);
75 : }
76 309144 : else if (S_ISREG(fst.st_mode))
77 309144 : copy_file(fromfile, tofile);
78 : }
79 1066 : 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 1066 : if (!enableFsync)
86 1066 : return;
87 :
88 0 : xldir = AllocateDir(todir);
89 :
90 0 : while ((xlde = ReadDir(xldir, todir)) != NULL)
91 : {
92 : struct stat fst;
93 :
94 0 : if (strcmp(xlde->d_name, ".") == 0 ||
95 0 : strcmp(xlde->d_name, "..") == 0)
96 0 : continue;
97 :
98 0 : 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 0 : if (lstat(tofile, &fst) < 0)
105 0 : ereport(ERROR,
106 : (errcode_for_file_access(),
107 : errmsg("could not stat file \"%s\": %m", tofile)));
108 :
109 0 : if (S_ISREG(fst.st_mode))
110 0 : fsync_fname(tofile, false);
111 : }
112 0 : 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 0 : fsync_fname(todir, true);
121 : }
122 :
123 : /*
124 : * copy one file
125 : */
126 : void
127 309148 : 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 309148 : buffer = palloc(COPY_BUF_SIZE);
153 :
154 : /*
155 : * Open the files
156 : */
157 309148 : srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
158 309148 : if (srcfd < 0)
159 0 : ereport(ERROR,
160 : (errcode_for_file_access(),
161 : errmsg("could not open file \"%s\": %m", fromfile)));
162 :
163 309148 : dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
164 309148 : if (dstfd < 0)
165 0 : 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 309148 : flush_offset = 0;
173 309148 : for (offset = 0;; offset += nbytes)
174 : {
175 : /* If we got a cancel signal during the copy of the file, quit */
176 625762 : 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 625762 : if (offset - flush_offset >= FLUSH_DISTANCE)
184 : {
185 0 : pg_flush_data(dstfd, flush_offset, offset - flush_offset);
186 0 : flush_offset = offset;
187 : }
188 :
189 625762 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
190 625762 : nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
191 625762 : pgstat_report_wait_end();
192 625762 : if (nbytes < 0)
193 0 : ereport(ERROR,
194 : (errcode_for_file_access(),
195 : errmsg("could not read file \"%s\": %m", fromfile)));
196 625762 : if (nbytes == 0)
197 309148 : break;
198 316614 : errno = 0;
199 316614 : pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
200 316614 : if ((int) write(dstfd, buffer, nbytes) != nbytes)
201 : {
202 : /* if write didn't set errno, assume problem is no disk space */
203 0 : if (errno == 0)
204 0 : errno = ENOSPC;
205 0 : ereport(ERROR,
206 : (errcode_for_file_access(),
207 : errmsg("could not write to file \"%s\": %m", tofile)));
208 : }
209 316614 : pgstat_report_wait_end();
210 : }
211 :
212 309148 : if (offset > flush_offset)
213 255844 : pg_flush_data(dstfd, flush_offset, offset - flush_offset);
214 :
215 309148 : if (CloseTransientFile(dstfd) != 0)
216 0 : ereport(ERROR,
217 : (errcode_for_file_access(),
218 : errmsg("could not close file \"%s\": %m", tofile)));
219 :
220 309148 : if (CloseTransientFile(srcfd) != 0)
221 0 : ereport(ERROR,
222 : (errcode_for_file_access(),
223 : errmsg("could not close file \"%s\": %m", fromfile)));
224 :
225 309148 : pfree(buffer);
226 309148 : }
|