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