diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/walmethods.c index f662379fbed..3151a88117a 100644 --- a/src/bin/pg_basebackup/walmethods.c +++ b/src/bin/pg_basebackup/walmethods.c @@ -43,6 +43,8 @@ typedef struct DirectoryMethodData char *basedir; int compression; bool sync; + const char *lasterrstring; /* if set, takes precedence over lasterrno */ + int lasterrno; } DirectoryMethodData; static DirectoryMethodData *dir_data = NULL; @@ -61,11 +63,17 @@ typedef struct DirectoryMethodFile #endif } DirectoryMethodFile; +#define dir_clear_error() \ + (dir_data->lasterrstring = NULL, dir_data->lasterrno = 0) +#define dir_set_error(msg) \ + (dir_data->lasterrstring = _(msg)) + static const char * dir_getlasterror(void) { - /* Directory method always sets errno, so just use strerror */ - return strerror(errno); + if (dir_data->lasterrstring) + return dir_data->lasterrstring; + return strerror(dir_data->lasterrno); } static char * @@ -83,7 +91,7 @@ dir_get_file_name(const char *pathname, const char *temp_suffix) static Walfile dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_size) { - static char tmppath[MAXPGPATH]; + char tmppath[MAXPGPATH]; char *filename; int fd; DirectoryMethodFile *f; @@ -91,6 +99,8 @@ dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ gzFile gzfp = NULL; #endif + dir_clear_error(); + filename = dir_get_file_name(pathname, temp_suffix); snprintf(tmppath, sizeof(tmppath), "%s/%s", dir_data->basedir, filename); @@ -104,7 +114,10 @@ dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ */ fd = open(tmppath, O_WRONLY | O_CREAT | PG_BINARY, S_IRUSR | S_IWUSR); if (fd < 0) + { + dir_data->lasterrno = errno; return NULL; + } #ifdef HAVE_LIBZ if (dir_data->compression > 0) @@ -112,6 +125,7 @@ dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ gzfp = gzdopen(fd, "wb"); if (gzfp == NULL) { + dir_data->lasterrno = errno; close(fd); return NULL; } @@ -119,6 +133,7 @@ dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ if (gzsetparams(gzfp, dir_data->compression, Z_DEFAULT_STRATEGY) != Z_OK) { + dir_data->lasterrno = errno; gzclose(gzfp); return NULL; } @@ -137,24 +152,17 @@ dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ errno = 0; if (write(fd, zerobuf.data, XLOG_BLCKSZ) != XLOG_BLCKSZ) { - int save_errno = errno; - + /* If write didn't set errno, assume problem is no disk space */ + dir_data->lasterrno = errno ? errno : ENOSPC; close(fd); - - /* - * If write didn't set errno, assume problem is no disk space. - */ - errno = save_errno ? save_errno : ENOSPC; return NULL; } } if (lseek(fd, 0, SEEK_SET) != 0) { - int save_errno = errno; - + dir_data->lasterrno = errno; close(fd); - errno = save_errno; return NULL; } } @@ -170,6 +178,7 @@ dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ if (fsync_fname(tmppath, false, progname) != 0 || fsync_parent_path(tmppath, progname) != 0) { + dir_data->lasterrno = errno; #ifdef HAVE_LIBZ if (dir_data->compression > 0) gzclose(gzfp); @@ -202,13 +211,30 @@ dir_write(Walfile f, const void *buf, size_t count) DirectoryMethodFile *df = (DirectoryMethodFile *) f; Assert(f != NULL); + dir_clear_error(); #ifdef HAVE_LIBZ if (dir_data->compression > 0) + { + errno = 0; r = (ssize_t) gzwrite(df->gzfp, buf, count); + if (r != count) + { + /* If write didn't set errno, assume problem is no disk space */ + dir_data->lasterrno = errno ? errno : ENOSPC; + } + } else #endif + { + errno = 0; r = write(df->fd, buf, count); + if (r != count) + { + /* If write didn't set errno, assume problem is no disk space */ + dir_data->lasterrno = errno ? errno : ENOSPC; + } + } if (r > 0) df->currpos += r; return r; @@ -218,6 +244,7 @@ static off_t dir_get_current_pos(Walfile f) { Assert(f != NULL); + dir_clear_error(); /* Use a cached value to prevent lots of reseeks */ return ((DirectoryMethodFile *) f)->currpos; @@ -228,10 +255,11 @@ dir_close(Walfile f, WalCloseMethod method) { int r; DirectoryMethodFile *df = (DirectoryMethodFile *) f; - static char tmppath[MAXPGPATH]; - static char tmppath2[MAXPGPATH]; + char tmppath[MAXPGPATH]; + char tmppath2[MAXPGPATH]; Assert(f != NULL); + dir_clear_error(); #ifdef HAVE_LIBZ if (dir_data->compression > 0) @@ -291,6 +319,9 @@ dir_close(Walfile f, WalCloseMethod method) } } + if (r != 0) + dir_data->lasterrno = errno; + pg_free(df->pathname); pg_free(df->fullpath); if (df->temp_suffix) @@ -303,7 +334,10 @@ dir_close(Walfile f, WalCloseMethod method) static int dir_sync(Walfile f) { + int r; + Assert(f != NULL); + dir_clear_error(); if (!dir_data->sync) return 0; @@ -312,24 +346,33 @@ dir_sync(Walfile f) if (dir_data->compression > 0) { if (gzflush(((DirectoryMethodFile *) f)->gzfp, Z_SYNC_FLUSH) != Z_OK) + { + dir_data->lasterrno = errno; return -1; + } } #endif - return fsync(((DirectoryMethodFile *) f)->fd); + r = fsync(((DirectoryMethodFile *) f)->fd); + if (r < 0) + dir_data->lasterrno = errno; + return r; } static ssize_t dir_get_file_size(const char *pathname) { struct stat statbuf; - static char tmppath[MAXPGPATH]; + char tmppath[MAXPGPATH]; snprintf(tmppath, sizeof(tmppath), "%s/%s", dir_data->basedir, pathname); if (stat(tmppath, &statbuf) != 0) + { + dir_data->lasterrno = errno; return -1; + } return statbuf.st_size; } @@ -343,9 +386,11 @@ dir_compression(void) static bool dir_existsfile(const char *pathname) { - static char tmppath[MAXPGPATH]; + char tmppath[MAXPGPATH]; int fd; + dir_clear_error(); + snprintf(tmppath, sizeof(tmppath), "%s/%s", dir_data->basedir, pathname); @@ -359,6 +404,8 @@ dir_existsfile(const char *pathname) static bool dir_finish(void) { + dir_clear_error(); + if (dir_data->sync) { /* @@ -366,7 +413,10 @@ dir_finish(void) * directory entry here as well. */ if (fsync_fname(dir_data->basedir, true, progname) != 0) + { + dir_data->lasterrno = errno; return false; + } } return true; } @@ -403,6 +453,7 @@ FreeWalDirectoryMethod(void) { pg_free(dir_data->basedir); pg_free(dir_data); + dir_data = NULL; } @@ -427,7 +478,8 @@ typedef struct TarMethodData int compression; bool sync; TarMethodFile *currentfile; - char lasterror[1024]; + const char *lasterrstring; /* if set, takes precedence over lasterrno */ + int lasterrno; #ifdef HAVE_LIBZ z_streamp zp; void *zlibOut; @@ -435,19 +487,17 @@ typedef struct TarMethodData } TarMethodData; static TarMethodData *tar_data = NULL; -#define tar_clear_error() tar_data->lasterror[0] = '\0' -#define tar_set_error(msg) strlcpy(tar_data->lasterror, _(msg), sizeof(tar_data->lasterror)) +#define tar_clear_error() \ + (tar_data->lasterrstring = NULL, tar_data->lasterrno = 0) +#define tar_set_error(msg) \ + (tar_data->lasterrstring = _(msg)) static const char * tar_getlasterror(void) { - /* - * If a custom error is set, return that one. Otherwise, assume errno is - * set and return that one. - */ - if (tar_data->lasterror[0]) - return tar_data->lasterror; - return strerror(errno); + if (tar_data->lasterrstring) + return tar_data->lasterrstring; + return strerror(tar_data->lasterrno); } #ifdef HAVE_LIBZ @@ -475,11 +525,8 @@ tar_write_compressed_data(void *buf, size_t count, bool flush) errno = 0; if (write(tar_data->fd, tar_data->zlibOut, len) != len) { - /* - * If write didn't set errno, assume problem is no disk space. - */ - if (errno == 0) - errno = ENOSPC; + /* If write didn't set errno, assume problem is no disk space */ + tar_data->lasterrno = errno ? errno : ENOSPC; return false; } @@ -516,9 +563,15 @@ tar_write(Walfile f, const void *buf, size_t count) /* Tarfile will always be positioned at the end */ if (!tar_data->compression) { + errno = 0; r = write(tar_data->fd, buf, count); - if (r > 0) - ((TarMethodFile *) f)->currpos += r; + if (r != count) + { + /* If write didn't set errno, assume problem is no disk space */ + tar_data->lasterrno = errno ? errno : ENOSPC; + return -1; + } + ((TarMethodFile *) f)->currpos += r; return r; } #ifdef HAVE_LIBZ @@ -531,8 +584,11 @@ tar_write(Walfile f, const void *buf, size_t count) } #else else + { /* Can't happen - compression enabled with no libz */ + tar_data->lasterrno = ENOSYS; return -1; + } #endif } @@ -570,7 +626,6 @@ tar_get_file_name(const char *pathname, const char *temp_suffix) static Walfile tar_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_size) { - int save_errno; char *tmppath; tar_clear_error(); @@ -583,7 +638,10 @@ tar_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ tar_data->fd = open(tar_data->tarfilename, O_WRONLY | O_CREAT | PG_BINARY, S_IRUSR | S_IWUSR); if (tar_data->fd < 0) + { + tar_data->lasterrno = errno; return NULL; + } #ifdef HAVE_LIBZ if (tar_data->compression) @@ -613,7 +671,6 @@ tar_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ /* There's no tar header itself, the file starts with regular files */ } - Assert(tar_data->currentfile == NULL); if (tar_data->currentfile != NULL) { tar_set_error("implementation error: tar files can't have more than one open file"); @@ -655,10 +712,9 @@ tar_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ tar_data->currentfile->ofs_start = lseek(tar_data->fd, 0, SEEK_CUR); if (tar_data->currentfile->ofs_start == -1) { - save_errno = errno; + tar_data->lasterrno = errno; pg_free(tar_data->currentfile); tar_data->currentfile = NULL; - errno = save_errno; return NULL; } tar_data->currentfile->currpos = 0; @@ -668,11 +724,10 @@ tar_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ errno = 0; if (write(tar_data->fd, tar_data->currentfile->header, 512) != 512) { - save_errno = errno; + /* If write didn't set errno, assume problem is no disk space */ + tar_data->lasterrno = errno ? errno : ENOSPC; pg_free(tar_data->currentfile); tar_data->currentfile = NULL; - /* if write didn't set errno, assume problem is no disk space */ - errno = save_errno ? save_errno : ENOSPC; return NULL; } } @@ -704,10 +759,16 @@ tar_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_ if (!tar_data->compression) { /* Uncompressed, so pad now */ - tar_write_padding_data(tar_data->currentfile, pad_to_size); - /* Seek back to start */ - if (lseek(tar_data->fd, tar_data->currentfile->ofs_start + 512, SEEK_SET) != tar_data->currentfile->ofs_start + 512) + if (!tar_write_padding_data(tar_data->currentfile, pad_to_size)) return NULL; + /* Seek back to start */ + if (lseek(tar_data->fd, + tar_data->currentfile->ofs_start + 512, + SEEK_SET) != tar_data->currentfile->ofs_start + 512) + { + tar_data->lasterrno = errno; + return NULL; + } tar_data->currentfile->currpos = 0; } @@ -722,7 +783,7 @@ tar_get_file_size(const char *pathname) tar_clear_error(); /* Currently not used, so not supported */ - errno = ENOSYS; + tar_data->lasterrno = ENOSYS; return -1; } @@ -744,6 +805,8 @@ tar_get_current_pos(Walfile f) static int tar_sync(Walfile f) { + int r; + Assert(f != NULL); tar_clear_error(); @@ -757,7 +820,10 @@ tar_sync(Walfile f) if (tar_data->compression) return 0; - return fsync(tar_data->fd); + r = fsync(tar_data->fd); + if (r < 0) + tar_data->lasterrno = errno; + return r; } static int @@ -784,7 +850,10 @@ tar_close(Walfile f, WalCloseMethod method) * allow writing of the very last file. */ if (ftruncate(tar_data->fd, tf->ofs_start) != 0) + { + tar_data->lasterrno = errno; return -1; + } pg_free(tf->pathname); pg_free(tf); @@ -845,10 +914,7 @@ tar_close(Walfile f, WalCloseMethod method) { /* Flush the current buffer */ if (!tar_write_compressed_data(NULL, 0, true)) - { - errno = EINVAL; return -1; - } } #endif @@ -869,15 +935,17 @@ tar_close(Walfile f, WalCloseMethod method) print_tar_number(&(tf->header[148]), 8, tarChecksum(((TarMethodFile *) f)->header)); if (lseek(tar_data->fd, tf->ofs_start, SEEK_SET) != ((TarMethodFile *) f)->ofs_start) + { + tar_data->lasterrno = errno; return -1; + } if (!tar_data->compression) { errno = 0; if (write(tar_data->fd, tf->header, 512) != 512) { - /* if write didn't set errno, assume problem is no disk space */ - if (errno == 0) - errno = ENOSPC; + /* If write didn't set errno, assume problem is no disk space */ + tar_data->lasterrno = errno ? errno : ENOSPC; return -1; } } @@ -906,7 +974,10 @@ tar_close(Walfile f, WalCloseMethod method) /* Move file pointer back down to end, so we can write the next file */ if (lseek(tar_data->fd, 0, SEEK_END) < 0) + { + tar_data->lasterrno = errno; return -1; + } /* Always fsync on close, so the padding gets fsynced */ if (tar_sync(f) < 0) @@ -948,9 +1019,8 @@ tar_finish(void) errno = 0; if (write(tar_data->fd, zerobuf, sizeof(zerobuf)) != sizeof(zerobuf)) { - /* if write didn't set errno, assume problem is no disk space */ - if (errno == 0) - errno = ENOSPC; + /* If write didn't set errno, assume problem is no disk space */ + tar_data->lasterrno = errno ? errno : ENOSPC; return false; } } @@ -985,8 +1055,7 @@ tar_finish(void) * If write didn't set errno, assume problem is no disk * space. */ - if (errno == 0) - errno = ENOSPC; + tar_data->lasterrno = errno ? errno : ENOSPC; return false; } } @@ -1006,20 +1075,28 @@ tar_finish(void) if (tar_data->sync) { if (fsync(tar_data->fd) != 0) + { + tar_data->lasterrno = errno; return false; + } } if (close(tar_data->fd) != 0) + { + tar_data->lasterrno = errno; return false; + } tar_data->fd = -1; if (tar_data->sync) { - if (fsync_fname(tar_data->tarfilename, false, progname) != 0) - return false; - if (fsync_parent_path(tar_data->tarfilename, progname) != 0) + if (fsync_fname(tar_data->tarfilename, false, progname) != 0 || + fsync_parent_path(tar_data->tarfilename, progname) != 0) + { + tar_data->lasterrno = errno; return false; + } } return true; @@ -1067,4 +1144,5 @@ FreeWalTarMethod(void) pg_free(tar_data->zlibOut); #endif pg_free(tar_data); + tar_data = NULL; }