]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/file.c
Remove all mentions of EnterpriseDB Advanced Server from pg_upgrade;
[postgresql] / contrib / pg_upgrade / file.c
1 /*
2  *      file.c
3  *
4  *      file system operations
5  */
6
7 #include "pg_upgrade.h"
8
9 #include <sys/types.h>
10 #include <fcntl.h>
11
12 #ifdef WIN32
13 #include <windows.h>
14 #endif
15
16 #ifndef WIN32
17 char            pathSeparator = '/';
18 #else
19 char            pathSeparator = '\\';
20 #endif
21
22
23 static int      copy_file(const char *fromfile, const char *tofile, bool force);
24
25 #ifdef WIN32
26 static int      win32_pghardlink(const char *src, const char *dst);
27 #endif
28 #ifdef NOT_USED
29 static int      copy_dir(const char *from, const char *to, bool force);
30 #endif
31
32 #ifndef HAVE_SCANDIR
33 static int pg_scandir_internal(migratorContext *ctx, const char *dirname,
34                                         struct dirent *** namelist,
35                                         int (*selector) (const struct dirent *));
36 #endif
37
38
39 /*
40  * copyAndUpdateFile()
41  *
42  *      Copies a relation file from src to dst.  If pageConverter is non-NULL, this function
43  *      uses that pageConverter to do a page-by-page conversion.
44  */
45 const char *
46 copyAndUpdateFile(migratorContext *ctx, pageCnvCtx *pageConverter,
47                                   const char *src, const char *dst, bool force)
48 {
49         if (pageConverter == NULL)
50         {
51                 if (pg_copy_file(src, dst, force) == -1)
52                         return getErrorText(errno);
53                 else
54                         return NULL;
55         }
56         else
57         {
58                 /*
59                  * We have a pageConverter object - that implies that the
60                  * PageLayoutVersion differs between the two clusters so we have to
61                  * perform a page-by-page conversion.
62                  *
63                  * If the pageConverter can convert the entire file at once, invoke
64                  * that plugin function, otherwise, read each page in the relation
65                  * file and call the convertPage plugin function.
66                  */
67
68 #ifdef PAGE_CONVERSION
69                 if (pageConverter->convertFile)
70                         return pageConverter->convertFile(pageConverter->pluginData,
71                                                                                           dst, src);
72                 else
73 #endif
74                 {
75                         int                     src_fd;
76                         int                     dstfd;
77                         char            buf[BLCKSZ];
78                         ssize_t         bytesRead;
79                         const char *msg = NULL;
80
81                         if ((src_fd = open(src, O_RDONLY, 0)) < 0)
82                                 return "can't open source file";
83
84                         if ((dstfd = open(dst, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
85                                 return "can't create destination file";
86
87                         while ((bytesRead = read(src_fd, buf, BLCKSZ)) == BLCKSZ)
88                         {
89 #ifdef PAGE_CONVERSION
90                                 if ((msg = pageConverter->convertPage(pageConverter->pluginData, buf, buf)) != NULL)
91                                         break;
92 #endif
93                                 if (write(dstfd, buf, BLCKSZ) != BLCKSZ)
94                                 {
95                                         msg = "can't write new page to destination";
96                                         break;
97                                 }
98                         }
99
100                         close(src_fd);
101                         close(dstfd);
102
103                         if (msg)
104                                 return msg;
105                         else if (bytesRead != 0)
106                                 return "found partial page in source file";
107                         else
108                                 return NULL;
109                 }
110         }
111 }
112
113
114 /*
115  * linkAndUpdateFile()
116  *
117  * Creates a symbolic link between the given relation files. We use
118  * this function to perform a true in-place update. If the on-disk
119  * format of the new cluster is bit-for-bit compatible with the on-disk
120  * format of the old cluster, we can simply symlink each relation
121  * instead of copying the data from the old cluster to the new cluster.
122  */
123 const char *
124 linkAndUpdateFile(migratorContext *ctx, pageCnvCtx *pageConverter,
125                                   const char *src, const char *dst)
126 {
127         if (pageConverter != NULL)
128                 return "Can't in-place update this cluster, page-by-page conversion is required";
129
130         if (pg_link_file(src, dst) == -1)
131                 return getErrorText(errno);
132         else
133                 return NULL;
134 }
135
136
137 static int
138 copy_file(const char *srcfile, const char *dstfile, bool force)
139 {
140
141 #define COPY_BUF_SIZE (50 * BLCKSZ)
142
143         int                     src_fd;
144         int                     dest_fd;
145         char       *buffer;
146
147         if ((srcfile == NULL) || (dstfile == NULL))
148                 return -1;
149
150         if ((src_fd = open(srcfile, O_RDONLY, 0)) < 0)
151                 return -1;
152
153         if ((dest_fd = open(dstfile, O_RDWR | O_CREAT | (force ? 0 : O_EXCL), S_IRUSR | S_IWUSR)) < 0)
154         {
155                 if (src_fd != 0)
156                         close(src_fd);
157
158                 return -1;
159         }
160
161         buffer = (char *) malloc(COPY_BUF_SIZE);
162
163         if (buffer == NULL)
164         {
165                 if (src_fd != 0)
166                         close(src_fd);
167
168                 if (dest_fd != 0)
169                         close(dest_fd);
170
171                 return -1;
172         }
173
174         /* perform data copying i.e read src source, write to destination */
175         while (true)
176         {
177                 ssize_t         nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
178
179                 if (nbytes < 0)
180                 {
181                         if (buffer != NULL)
182                                 free(buffer);
183
184                         if (src_fd != 0)
185                                 close(src_fd);
186
187                         if (dest_fd != 0)
188                                 close(dest_fd);
189
190                         return -1;
191                 }
192
193                 if (nbytes == 0)
194                         break;
195
196                 errno = 0;
197
198                 if (write(dest_fd, buffer, nbytes) != nbytes)
199                 {
200                         /* if write didn't set errno, assume problem is no disk space */
201                         if (errno == 0)
202                                 errno = ENOSPC;
203
204                         if (buffer != NULL)
205                                 free(buffer);
206
207                         if (src_fd != 0)
208                                 close(src_fd);
209
210                         if (dest_fd != 0)
211                                 close(dest_fd);
212
213                         return -1;
214                 }
215         }
216
217         if (buffer != NULL)
218                 free(buffer);
219
220         if (src_fd != 0)
221                 close(src_fd);
222
223         if (dest_fd != 0)
224                 close(dest_fd);
225
226         return 1;
227 }
228
229
230 /*
231  * pg_scandir()
232  *
233  * Wrapper for portable scandir functionality
234  */
235 int
236 pg_scandir(migratorContext *ctx, const char *dirname,
237                    struct dirent ***namelist,
238                    int (*selector) (const struct dirent *))
239 {
240 #ifndef HAVE_SCANDIR
241         return pg_scandir_internal(ctx, dirname, namelist, selector);
242
243         /*
244          * scandir() is originally from BSD 4.3, which had the third argument as
245          * non-const. Linux and other C libraries have updated it to use a const.
246          * http://unix.derkeiler.com/Mailing-Lists/FreeBSD/questions/2005-12/msg00214.html
247          *
248          * Here we try to guess which libc's need const, and which don't. The net
249          * goal here is to try to suppress a compiler warning due to a prototype
250          * mismatch of const usage. Ideally we would do this via autoconf, but
251          * autoconf doesn't have a suitable builtin test and it seems overkill
252          * to add one just to avoid a warning.
253          */
254 #elif defined(freebsd) || defined(bsdi) || defined(darwin) || defined(openbsd)
255         /* no const */
256         return scandir(dirname, namelist, (int (*) (struct dirent *)) selector, NULL);
257 #else
258         /* use const */
259         return scandir(dirname, namelist, selector, NULL);
260 #endif
261 }
262
263
264 #ifndef HAVE_SCANDIR
265 /*
266  * pg_scandir_internal()
267  *
268  * Implement our own scandir() on platforms that don't have it.
269  *
270  * Returns count of files that meet the selection criteria coded in
271  * the function pointed to by selector.  Creates an array of pointers
272  * to dirent structures.  Address of array returned in namelist.
273  *
274  * Note that the number of dirent structures needed is dynamically
275  * allocated using realloc.  Realloc can be inefficient if invoked a
276  * large number of times.  Its use in pg_upgrade is to find filesystem
277  * filenames that have extended beyond the initial segment (file.1,
278  * .2, etc.) and should therefore be invoked a small number of times.
279  */
280 static int
281 pg_scandir_internal(migratorContext *ctx, const char *dirname,
282                  struct dirent *** namelist, int (*selector) (const struct dirent *))
283 {
284         DIR                *dirdesc;
285         struct dirent *direntry;
286         int                     count = 0;
287         int                     name_num = 0;
288         size_t          entrysize;
289
290         if ((dirdesc = opendir(dirname)) == NULL)
291                 pg_log(ctx, PG_FATAL, "Could not open directory \"%s\": %m\n", dirname);
292
293         *namelist = NULL;
294
295         while ((direntry = readdir(dirdesc)) != NULL)
296         {
297                 /* Invoke the selector function to see if the direntry matches */
298                 if ((*selector) (direntry))
299                 {
300                         count++;
301
302                         *namelist = (struct dirent **) realloc((void *) (*namelist),
303                                                 (size_t) ((name_num + 1) * sizeof(struct dirent *)));
304
305                         if (*namelist == NULL)
306                                 return -1;
307
308                         entrysize = sizeof(struct dirent) - sizeof(direntry->d_name) +
309                                 strlen(direntry->d_name) + 1;
310
311                         (*namelist)[name_num] = (struct dirent *) malloc(entrysize);
312
313                         if ((*namelist)[name_num] == NULL)
314                                 return -1;
315
316                         memcpy((*namelist)[name_num], direntry, entrysize);
317
318                         name_num++;
319                 }
320         }
321
322         closedir(dirdesc);
323
324         return count;
325 }
326 #endif
327
328
329 /*
330  *      dir_matching_filenames
331  *
332  *      Return only matching file names during directory scan
333  */
334 int
335 dir_matching_filenames(const struct dirent * scan_ent)
336 {
337         /* we only compare for string length because the number suffix varies */
338         if (!strncmp(scandir_file_pattern, scan_ent->d_name, strlen(scandir_file_pattern)))
339                 return 1;
340
341         return 0;
342 }
343
344
345 void
346 check_hard_link(migratorContext *ctx)
347 {
348         char            existing_file[MAXPGPATH];
349         char            new_link_file[MAXPGPATH];
350
351         snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", ctx->old.pgdata);
352         snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", ctx->new.pgdata);
353         unlink(new_link_file);          /* might fail */
354
355         if (pg_link_file(existing_file, new_link_file) == -1)
356         {
357                 pg_log(ctx, PG_FATAL,
358                            "Could not create hard link between old and new data directories:  %s\n"
359                            "In link mode the old and new data directories must be on the same file system volume.\n",
360                            getErrorText(errno));
361         }
362         unlink(new_link_file);
363 }
364
365 #ifdef WIN32
366 static int
367 win32_pghardlink(const char *src, const char *dst)
368 {
369         /*
370          * CreateHardLinkA returns zero for failure
371          * http://msdn.microsoft.com/en-us/library/aa363860(VS.85).aspx
372          */
373         if (CreateHardLinkA(dst, src, NULL) == 0)
374                 return -1;
375         else
376                 return 0;
377 }
378 #endif
379
380
381 #ifdef NOT_USED
382 /*
383  * copy_dir()
384  *
385  *      Copies either a directory or a single file within a directory.  If the
386  *      source argument names a directory, we recursively copy that directory,
387  *      otherwise we copy a single file.
388  */
389 static int
390 copy_dir(const char *src, const char *dst, bool force)
391 {
392         DIR                *srcdir;
393         struct dirent *de = NULL;
394         struct stat fst;
395
396         if (src == NULL || dst == NULL)
397                 return -1;
398
399         /*
400          * Try to open the source directory - if it turns out not to be a
401          * directory, assume that it's a file and copy that instead.
402          */
403         if ((srcdir = opendir(src)) == NULL)
404         {
405                 if (errno == ENOTDIR)
406                         return copy_file(src, dst, true);
407                 return -1;
408         }
409
410         if (mkdir(dst, S_IRWXU) != 0)
411         {
412                 /*
413                  * ignore directory already exist error
414                  */
415                 if (errno != EEXIST)
416                         return -1;
417         }
418
419         while ((de = readdir(srcdir)) != NULL)
420         {
421                 char            src_file[MAXPGPATH];
422                 char            dest_file[MAXPGPATH];
423
424                 if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
425                         continue;
426
427                 memset(src_file, 0, sizeof(src_file));
428                 memset(dest_file, 0, sizeof(dest_file));
429
430                 snprintf(src_file, sizeof(src_file), "%s/%s", src, de->d_name);
431                 snprintf(dest_file, sizeof(dest_file), "%s/%s", dst, de->d_name);
432
433                 if (stat(src_file, &fst) < 0)
434                 {
435                         if (srcdir != NULL)
436                         {
437                                 closedir(srcdir);
438                                 srcdir = NULL;
439                         }
440
441                         return -1;
442                 }
443
444                 if (fst.st_mode & S_IFDIR)
445                 {
446                         /* recurse to handle subdirectories */
447                         if (force)
448                                 copy_dir(src_file, dest_file, true);
449                 }
450                 else if (fst.st_mode & S_IFREG)
451                 {
452                         if ((copy_file(src_file, dest_file, 1)) == -1)
453                         {
454                                 if (srcdir != NULL)
455                                 {
456                                         closedir(srcdir);
457                                         srcdir = NULL;
458                                 }
459                                 return -1;
460                         }
461                 }
462         }
463
464         if (srcdir != NULL)
465         {
466                 closedir(srcdir);
467                 srcdir = NULL;
468         }
469         return 1;
470 }
471
472 #endif