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