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