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