Branch data Line data Source code
1 : : /*
2 : : * file.c
3 : : *
4 : : * file system operations
5 : : *
6 : : * Copyright (c) 2010-2026, PostgreSQL Global Development Group
7 : : * src/bin/pg_upgrade/file.c
8 : : */
9 : :
10 : : #include "postgres_fe.h"
11 : :
12 : : #include <sys/stat.h>
13 : : #include <limits.h>
14 : : #include <fcntl.h>
15 : : #ifdef HAVE_COPYFILE_H
16 : : #include <copyfile.h>
17 : : #endif
18 : : #ifdef __linux__
19 : : #include <sys/ioctl.h>
20 : : #include <linux/fs.h>
21 : : #endif
22 : :
23 : : #include "common/file_perm.h"
24 : : #include "pg_upgrade.h"
25 : :
26 : :
27 : : /*
28 : : * cloneFile()
29 : : *
30 : : * Clones/reflinks a relation file from src to dst.
31 : : *
32 : : * schemaName/relName are relation's SQL name (used for error messages only).
33 : : */
34 : : void
35 : 0 : cloneFile(const char *src, const char *dst,
36 : : const char *schemaName, const char *relName)
37 : : {
38 : : #if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
39 : : if (copyfile(src, dst, NULL, COPYFILE_CLONE_FORCE) < 0)
40 : : pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %m",
41 : : schemaName, relName, src, dst);
42 : : #elif defined(__linux__) && defined(FICLONE)
43 : : int src_fd;
44 : : int dest_fd;
45 : :
46 [ # # ]: 0 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
47 : 0 : pg_fatal("error while cloning relation \"%s.%s\": could not open file \"%s\": %m",
48 : : schemaName, relName, src);
49 : :
50 [ # # ]: 0 : if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
51 : : pg_file_create_mode)) < 0)
52 : 0 : pg_fatal("error while cloning relation \"%s.%s\": could not create file \"%s\": %m",
53 : : schemaName, relName, dst);
54 : :
55 [ # # ]: 0 : if (ioctl(dest_fd, FICLONE, src_fd) < 0)
56 : : {
57 : 0 : int save_errno = errno;
58 : :
59 : 0 : unlink(dst);
60 : :
61 : 0 : pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %s",
62 : : schemaName, relName, src, dst, strerror(save_errno));
63 : : }
64 : :
65 : 0 : close(src_fd);
66 : 0 : close(dest_fd);
67 : : #endif
68 : 0 : }
69 : :
70 : :
71 : : /*
72 : : * copyFile()
73 : : *
74 : : * Copies a relation file from src to dst.
75 : : * schemaName/relName are relation's SQL name (used for error messages only).
76 : : */
77 : : void
78 : 2010 : copyFile(const char *src, const char *dst,
79 : : const char *schemaName, const char *relName)
80 : : {
81 : : #ifndef WIN32
82 : : int src_fd;
83 : : int dest_fd;
84 : : char *buffer;
85 : :
86 [ - + ]: 2010 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
87 : 0 : pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
88 : : schemaName, relName, src);
89 : :
90 [ - + ]: 2010 : if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
91 : : pg_file_create_mode)) < 0)
92 : 0 : pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
93 : : schemaName, relName, dst);
94 : :
95 : : /* copy in fairly large chunks for best efficiency */
96 : : #define COPY_BUF_SIZE (50 * BLCKSZ)
97 : :
98 : 2010 : buffer = (char *) pg_malloc(COPY_BUF_SIZE);
99 : :
100 : : /* perform data copying i.e read src source, write to destination */
101 : : while (true)
102 : 1585 : {
103 : 3595 : ssize_t nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
104 : :
105 [ - + ]: 3595 : if (nbytes < 0)
106 : 0 : pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
107 : : schemaName, relName, src);
108 : :
109 [ + + ]: 3595 : if (nbytes == 0)
110 : 2010 : break;
111 : :
112 : 1585 : errno = 0;
113 [ - + ]: 1585 : if (write(dest_fd, buffer, nbytes) != nbytes)
114 : : {
115 : : /* if write didn't set errno, assume problem is no disk space */
116 [ # # ]: 0 : if (errno == 0)
117 : 0 : errno = ENOSPC;
118 : 0 : pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
119 : : schemaName, relName, dst);
120 : : }
121 : : }
122 : :
123 : 2010 : pg_free(buffer);
124 : 2010 : close(src_fd);
125 : 2010 : close(dest_fd);
126 : :
127 : : #else /* WIN32 */
128 : :
129 : : if (CopyFile(src, dst, true) == 0)
130 : : {
131 : : _dosmaperr(GetLastError());
132 : : pg_fatal("error while copying relation \"%s.%s\" (\"%s\" to \"%s\"): %m",
133 : : schemaName, relName, src, dst);
134 : : }
135 : :
136 : : #endif /* WIN32 */
137 : 2010 : }
138 : :
139 : :
140 : : /*
141 : : * copyFileByRange()
142 : : *
143 : : * Copies a relation file from src to dst.
144 : : * schemaName/relName are relation's SQL name (used for error messages only).
145 : : */
146 : : void
147 : 20 : copyFileByRange(const char *src, const char *dst,
148 : : const char *schemaName, const char *relName)
149 : : {
150 : : #ifdef HAVE_COPY_FILE_RANGE
151 : : int src_fd;
152 : : int dest_fd;
153 : : ssize_t nbytes;
154 : :
155 [ - + ]: 20 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
156 : 0 : pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
157 : : schemaName, relName, src);
158 : :
159 [ - + ]: 20 : if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
160 : : pg_file_create_mode)) < 0)
161 : 0 : pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
162 : : schemaName, relName, dst);
163 : :
164 : : do
165 : : {
166 : 34 : nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
167 [ - + ]: 34 : if (nbytes < 0)
168 : 0 : pg_fatal("error while copying relation \"%s.%s\": could not copy file range from \"%s\" to \"%s\": %m",
169 : : schemaName, relName, src, dst);
170 : : }
171 [ + + ]: 34 : while (nbytes > 0);
172 : :
173 : 20 : close(src_fd);
174 : 20 : close(dest_fd);
175 : : #endif
176 : 20 : }
177 : :
178 : :
179 : : /*
180 : : * linkFile()
181 : : *
182 : : * Hard-links a relation file from src to dst.
183 : : * schemaName/relName are relation's SQL name (used for error messages only).
184 : : */
185 : : void
186 : 20 : linkFile(const char *src, const char *dst,
187 : : const char *schemaName, const char *relName)
188 : : {
189 [ - + ]: 20 : if (link(src, dst) < 0)
190 : 0 : pg_fatal("error while creating link for relation \"%s.%s\" (\"%s\" to \"%s\"): %m",
191 : : schemaName, relName, src, dst);
192 : 20 : }
193 : :
194 : :
195 : : void
196 : 1 : check_file_clone(void)
197 : : {
198 : : char existing_file[MAXPGPATH];
199 : : char new_link_file[MAXPGPATH];
200 : :
201 : 1 : snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
202 : 1 : snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.clonetest", new_cluster.pgdata);
203 : 1 : unlink(new_link_file); /* might fail */
204 : :
205 : : #if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
206 : : if (copyfile(existing_file, new_link_file, NULL, COPYFILE_CLONE_FORCE) < 0)
207 : : pg_fatal("could not clone file between old and new data directories: %m");
208 : : #elif defined(__linux__) && defined(FICLONE)
209 : : {
210 : : int src_fd;
211 : : int dest_fd;
212 : :
213 [ - + ]: 1 : if ((src_fd = open(existing_file, O_RDONLY | PG_BINARY, 0)) < 0)
214 : 0 : pg_fatal("could not open file \"%s\": %m",
215 : : existing_file);
216 : :
217 [ - + ]: 1 : if ((dest_fd = open(new_link_file, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
218 : : pg_file_create_mode)) < 0)
219 : 0 : pg_fatal("could not create file \"%s\": %m",
220 : : new_link_file);
221 : :
222 [ + - ]: 1 : if (ioctl(dest_fd, FICLONE, src_fd) < 0)
223 : 1 : pg_fatal("could not clone file between old and new data directories: %m");
224 : :
225 : 0 : close(src_fd);
226 : 0 : close(dest_fd);
227 : : }
228 : : #else
229 : : pg_fatal("file cloning not supported on this platform");
230 : : #endif
231 : :
232 : 0 : unlink(new_link_file);
233 : 0 : }
234 : :
235 : : void
236 : 1 : check_copy_file_range(void)
237 : : {
238 : : char existing_file[MAXPGPATH];
239 : : char new_link_file[MAXPGPATH];
240 : :
241 : 1 : snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
242 : 1 : snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.copy_file_range_test", new_cluster.pgdata);
243 : 1 : unlink(new_link_file); /* might fail */
244 : :
245 : : #if defined(HAVE_COPY_FILE_RANGE)
246 : : {
247 : : int src_fd;
248 : : int dest_fd;
249 : :
250 [ - + ]: 1 : if ((src_fd = open(existing_file, O_RDONLY | PG_BINARY, 0)) < 0)
251 : 0 : pg_fatal("could not open file \"%s\": %m",
252 : : existing_file);
253 : :
254 [ - + ]: 1 : if ((dest_fd = open(new_link_file, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
255 : : pg_file_create_mode)) < 0)
256 : 0 : pg_fatal("could not create file \"%s\": %m",
257 : : new_link_file);
258 : :
259 [ - + ]: 1 : if (copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0) < 0)
260 : 0 : pg_fatal("could not copy file range between old and new data directories: %m");
261 : :
262 : 1 : close(src_fd);
263 : 1 : close(dest_fd);
264 : : }
265 : : #else
266 : : pg_fatal("copy_file_range not supported on this platform");
267 : : #endif
268 : :
269 : 1 : unlink(new_link_file);
270 : 1 : }
271 : :
272 : : void
273 : 2 : check_hard_link(transferMode transfer_mode)
274 : : {
275 : : char existing_file[MAXPGPATH];
276 : : char new_link_file[MAXPGPATH];
277 : :
278 : 2 : snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
279 : 2 : snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", new_cluster.pgdata);
280 : 2 : unlink(new_link_file); /* might fail */
281 : :
282 [ - + ]: 2 : if (link(existing_file, new_link_file) < 0)
283 : : {
284 [ # # ]: 0 : if (transfer_mode == TRANSFER_MODE_LINK)
285 : 0 : pg_fatal("could not create hard link between old and new data directories: %m\n"
286 : : "In link mode the old and new data directories must be on the same file system.");
287 [ # # ]: 0 : else if (transfer_mode == TRANSFER_MODE_SWAP)
288 : 0 : pg_fatal("could not create hard link between old and new data directories: %m\n"
289 : : "In swap mode the old and new data directories must be on the same file system.");
290 : : else
291 : 0 : pg_fatal("unrecognized transfer mode");
292 : : }
293 : :
294 : 2 : unlink(new_link_file);
295 : 2 : }
|