PostgreSQL Source Code git master
Loading...
Searching...
No Matches
file_ops.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * file_ops.c
4 * Helper functions for operating on files.
5 *
6 * Most of the functions in this file are helper functions for writing to
7 * the target data directory. The functions check the --dry-run flag, and
8 * do nothing if it's enabled. You should avoid accessing the target files
9 * directly but if you do, make sure you honor the --dry-run mode!
10 *
11 * Portions Copyright (c) 2013-2026, PostgreSQL Global Development Group
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres_fe.h"
16
17#include <sys/stat.h>
18#include <dirent.h>
19#include <fcntl.h>
20#include <unistd.h>
21
22#include "common/file_perm.h"
23#include "common/file_utils.h"
24#include "file_ops.h"
25#include "filemap.h"
26#include "pg_rewind.h"
27
28/*
29 * Currently open target file.
30 */
31static int dstfd = -1;
32static char dstpath[MAXPGPATH] = "";
33
34static void create_target_dir(const char *path);
35static void remove_target_dir(const char *path);
36static void create_target_symlink(const char *path, const char *link);
37static void remove_target_symlink(const char *path);
38
39static void recurse_dir(const char *datadir, const char *parentpath,
41
42/*
43 * Open a target file for writing. If 'trunc' is true and the file already
44 * exists, it will be truncated.
45 */
46void
47open_target_file(const char *path, bool trunc)
48{
49 int mode;
50
52 pg_fatal("target file path is unsafe for open: \"%s\"", path);
53
54 if (dry_run)
55 return;
56
57 if (dstfd != -1 && !trunc &&
58 strcmp(path, &dstpath[strlen(datadir_target) + 1]) == 0)
59 return; /* already open */
60
62
63 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
64
66 if (trunc)
67 mode |= O_TRUNC;
69 if (dstfd < 0)
70 pg_fatal("could not open target file \"%s\": %m",
71 dstpath);
72}
73
74/*
75 * Close target file, if it's open.
76 */
77void
79{
80 if (dstfd == -1)
81 return;
82
83 if (close(dstfd) != 0)
84 pg_fatal("could not close target file \"%s\": %m",
85 dstpath);
86
87 dstfd = -1;
88}
89
90void
91write_target_range(char *buf, off_t begin, size_t size)
92{
93 size_t writeleft;
94 char *p;
95
96 /* update progress report */
97 fetch_done += size;
98 progress_report(false);
99
100 if (dry_run)
101 return;
102
103 if (lseek(dstfd, begin, SEEK_SET) == -1)
104 pg_fatal("could not seek in target file \"%s\": %m",
105 dstpath);
106
107 writeleft = size;
108 p = buf;
109 while (writeleft > 0)
110 {
112
113 errno = 0;
115 if (writelen < 0)
116 {
117 /* if write didn't set errno, assume problem is no disk space */
118 if (errno == 0)
119 errno = ENOSPC;
120 pg_fatal("could not write file \"%s\": %m",
121 dstpath);
122 }
123
124 p += writelen;
126 }
127
128 /* keep the file open, in case we need to copy more blocks in it */
129}
130
131
132void
134{
136 Assert(entry->target_exists);
137
138 switch (entry->target_type)
139 {
141 remove_target_dir(entry->path);
142 break;
143
145 remove_target_file(entry->path, false);
146 break;
147
150 break;
151
153 pg_fatal("undefined file type for \"%s\"", entry->path);
154 break;
155 }
156}
157
158void
160{
162 Assert(!entry->target_exists);
163
164 switch (entry->source_type)
165 {
167 create_target_dir(entry->path);
168 break;
169
172 break;
173
175 /* can't happen. Regular files are created with open_target_file. */
176 pg_fatal("invalid action (CREATE) for regular file");
177 break;
178
180 pg_fatal("undefined file type for \"%s\"", entry->path);
181 break;
182 }
183}
184
185/*
186 * Remove a file from target data directory. If missing_ok is true, it
187 * is fine for the target file to not exist.
188 */
189void
190remove_target_file(const char *path, bool missing_ok)
191{
192 char dstpath[MAXPGPATH];
193
195 pg_fatal("target file path is unsafe for removal: \"%s\"", path);
196
197 if (dry_run)
198 return;
199
200 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
201 if (unlink(dstpath) != 0)
202 {
203 if (errno == ENOENT && missing_ok)
204 return;
205
206 pg_fatal("could not remove file \"%s\": %m",
207 dstpath);
208 }
209}
210
211void
213{
214 char dstpath[MAXPGPATH];
215 int fd;
216
218 pg_fatal("target file path is unsafe for truncation: \"%s\"", path);
219
220 if (dry_run)
221 return;
222
223 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
224
226 if (fd < 0)
227 pg_fatal("could not open file \"%s\" for truncation: %m",
228 dstpath);
229
230 if (ftruncate(fd, newsize) != 0)
231 pg_fatal("could not truncate file \"%s\" to %u: %m",
232 dstpath, (unsigned int) newsize);
233
234 close(fd);
235}
236
237static void
238create_target_dir(const char *path)
239{
240 char dstpath[MAXPGPATH];
241
243 pg_fatal("target directory path is unsafe for directory creation: \"%s\"",
244 path);
245
246 if (dry_run)
247 return;
248
249 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
251 pg_fatal("could not create directory \"%s\": %m",
252 dstpath);
253}
254
255static void
256remove_target_dir(const char *path)
257{
258 char dstpath[MAXPGPATH];
259
261 pg_fatal("target directory path is unsafe for directory removal: \"%s\"",
262 path);
263
264 if (dry_run)
265 return;
266
267 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
268 if (rmdir(dstpath) != 0)
269 pg_fatal("could not remove directory \"%s\": %m",
270 dstpath);
271}
272
273static void
274create_target_symlink(const char *path, const char *link)
275{
276 char dstpath[MAXPGPATH];
277
279 pg_fatal("target symlink path is unsafe for creation: \"%s\"", path);
280
281 if (dry_run)
282 return;
283
284 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
285 if (symlink(link, dstpath) != 0)
286 pg_fatal("could not create symbolic link at \"%s\": %m",
287 dstpath);
288}
289
290static void
291remove_target_symlink(const char *path)
292{
293 char dstpath[MAXPGPATH];
294
296 pg_fatal("target symlink path is unsafe for removal: \"%s\"", path);
297
298 if (dry_run)
299 return;
300
301 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
302 if (unlink(dstpath) != 0)
303 pg_fatal("could not remove symbolic link \"%s\": %m",
304 dstpath);
305}
306
307/*
308 * Sync target data directory to ensure that modifications are safely on disk.
309 *
310 * We do this once, for the whole data directory, for performance reasons. At
311 * the end of pg_rewind's run, the kernel is likely to already have flushed
312 * most dirty buffers to disk. Additionally sync_pgdata uses a two-pass
313 * approach when fsync is specified (only initiating writeback in the first
314 * pass), which often reduces the overall amount of IO noticeably.
315 */
316void
318{
319 if (!do_sync || dry_run)
320 return;
321
323}
324
325
326/*
327 * Read a file into memory. The file to be read is <datadir>/<path>.
328 * The file contents are returned in a malloc'd buffer, and *filesize
329 * is set to the length of the file.
330 *
331 * The returned buffer is always zero-terminated; the size of the returned
332 * buffer is actually *filesize + 1. That's handy when reading a text file.
333 * This function can be used to read binary files as well, you can just
334 * ignore the zero-terminator in that case.
335 */
336char *
337slurpFile(const char *datadir, const char *path, size_t *filesize)
338{
339 int fd;
340 char *buffer;
341 struct stat statbuf;
342 char fullpath[MAXPGPATH];
343 int len;
344 int r;
345
346 snprintf(fullpath, sizeof(fullpath), "%s/%s", datadir, path);
347
348 if ((fd = open(fullpath, O_RDONLY | PG_BINARY, 0)) == -1)
349 pg_fatal("could not open file \"%s\" for reading: %m",
350 fullpath);
351
352 if (fstat(fd, &statbuf) < 0)
353 pg_fatal("could not stat file \"%s\": %m",
354 fullpath);
355
356 len = statbuf.st_size;
357
358 buffer = pg_malloc(len + 1);
359
360 r = read(fd, buffer, len);
361 if (r != len)
362 {
363 if (r < 0)
364 pg_fatal("could not read file \"%s\": %m",
365 fullpath);
366 else
367 pg_fatal("could not read file \"%s\": read %d of %zu",
368 fullpath, r, (Size) len);
369 }
370 close(fd);
371
372 /* Zero-terminate the buffer. */
373 buffer[len] = '\0';
374
375 if (filesize)
376 *filesize = len;
377 return buffer;
378}
379
380/*
381 * Traverse through all files in a data directory, calling 'callback'
382 * for each file.
383 */
384void
389
390/*
391 * recursive part of traverse_datadir
392 *
393 * parentpath is the current subdirectory's path relative to datadir,
394 * or NULL at the top level.
395 */
396static void
397recurse_dir(const char *datadir, const char *parentpath,
399{
400 DIR *xldir;
401 struct dirent *xlde;
403
404 if (parentpath)
406 else
408
410 if (xldir == NULL)
411 pg_fatal("could not open directory \"%s\": %m",
413
414 while (errno = 0, (xlde = readdir(xldir)) != NULL)
415 {
416 struct stat fst;
417 char fullpath[MAXPGPATH * 2];
418 char path[MAXPGPATH * 2];
419
420 if (strcmp(xlde->d_name, ".") == 0 ||
421 strcmp(xlde->d_name, "..") == 0)
422 continue;
423
424 snprintf(fullpath, sizeof(fullpath), "%s/%s", fullparentpath, xlde->d_name);
425
426 if (lstat(fullpath, &fst) < 0)
427 {
428 if (errno == ENOENT)
429 {
430 /*
431 * File doesn't exist anymore. This is ok, if the new primary
432 * is running and the file was just removed. If it was a data
433 * file, there should be a WAL record of the removal. If it
434 * was something else, it couldn't have been anyway.
435 *
436 * TODO: But complain if we're processing the target dir!
437 */
438 }
439 else
440 pg_fatal("could not stat file \"%s\": %m",
441 fullpath);
442 }
443
444 if (parentpath)
445 snprintf(path, sizeof(path), "%s/%s", parentpath, xlde->d_name);
446 else
447 snprintf(path, sizeof(path), "%s", xlde->d_name);
448
449 if (S_ISREG(fst.st_mode))
450 callback(path, FILE_TYPE_REGULAR, fst.st_size, NULL);
451 else if (S_ISDIR(fst.st_mode))
452 {
454 /* recurse to handle subdirectories */
456 }
457 else if (S_ISLNK(fst.st_mode))
458 {
460 int len;
461
462 len = readlink(fullpath, link_target, sizeof(link_target));
463 if (len < 0)
464 pg_fatal("could not read symbolic link \"%s\": %m",
465 fullpath);
466 if (len >= sizeof(link_target))
467 pg_fatal("symbolic link \"%s\" target is too long",
468 fullpath);
469 link_target[len] = '\0';
470
472
473 /*
474 * If it's a symlink within pg_tblspc, we need to recurse into it,
475 * to process all the tablespaces. We also follow a symlink if
476 * it's for pg_wal. Symlinks elsewhere are ignored.
477 */
478 if ((parentpath && strcmp(parentpath, PG_TBLSPC_DIR) == 0) ||
479 strcmp(path, "pg_wal") == 0)
481 }
482 }
483
484 if (errno)
485 pg_fatal("could not read directory \"%s\": %m",
487
488 if (closedir(xldir))
489 pg_fatal("could not close directory \"%s\": %m",
491}
#define Assert(condition)
Definition c.h:943
#define PG_BINARY
Definition c.h:1374
size_t Size
Definition c.h:689
int closedir(DIR *)
Definition dirent.c:127
struct dirent * readdir(DIR *)
Definition dirent.c:78
DIR * opendir(const char *)
Definition dirent.c:33
void * pg_malloc(size_t size)
Definition fe_memutils.c:53
void remove_target_file(const char *path, bool missing_ok)
Definition file_ops.c:190
static char dstpath[MAXPGPATH]
Definition file_ops.c:32
void traverse_datadir(const char *datadir, process_file_callback_t callback)
Definition file_ops.c:385
static void remove_target_dir(const char *path)
Definition file_ops.c:256
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
Definition file_ops.c:337
void close_target_file(void)
Definition file_ops.c:78
static void create_target_symlink(const char *path, const char *link)
Definition file_ops.c:274
void truncate_target_file(const char *path, off_t newsize)
Definition file_ops.c:212
void remove_target(file_entry_t *entry)
Definition file_ops.c:133
void sync_target_dir(void)
Definition file_ops.c:317
void create_target(file_entry_t *entry)
Definition file_ops.c:159
void open_target_file(const char *path, bool trunc)
Definition file_ops.c:47
static void create_target_dir(const char *path)
Definition file_ops.c:238
static int dstfd
Definition file_ops.c:31
void write_target_range(char *buf, off_t begin, size_t size)
Definition file_ops.c:91
static void remove_target_symlink(const char *path)
Definition file_ops.c:291
static void recurse_dir(const char *datadir, const char *parentpath, process_file_callback_t callback)
Definition file_ops.c:397
void(* process_file_callback_t)(const char *path, file_type_t type, size_t size, const char *link_target)
Definition file_ops.h:26
int pg_file_create_mode
Definition file_perm.c:19
int pg_dir_create_mode
Definition file_perm.c:18
@ FILE_ACTION_REMOVE
Definition filemap.h:28
@ FILE_ACTION_CREATE
Definition filemap.h:21
@ FILE_TYPE_UNDEFINED
Definition filemap.h:33
@ FILE_TYPE_REGULAR
Definition filemap.h:35
@ FILE_TYPE_SYMLINK
Definition filemap.h:37
@ FILE_TYPE_DIRECTORY
Definition filemap.h:36
static bool do_sync
Definition initdb.c:164
static DataDirSyncMethod sync_method
Definition initdb.c:170
#define close(a)
Definition win32.h:12
#define write(a, b, c)
Definition win32.h:14
#define read(a, b, c)
Definition win32.h:13
static void progress_report(uint64 relations_total, uint64 relations_checked, uint64 relpages_total, uint64 relpages_checked, const char *datname, bool force, bool finished)
#define pg_fatal(...)
static PgChecksumMode mode
#define MAXPGPATH
const void size_t len
static bool dry_run
char * datadir
char * datadir_target
Definition pg_rewind.c:67
uint64 fetch_done
Definition pg_rewind.c:86
static char buf[DEFAULT_XLOG_SEG_SIZE]
bool path_is_safe_for_extraction(const char *path)
Definition path.c:637
#define snprintf
Definition port.h:261
static int fd(const char *x, int i)
static int fb(int x)
#define PG_TBLSPC_DIR
Definition relpath.h:41
Definition dirent.c:26
Definition filemap.h:58
const char * path
Definition filemap.h:61
bool target_exists
Definition filemap.h:67
char * source_link_target
Definition filemap.h:84
file_type_t source_type
Definition filemap.h:82
file_action_t action
Definition filemap.h:89
file_type_t target_type
Definition filemap.h:68
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
#define lstat(path, sb)
Definition win32_port.h:275
#define S_ISDIR(m)
Definition win32_port.h:315
#define S_ISLNK(m)
Definition win32_port.h:334
#define mkdir(a, b)
Definition win32_port.h:80
#define fstat
Definition win32_port.h:73
#define symlink(oldpath, newpath)
Definition win32_port.h:225
#define readlink(path, buf, size)
Definition win32_port.h:226
#define S_ISREG(m)
Definition win32_port.h:318