/* recurse to handle subdirectories */
recurse_dir(datadir, path, callback);
}
+#ifndef WIN32
else if (S_ISLNK(fst.st_mode))
+#else
+ else if (pgwin32_is_junction(fullpath))
+#endif
{
+#if defined(HAVE_READLINK) || defined(WIN32)
char link_target[MAXPGPATH];
ssize_t len;
*/
if (strcmp(parentpath, "pg_tblspc") == 0)
recurse_dir(datadir, path, callback);
+#else
+ fprintf(stderr, "\"%s\" is a symbolic link, but symbolic links are not supported on this platform\n", fullpath);
+ exit(1);
+#endif /* HAVE_READLINK */
}
}
closedir(xldir);
truncate_target_file(const char *path, off_t newsize)
{
char dstpath[MAXPGPATH];
+ int fd;
if (dry_run)
return;
snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
- if (truncate(dstpath, newsize) != 0)
+
+ fd = open(path, O_WRONLY, 0);
+ if (fd < 0)
+ {
+ fprintf(stderr, "could not open file \"%s\" for truncation: %s\n",
+ dstpath, strerror(errno));
+ exit(1);
+ }
+ if (ftruncate(fd, newsize) != 0)
{
fprintf(stderr, "could not truncate file \"%s\" to %u bytes: %s\n",
dstpath, (unsigned int) newsize, strerror(errno));
exit(1);
}
+ close(fd);
}
static void
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
-#include <regex.h>
#include "datapagemap.h"
#include "filemap.h"
#include "util.h"
#include "pg_rewind.h"
+#include "catalog/pg_tablespace.h"
#include "storage/fd.h"
filemap_t *filemap = NULL;
break;
case FILE_TYPE_SYMLINK:
- if (exists && !S_ISLNK(statbuf.st_mode))
+ if (exists &&
+#ifndef WIN32
+ !S_ISLNK(statbuf.st_mode)
+#else
+ !pgwin32_is_junction(localpath)
+#endif
+ )
{
/* it's a symbolic link in target, but not in source. Strange.. */
fprintf(stderr, "\"%s\" is not a symbolic link.\n", localpath);
static bool
isRelDataFile(const char *path)
{
- static bool regexps_compiled = false;
- static regex_t datasegment_regex;
- int rc;
+ char buf[20 + 1];
+ Oid spcNode;
+ Oid dbNode;
+ Oid relNode;
+ unsigned int segNo;
+ int nmatch;
+ bool matched;
- /* Compile the regexp if not compiled yet. */
- if (!regexps_compiled)
+ /*
+ * Relation data files can be in one of the following directories:
+ *
+ * global/
+ * shared relations
+ *
+ * base/<db oid>/
+ * regular relations, default tablespace
+ *
+ * pg_tblspc/<tblspc oid>/PG_9.4_201403261/
+ * within a non-default tablespace (the name of the directory
+ * depends on version)
+ *
+ * And the relation data files themselves have a filename like:
+ *
+ * <oid>.<segment number>
+ *
+ */
+ spcNode = InvalidOid;
+ dbNode = InvalidOid;
+ relNode = InvalidOid;
+ segNo = 0;
+ matched = false;
+
+ nmatch = sscanf(path, "global/%u.%u", &relNode, &segNo);
+ if (nmatch == 1 || nmatch == 2)
{
- /*
- * Relation data files can be in one of the following directories:
- *
- * global/
- * shared relations
- *
- * base/<db oid>/
- * regular relations, default tablespace
- *
- * pg_tblspc/<tblspc oid>/PG_9.4_201403261/
- * within a non-default tablespace (the name of the directory
- * depends on version)
- *
- * And the relation data files themselves have a filename like:
- *
- * <oid>.<segment number>
- *
- * This regular expression tries to capture all of above.
- */
- const char *datasegment_regex_str =
- "("
- "global"
- "|"
- "base/[0-9]+"
- "|"
- "pg_tblspc/[0-9]+/[PG_0-9.0-9_0-9]+/[0-9]+"
- ")/"
- "[0-9]+(\\.[0-9]+)?$";
- rc = regcomp(&datasegment_regex, datasegment_regex_str, REG_NOSUB | REG_EXTENDED);
- if (rc != 0)
+ spcNode = GLOBALTABLESPACE_OID;
+ dbNode = 0;
+ matched = true;
+ }
+ else
+ {
+ nmatch = sscanf(path, "base/%u/%u.%u", &dbNode, &relNode, &segNo);
+ if (nmatch == 2 || nmatch == 3)
{
- char errmsg[100];
- regerror(rc, &datasegment_regex, errmsg, sizeof(errmsg));
- fprintf(stderr, "could not compile regular expression: %s\n",
- errmsg);
- exit(1);
+ spcNode = DEFAULTTABLESPACE_OID;
+ matched = true;
+ }
+ else
+ {
+ nmatch = sscanf(path, "pg_tblspc/%u/PG_%20s/%u/%u.%u",
+ &spcNode, buf, &dbNode, &relNode, &segNo);
+ if (nmatch == 4 || nmatch == 5)
+ matched = true;
}
}
- rc = regexec(&datasegment_regex, path, 0, NULL, 0);
- if (rc == 0)
- {
- /* it's a data segment file */
- return true;
- }
- else if (rc != REG_NOMATCH)
+ /*
+ * The sscanf tests above can match files that have extra characters
+ * at the end, and the last check can also match a path belonging to
+ * a different version (different TABLESPACE_VERSION_DIRECTORY). To
+ * make eliminate such cases, cross-check that GetRelationPath creates
+ * the exact same filename, when passed the RelFileNode information we
+ * extracted from the filename.
+ */
+ if (matched)
{
- char errmsg[100];
- regerror(rc, &datasegment_regex, errmsg, sizeof(errmsg));
- fprintf(stderr, "could not execute regular expression: %s\n", errmsg);
- exit(1);
+ char *check_path;
+ char *check_path_with_seg;
+
+ check_path = GetRelationPath(dbNode, spcNode, relNode, InvalidBackendId,
+ MAIN_FORKNUM);
+ if (segNo != 0)
+ {
+ check_path_with_seg = psprintf("%s.%u", check_path, segNo);
+ pfree(check_path);
+ }
+ else
+ check_path_with_seg = check_path;
+
+ if (strcmp(check_path_with_seg, path) != 0)
+ matched = false;
}
- return false;
+
+ return matched;
}
static int
my $libpq;
my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' };
-my @contrib_uselibpq =
- ('dblink', 'oid2name', 'pgbench', 'pg_upgrade', 'postgres_fdw', 'vacuumlo');
+my @contrib_uselibpq = (
+ 'dblink', 'oid2name',
+ 'pgbench', 'pg_upgrade',
+ 'postgres_fdw', 'vacuumlo',
+ 'pg_rewind');
my @contrib_uselibpgport = (
'oid2name', 'pgbench',
'pg_standby', 'pg_archivecleanup',
'pg_test_fsync', 'pg_test_timing',
'pg_upgrade', 'pg_xlogdump',
- 'vacuumlo');
+ 'pg_rewind', 'vacuumlo');
my @contrib_uselibpgcommon = (
'oid2name', 'pgbench',
'pg_standby', 'pg_archivecleanup',
'pg_test_fsync', 'pg_test_timing',
'pg_upgrade', 'pg_xlogdump',
- 'vacuumlo');
-my $contrib_extralibs = { 'pgbench' => ['ws2_32.lib'] };
+ 'pg_rewind', 'vacuumlo');
+my $contrib_extralibs = {
+ 'pgbench' => ['ws2_32.lib'],
+ 'pg_rewind' => ['ws2_32.lib'] };
my $contrib_extraincludes =
{ 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
my $contrib_extrasource = {