4 * file system operations
7 #include "pg_upgrade.h"
17 char pathSeparator = '/';
19 char pathSeparator = '\\';
23 static int copy_file(const char *fromfile, const char *tofile, bool force);
26 static int win32_pghardlink(const char *src, const char *dst);
29 static int copy_dir(const char *from, const char *to, bool force);
33 static int pg_scandir_internal(migratorContext *ctx, const char *dirname,
34 struct dirent *** namelist,
35 int (*selector) (const struct dirent *));
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.
46 copyAndUpdateFile(migratorContext *ctx, pageCnvCtx *pageConverter,
47 const char *src, const char *dst, bool force)
49 if (pageConverter == NULL)
51 if (pg_copy_file(src, dst, force) == -1)
52 return getErrorText(errno);
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.
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.
68 #ifdef PAGE_CONVERSION
69 if (pageConverter->convertFile)
70 return pageConverter->convertFile(pageConverter->pluginData,
79 const char *msg = NULL;
81 if ((src_fd = open(src, O_RDONLY, 0)) < 0)
82 return "can't open source file";
84 if ((dstfd = open(dst, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
85 return "can't create destination file";
87 while ((bytesRead = read(src_fd, buf, BLCKSZ)) == BLCKSZ)
89 #ifdef PAGE_CONVERSION
90 if ((msg = pageConverter->convertPage(pageConverter->pluginData, buf, buf)) != NULL)
93 if (write(dstfd, buf, BLCKSZ) != BLCKSZ)
95 msg = "can't write new page to destination";
105 else if (bytesRead != 0)
106 return "found partial page in source file";
115 * linkAndUpdateFile()
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.
124 linkAndUpdateFile(migratorContext *ctx, pageCnvCtx *pageConverter,
125 const char *src, const char *dst)
127 if (pageConverter != NULL)
128 return "Can't in-place update this cluster, page-by-page conversion is required";
130 if (pg_link_file(src, dst) == -1)
131 return getErrorText(errno);
138 copy_file(const char *srcfile, const char *dstfile, bool force)
141 #define COPY_BUF_SIZE (50 * BLCKSZ)
147 if ((srcfile == NULL) || (dstfile == NULL))
150 if ((src_fd = open(srcfile, O_RDONLY, 0)) < 0)
153 if ((dest_fd = open(dstfile, O_RDWR | O_CREAT | (force ? 0 : O_EXCL), S_IRUSR | S_IWUSR)) < 0)
161 buffer = (char *) malloc(COPY_BUF_SIZE);
174 /* perform data copying i.e read src source, write to destination */
177 ssize_t nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
198 if (write(dest_fd, buffer, nbytes) != nbytes)
200 /* if write didn't set errno, assume problem is no disk space */
233 * Wrapper for portable scandir functionality
236 pg_scandir(migratorContext *ctx, const char *dirname,
237 struct dirent ***namelist,
238 int (*selector) (const struct dirent *))
241 return pg_scandir_internal(ctx, dirname, namelist, selector);
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
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.
254 #elif defined(freebsd) || defined(bsdi) || defined(darwin) || defined(openbsd)
256 return scandir(dirname, namelist, (int (*) (struct dirent *)) selector, NULL);
259 return scandir(dirname, namelist, selector, NULL);
266 * pg_scandir_internal()
268 * Implement our own scandir() on platforms that don't have it.
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.
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.
281 pg_scandir_internal(migratorContext *ctx, const char *dirname,
282 struct dirent *** namelist, int (*selector) (const struct dirent *))
285 struct dirent *direntry;
290 if ((dirdesc = opendir(dirname)) == NULL)
291 pg_log(ctx, PG_FATAL, "Could not open directory \"%s\": %m\n", dirname);
295 while ((direntry = readdir(dirdesc)) != NULL)
297 /* Invoke the selector function to see if the direntry matches */
298 if ((*selector) (direntry))
302 *namelist = (struct dirent **) realloc((void *) (*namelist),
303 (size_t) ((name_num + 1) * sizeof(struct dirent *)));
305 if (*namelist == NULL)
308 entrysize = sizeof(struct dirent) - sizeof(direntry->d_name) +
309 strlen(direntry->d_name) + 1;
311 (*namelist)[name_num] = (struct dirent *) malloc(entrysize);
313 if ((*namelist)[name_num] == NULL)
316 memcpy((*namelist)[name_num], direntry, entrysize);
330 * dir_matching_filenames
332 * Return only matching file names during directory scan
335 dir_matching_filenames(const struct dirent * scan_ent)
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)))
346 check_hard_link(migratorContext *ctx)
348 char existing_file[MAXPGPATH];
349 char new_link_file[MAXPGPATH];
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 */
355 if (pg_link_file(existing_file, new_link_file) == -1)
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));
362 unlink(new_link_file);
367 win32_pghardlink(const char *src, const char *dst)
370 * CreateHardLinkA returns zero for failure
371 * http://msdn.microsoft.com/en-us/library/aa363860(VS.85).aspx
373 if (CreateHardLinkA(dst, src, NULL) == 0)
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.
390 copy_dir(const char *src, const char *dst, bool force)
393 struct dirent *de = NULL;
396 if (src == NULL || dst == NULL)
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.
403 if ((srcdir = opendir(src)) == NULL)
405 if (errno == ENOTDIR)
406 return copy_file(src, dst, true);
410 if (mkdir(dst, S_IRWXU) != 0)
413 * ignore directory already exist error
419 while ((de = readdir(srcdir)) != NULL)
421 char src_file[MAXPGPATH];
422 char dest_file[MAXPGPATH];
424 if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
427 memset(src_file, 0, sizeof(src_file));
428 memset(dest_file, 0, sizeof(dest_file));
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);
433 if (stat(src_file, &fst) < 0)
444 if (fst.st_mode & S_IFDIR)
446 /* recurse to handle subdirectories */
448 copy_dir(src_file, dest_file, true);
450 else if (fst.st_mode & S_IFREG)
452 if ((copy_file(src_file, dest_file, 1)) == -1)