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-2017, 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 "file_ops.h"
22 #include "filemap.h"
23 #include "logging.h"
24 #include "pg_rewind.h"
25
26 /*
27 * Currently open target file.
28 */
29 static int dstfd = -1;
30 static char dstpath[MAXPGPATH] = "";
31
32 static void create_target_dir(const char *path);
33 static void remove_target_dir(const char *path);
34 static void create_target_symlink(const char *path, const char *link);
35 static 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 */
41 void
open_target_file(const char * path,bool trunc)42 open_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, 0600);
61 if (dstfd < 0)
62 pg_fatal("could not open target file \"%s\": %s\n",
63 dstpath, strerror(errno));
64 }
65
66 /*
67 * Close target file, if it's open.
68 */
69 void
close_target_file(void)70 close_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\": %s\n",
77 dstpath, strerror(errno));
78
79 dstfd = -1;
80 }
81
82 void
write_target_range(char * buf,off_t begin,size_t size)83 write_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\": %s\n",
97 dstpath, strerror(errno));
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\": %s\n",
113 dstpath, strerror(errno));
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
124 void
remove_target(file_entry_t * entry)125 remove_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
145 void
create_target(file_entry_t * entry)146 create_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\n");
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 */
171 void
remove_target_file(const char * path,bool missing_ok)172 remove_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\": %s\n",
186 dstpath, strerror(errno));
187 }
188 }
189
190 void
truncate_target_file(const char * path,off_t newsize)191 truncate_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, 0);
202 if (fd < 0)
203 pg_fatal("could not open file \"%s\" for truncation: %s\n",
204 dstpath, strerror(errno));
205
206 if (ftruncate(fd, newsize) != 0)
207 pg_fatal("could not truncate file \"%s\" to %u: %s\n",
208 dstpath, (unsigned int) newsize, strerror(errno));
209
210 close(fd);
211 }
212
213 static void
create_target_dir(const char * path)214 create_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, S_IRWXU) != 0)
223 pg_fatal("could not create directory \"%s\": %s\n",
224 dstpath, strerror(errno));
225 }
226
227 static void
remove_target_dir(const char * path)228 remove_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\": %s\n",
238 dstpath, strerror(errno));
239 }
240
241 static void
create_target_symlink(const char * path,const char * link)242 create_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\": %s\n",
252 dstpath, strerror(errno));
253 }
254
255 static void
remove_target_symlink(const char * path)256 remove_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\": %s\n",
266 dstpath, strerror(errno));
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 */
283 char *
slurpFile(const char * datadir,const char * path,size_t * filesize)284 slurpFile(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
292 snprintf(fullpath, sizeof(fullpath), "%s/%s", datadir, path);
293
294 if ((fd = open(fullpath, O_RDONLY | PG_BINARY, 0)) == -1)
295 pg_fatal("could not open file \"%s\" for reading: %s\n",
296 fullpath, strerror(errno));
297
298 if (fstat(fd, &statbuf) < 0)
299 pg_fatal("could not open file \"%s\" for reading: %s\n",
300 fullpath, strerror(errno));
301
302 len = statbuf.st_size;
303
304 buffer = pg_malloc(len + 1);
305
306 if (read(fd, buffer, len) != len)
307 pg_fatal("could not read file \"%s\": %s\n",
308 fullpath, strerror(errno));
309 close(fd);
310
311 /* Zero-terminate the buffer. */
312 buffer[len] = '\0';
313
314 if (filesize)
315 *filesize = len;
316 return buffer;
317 }
318