/* * Write out the new pg_control file. */ static void RewriteControlFile(void) { int fd; char buffer[PG_CONTROL_SIZE]; /* need not be aligned */ /* * Adjust fields as needed to force an empty XLOG starting at the next * available segment. */ newXlogId = ControlFile.logId; newXlogSeg = ControlFile.logSeg; /* adjust in case we are changing segment size */ newXlogSeg *= ControlFile.xlog_seg_size; newXlogSeg = (newXlogSeg + XLogSegSize - 1) / XLogSegSize; /* be sure we wrap around correctly at end of a logfile */ NextLogSeg(newXlogId, newXlogSeg); /* Now we can force the recorded xlog seg size to the right thing. */ ControlFile.xlog_seg_size = XLogSegSize; ControlFile.checkPointCopy.redo.xlogid = newXlogId; ControlFile.checkPointCopy.redo.xrecoff = newXlogSeg * XLogSegSize + SizeOfXLogLongPHD; ControlFile.checkPointCopy.time = (pg_time_t) time(NULL); ControlFile.state = DB_SHUTDOWNED; ControlFile.time = (pg_time_t) time(NULL); ControlFile.checkPoint = ControlFile.checkPointCopy.redo; ControlFile.prevCheckPoint.xlogid = 0; ControlFile.prevCheckPoint.xrecoff = 0; ControlFile.minRecoveryPoint.xlogid = 0; ControlFile.minRecoveryPoint.xrecoff = 0; /* Contents are protected with a CRC */ INIT_CRC32C(ControlFile.crc); COMP_CRC32C(ControlFile.crc, &ControlFile, offsetof(ControlFileData, crc)); FIN_CRC32C(ControlFile.crc); /* INIT_LEGACY_CRC32(ControlFile.crc); COMP_LEGACY_CRC32(ControlFile.crc, (char *) &ControlFile, offsetof(ControlFileData, crc)); FIN_LEGACY_CRC32(ControlFile.crc); */ /* * We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the * excess over sizeof(ControlFileData). This reduces the odds of * premature-EOF errors when reading pg_control. We'll still fail when we * check the contents of the file, but hopefully with a more specific * error than "couldn't read pg_control". */ if (sizeof(ControlFileData) > PG_CONTROL_SIZE) { fprintf(stderr, _("%s: internal error -- sizeof(ControlFileData) is too large ... fix PG_CONTROL_SIZE\n"), progname); exit(1); } memset(buffer, 0, PG_CONTROL_SIZE); memcpy(buffer, &ControlFile, sizeof(ControlFileData)); unlink(XLOG_CONTROL_FILE); fd = open(XLOG_CONTROL_FILE, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, S_IRUSR | S_IWUSR); if (fd < 0) { fprintf(stderr, _("%s: could not create pg_control file: %s\n"), progname, strerror(errno)); exit(1); } errno = 0; if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE) { /* if write didn't set errno, assume problem is no disk space */ if (errno == 0) errno = ENOSPC; fprintf(stderr, _("%s: could not write pg_control file: %s\n"), progname, strerror(errno)); exit(1); } if (fsync(fd) != 0) { fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno)); exit(1); } close(fd); }
/* * Determine starting location for streaming, based on: * 1. If there are existing xlog segments, start at the end of the last one * that is complete (size matches XLogSegSize) * 2. If no valid xlog exists, start from the beginning of the current * WAL segment. */ static XLogRecPtr FindStreamingStart(XLogRecPtr currentpos, uint32 currenttimeline) { DIR *dir; struct dirent *dirent; int i; bool b; uint32 high_log = 0; uint32 high_seg = 0; dir = opendir(basedir); if (dir == NULL) { fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"), progname, basedir, strerror(errno)); disconnect_and_exit(1); } while ((dirent = readdir(dir)) != NULL) { char fullpath[MAXPGPATH]; struct stat statbuf; uint32 tli, log, seg; if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) continue; /* xlog files are always 24 characters */ if (strlen(dirent->d_name) != 24) continue; /* Filenames are always made out of 0-9 and A-F */ b = false; for (i = 0; i < 24; i++) { if (!(dirent->d_name[i] >= '0' && dirent->d_name[i] <= '9') && !(dirent->d_name[i] >= 'A' && dirent->d_name[i] <= 'F')) { b = true; break; } } if (b) continue; /* * Looks like an xlog file. Parse its position. */ if (sscanf(dirent->d_name, "%08X%08X%08X", &tli, &log, &seg) != 3) { fprintf(stderr, _("%s: could not parse transaction log file name \"%s\"\n"), progname, dirent->d_name); disconnect_and_exit(1); } /* Ignore any files that are for another timeline */ if (tli != currenttimeline) continue; /* Check if this is a completed segment or not */ snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name); if (stat(fullpath, &statbuf) != 0) { fprintf(stderr, _("%s: could not stat file \"%s\": %s\n"), progname, fullpath, strerror(errno)); disconnect_and_exit(1); } if (statbuf.st_size == XLOG_SEG_SIZE) { /* Completed segment */ if (log > high_log || (log == high_log && seg > high_seg)) { high_log = log; high_seg = seg; continue; } } else { fprintf(stderr, _("%s: segment file \"%s\" has incorrect size %d, skipping\n"), progname, dirent->d_name, (int) statbuf.st_size); continue; } } closedir(dir); if (high_log > 0 || high_seg > 0) { XLogRecPtr high_ptr; /* * Move the starting pointer to the start of the next segment, since * the highest one we've seen was completed. */ NextLogSeg(high_log, high_seg); high_ptr.xlogid = high_log; high_ptr.xrecoff = high_seg * XLOG_SEG_SIZE; return high_ptr; } else return currentpos; }
/* * Scan existing XLOG files and determine the highest existing WAL address * * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size * are assumed valid (note that we allow the old xlog seg size to differ * from what we're using). On exit, newXlogId and newXlogSeg are set to * suitable values for the beginning of replacement WAL (in our seg size). */ static void FindEndOfXLOG(void) { DIR *xldir; struct dirent *xlde; /* * Initialize the max() computation using the last checkpoint address * from old pg_control. Note that for the moment we are working with * segment numbering according to the old xlog seg size. */ newXlogId = ControlFile.checkPointCopy.redo.xlogid; newXlogSeg = ControlFile.checkPointCopy.redo.xrecoff / ControlFile.xlog_seg_size; /* * Scan the pg_xlog directory to find existing WAL segment files. * We assume any present have been used; in most scenarios this should * be conservative, because of xlog.c's attempts to pre-create files. */ xldir = opendir(XLOGDIR); if (xldir == NULL) { fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } errno = 0; while ((xlde = readdir(xldir)) != NULL) { if (strlen(xlde->d_name) == 24 && strspn(xlde->d_name, "0123456789ABCDEF") == 24) { unsigned int tli, log, seg; sscanf(xlde->d_name, "%08X%08X%08X", &tli, &log, &seg); /* * Note: we take the max of all files found, regardless of their * timelines. Another possibility would be to ignore files of * timelines other than the target TLI, but this seems safer. * Better too large a result than too small... */ if (log > newXlogId || (log == newXlogId && seg > newXlogSeg)) { newXlogId = log; newXlogSeg = seg; } } errno = 0; } #ifdef WIN32 /* * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in * released version */ if (GetLastError() == ERROR_NO_MORE_FILES) errno = 0; #endif if (errno) { fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } closedir(xldir); /* * Finally, convert to new xlog seg size, and advance by one to ensure * we are in virgin territory. */ newXlogSeg *= ControlFile.xlog_seg_size; newXlogSeg = (newXlogSeg + XLogSegSize - 1) / XLogSegSize; /* be sure we wrap around correctly at end of a logfile */ NextLogSeg(newXlogId, newXlogSeg); }