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-2019, PostgreSQL Global Development Group
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres_fe.h"
16
17#include <sys/stat.h>
18#include <fcntl.h>
19#include <unistd.h>
20
21#include "common/file_perm.h"
22#include "file_ops.h"
23#include "filemap.h"
24#include "pg_rewind.h"
25
26/*
27 * Currently open target file.
28 */
29static int dstfd = -1;
30static char dstpath[MAXPGPATH] = "";
31
32static void create_target_dir(const char *path);
33static void remove_target_dir(const char *path);
34static void create_target_symlink(const char *path, const char *link);
35static void remove_target_symlink(const char *path);
36
37/*
38 * Open a target file for writing. If 'trunc' is true and the file already
39 * exists, it will be truncated.
40 */
41void
42open_target_file(const char *path, bool trunc)
43{
44 int mode;
45
46 if (dry_run)
47 return;
48
49 if (dstfd != -1 && !trunc &&
50 strcmp(path, &dstpath[strlen(datadir_target) + 1]) == 0)
51 return; /* already open */
52
53 close_target_file();
54
55 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
56
57 mode = O_WRONLY | O_CREAT | PG_BINARY;
58 if (trunc)
59 mode |= O_TRUNC;
60 dstfd = open(dstpath, mode, pg_file_create_mode);
61 if (dstfd < 0)
62 pg_fatal("could not open target file \"%s\": %m",
63 dstpath);
64}
65
66/*
67 * Close target file, if it's open.
68 */
69void
70close_target_file(void)
71{
72 if (dstfd == -1)
73 return;
74
75 if (close(dstfd) != 0)
76 pg_fatal("could not close target file \"%s\": %m",
77 dstpath);
78
79 dstfd = -1;
80}
81
82void
83write_target_range(char *buf, off_t begin, size_t size)
84{
85 int writeleft;
86 char *p;
87
88 /* update progress report */
89 fetch_done += size;
90 progress_report(false);
91
92 if (dry_run)
93 return;
94
95 if (lseek(dstfd, begin, SEEK_SET) == -1)
96 pg_fatal("could not seek in target file \"%s\": %m",
97 dstpath);
98
99 writeleft = size;
100 p = buf;
101 while (writeleft > 0)
102 {
103 int writelen;
104
105 errno = 0;
106 writelen = write(dstfd, p, writeleft);
107 if (writelen < 0)
108 {
109 /* if write didn't set errno, assume problem is no disk space */
110 if (errno == 0)
111 errno = ENOSPC;
112 pg_fatal("could not write file \"%s\": %m",
113 dstpath);
114 }
115
116 p += writelen;
117 writeleft -= writelen;
118 }
119
120 /* keep the file open, in case we need to copy more blocks in it */
121}
122
123
124void
125remove_target(file_entry_t *entry)
126{
127 Assert(entry->action == FILE_ACTION_REMOVE);
128
129 switch (entry->type)
130 {
131 case FILE_TYPE_DIRECTORY:
132 remove_target_dir(entry->path);
133 break;
134
135 case FILE_TYPE_REGULAR:
136 remove_target_file(entry->path, false);
137 break;
138
139 case FILE_TYPE_SYMLINK:
140 remove_target_symlink(entry->path);
141 break;
142 }
143}
144
145void
146create_target(file_entry_t *entry)
147{
148 Assert(entry->action == FILE_ACTION_CREATE);
149
150 switch (entry->type)
151 {
152 case FILE_TYPE_DIRECTORY:
153 create_target_dir(entry->path);
154 break;
155
156 case FILE_TYPE_SYMLINK:
157 create_target_symlink(entry->path, entry->link_target);
158 break;
159
160 case FILE_TYPE_REGULAR:
161 /* can't happen. Regular files are created with open_target_file. */
162 pg_fatal("invalid action (CREATE) for regular file");
163 break;
164 }
165}
166
167/*
168 * Remove a file from target data directory. If missing_ok is true, it
169 * is fine for the target file to not exist.
170 */
171void
172remove_target_file(const char *path, bool missing_ok)
173{
174 char dstpath[MAXPGPATH];
175
176 if (dry_run)
177 return;
178
179 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
180 if (unlink(dstpath) != 0)
181 {
182 if (errno == ENOENT && missing_ok)
183 return;
184
185 pg_fatal("could not remove file \"%s\": %m",
186 dstpath);
187 }
188}
189
190void
191truncate_target_file(const char *path, off_t newsize)
192{
193 char dstpath[MAXPGPATH];
194 int fd;
195
196 if (dry_run)
197 return;
198
199 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
200
201 fd = open(dstpath, O_WRONLY, pg_file_create_mode);
202 if (fd < 0)
203 pg_fatal("could not open file \"%s\" for truncation: %m",
204 dstpath);
205
206 if (ftruncate(fd, newsize) != 0)
207 pg_fatal("could not truncate file \"%s\" to %u: %m",
208 dstpath, (unsigned int) newsize);
209
210 close(fd);
211}
212
213static void
214create_target_dir(const char *path)
215{
216 char dstpath[MAXPGPATH];
217
218 if (dry_run)
219 return;
220
221 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
222 if (mkdir(dstpath, pg_dir_create_mode) != 0)
223 pg_fatal("could not create directory \"%s\": %m",
224 dstpath);
225}
226
227static void
228remove_target_dir(const char *path)
229{
230 char dstpath[MAXPGPATH];
231
232 if (dry_run)
233 return;
234
235 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
236 if (rmdir(dstpath) != 0)
237 pg_fatal("could not remove directory \"%s\": %m",
238 dstpath);
239}
240
241static void
242create_target_symlink(const char *path, const char *link)
243{
244 char dstpath[MAXPGPATH];
245
246 if (dry_run)
247 return;
248
249 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
250 if (symlink(link, dstpath) != 0)
251 pg_fatal("could not create symbolic link at \"%s\": %m",
252 dstpath);
253}
254
255static void
256remove_target_symlink(const char *path)
257{
258 char dstpath[MAXPGPATH];
259
260 if (dry_run)
261 return;
262
263 snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
264 if (unlink(dstpath) != 0)
265 pg_fatal("could not remove symbolic link \"%s\": %m",
266 dstpath);
267}
268
269
270/*
271 * Read a file into memory. The file to be read is <datadir>/<path>.
272 * The file contents are returned in a malloc'd buffer, and *filesize
273 * is set to the length of the file.
274 *
275 * The returned buffer is always zero-terminated; the size of the returned
276 * buffer is actually *filesize + 1. That's handy when reading a text file.
277 * This function can be used to read binary files as well, you can just
278 * ignore the zero-terminator in that case.
279 *
280 * This function is used to implement the fetchFile function in the "fetch"
281 * interface (see fetch.c), but is also called directly.
282 */
283char *
284slurpFile(const char *datadir, const char *path, size_t *filesize)
285{
286 int fd;
287 char *buffer;
288 struct stat statbuf;
289 char fullpath[MAXPGPATH];
290 int len;
291 int r;
292
293 snprintf(fullpath, sizeof(fullpath), "%s/%s", datadir, path);
294
295 if ((fd = open(fullpath, O_RDONLY | PG_BINARY, 0)) == -1)
296 pg_fatal("could not open file \"%s\" for reading: %m",
297 fullpath);
298
299 if (fstat(fd, &statbuf) < 0)
300 pg_fatal("could not open file \"%s\" for reading: %m",
301 fullpath);
302
303 len = statbuf.st_size;
304
305 buffer = pg_malloc(len + 1);
306
307 r = read(fd, buffer, len);
308 if (r != len)
309 {
310 if (r < 0)
311 pg_fatal("could not read file \"%s\": %m",
312 fullpath);
313 else
314 pg_fatal("could not read file \"%s\": read %d of %zu",
315 fullpath, r, (Size) len);
316 }
317 close(fd);
318
319 /* Zero-terminate the buffer. */
320 buffer[len] = '\0';
321
322 if (filesize)
323 *filesize = len;
324 return buffer;
325}
326