#include "common/file_perm.h"
#include "common/file_utils.h"
+#include "common/logging.h"
#include "pgtar.h"
#include "receivelog.h"
#include "streamutil.h"
WalCompressionMethod compression_method;
int compression_level;
bool sync;
+ const char *lasterrstring; /* if set, takes precedence over lasterrno */
+ int lasterrno;
} DirectoryMethodData;
static DirectoryMethodData *dir_data = NULL;
#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 *
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;
void *lz4buf = NULL;
#endif
+ dir_clear_error();
+
filename = dir_get_file_name(pathname, temp_suffix);
snprintf(tmppath, sizeof(tmppath), "%s/%s",
dir_data->basedir, filename);
*/
fd = open(tmppath, O_WRONLY | O_CREAT | PG_BINARY, pg_file_create_mode);
if (fd < 0)
+ {
+ dir_data->lasterrno = errno;
return NULL;
+ }
#ifdef HAVE_LIBZ
if (dir_data->compression_method == COMPRESSION_GZIP)
gzfp = gzdopen(fd, "wb");
if (gzfp == NULL)
{
+ dir_data->lasterrno = errno;
close(fd);
return NULL;
}
if (gzsetparams(gzfp, dir_data->compression_level,
Z_DEFAULT_STRATEGY) != Z_OK)
{
+ dir_data->lasterrno = errno;
gzclose(gzfp);
return NULL;
}
ctx_out = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
if (LZ4F_isError(ctx_out))
{
+ dir_data->lasterrstring = LZ4F_getErrorName(ctx_out);
close(fd);
return NULL;
}
header_size = LZ4F_compressBegin(ctx, lz4buf, lz4bufsize, NULL);
if (LZ4F_isError(header_size))
{
+ dir_data->lasterrstring = LZ4F_getErrorName(header_size);
(void) LZ4F_freeCompressionContext(ctx);
pg_free(lz4buf);
close(fd);
errno = 0;
if (write(fd, lz4buf, header_size) != header_size)
{
- int save_errno = errno;
-
+ /* If write didn't set errno, assume problem is no disk space */
+ dir_data->lasterrno = errno ? errno : ENOSPC;
(void) LZ4F_compressEnd(ctx, lz4buf, lz4bufsize, NULL);
(void) LZ4F_freeCompressionContext(ctx);
pg_free(lz4buf);
close(fd);
-
- /*
- * If write didn't set errno, assume problem is no disk space.
- */
- errno = save_errno ? save_errno : ENOSPC;
return NULL;
}
}
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;
}
}
if (fsync_fname(tmppath, false) != 0 ||
fsync_parent_path(tmppath) != 0)
{
+ dir_data->lasterrno = errno;
#ifdef HAVE_LIBZ
if (dir_data->compression_method == COMPRESSION_GZIP)
gzclose(gzfp);
DirectoryMethodFile *df = (DirectoryMethodFile *) f;
Assert(f != NULL);
+ dir_clear_error();
#ifdef HAVE_LIBZ
if (dir_data->compression_method == COMPRESSION_GZIP)
+ {
+ 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
#ifdef HAVE_LIBLZ4
NULL);
if (LZ4F_isError(compressed))
+ {
+ dir_data->lasterrstring = LZ4F_getErrorName(compressed);
return -1;
+ }
+ errno = 0;
if (write(df->fd, df->lz4buf, compressed) != compressed)
+ {
+ /* If write didn't set errno, assume problem is no disk space */
+ dir_data->lasterrno = errno ? errno : ENOSPC;
return -1;
+ }
inbuf = ((char *) inbuf) + chunk;
}
}
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;
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;
{
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_method == COMPRESSION_GZIP)
NULL);
if (LZ4F_isError(compressed))
+ {
+ dir_data->lasterrstring = LZ4F_getErrorName(compressed);
return -1;
+ }
+ errno = 0;
if (write(df->fd, df->lz4buf, compressed) != compressed)
+ {
+ /* If write didn't set errno, assume problem is no disk space */
+ dir_data->lasterrno = errno ? errno : ENOSPC;
return -1;
+ }
r = close(df->fd);
}
}
}
+ if (r != 0)
+ dir_data->lasterrno = errno;
+
#ifdef HAVE_LIBLZ4
pg_free(df->lz4buf);
/* supports free on NULL */
static int
dir_sync(Walfile f)
{
+ int r;
+
Assert(f != NULL);
+ dir_clear_error();
if (!dir_data->sync)
return 0;
if (dir_data->compression_method == COMPRESSION_GZIP)
{
if (gzflush(((DirectoryMethodFile *) f)->gzfp, Z_SYNC_FLUSH) != Z_OK)
+ {
+ dir_data->lasterrno = errno;
return -1;
+ }
}
#endif
#ifdef HAVE_LIBLZ4
/* Flush any internal buffers */
compressed = LZ4F_flush(df->ctx, df->lz4buf, df->lz4bufsize, NULL);
if (LZ4F_isError(compressed))
+ {
+ dir_data->lasterrstring = LZ4F_getErrorName(compressed);
return -1;
+ }
+ errno = 0;
if (write(df->fd, df->lz4buf, compressed) != compressed)
+ {
+ /* If write didn't set errno, assume problem is no disk space */
+ dir_data->lasterrno = errno ? errno : ENOSPC;
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;
}
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);
static bool
dir_finish(void)
{
+ dir_clear_error();
+
if (dir_data->sync)
{
/*
* directory entry here as well.
*/
if (fsync_fname(dir_data->basedir, true) != 0)
+ {
+ dir_data->lasterrno = errno;
return false;
+ }
}
return true;
}
{
pg_free(dir_data->basedir);
pg_free(dir_data);
+ dir_data = NULL;
}
int compression_level;
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;
} 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
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;
}
/* Tarfile will always be positioned at the end */
if (!tar_data->compression_level)
{
+ 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
}
#else
else
+ {
/* Can't happen - compression enabled with no libz */
+ tar_data->lasterrno = ENOSYS;
return -1;
+ }
#endif
}
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();
O_WRONLY | O_CREAT | PG_BINARY,
pg_file_create_mode);
if (tar_data->fd < 0)
+ {
+ tar_data->lasterrno = errno;
return NULL;
+ }
#ifdef HAVE_LIBZ
if (tar_data->compression_level)
/* 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");
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;
if (write(tar_data->fd, tar_data->currentfile->header,
TAR_BLOCK_SIZE) != TAR_BLOCK_SIZE)
{
- 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;
}
}
if (!tar_data->compression_level)
{
/* Uncompressed, so pad now */
- tar_write_padding_data(tar_data->currentfile, pad_to_size);
+ 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 + TAR_BLOCK_SIZE,
SEEK_SET) != tar_data->currentfile->ofs_start + TAR_BLOCK_SIZE)
+ {
+ tar_data->lasterrno = errno;
return NULL;
+ }
tar_data->currentfile->currpos = 0;
}
tar_clear_error();
/* Currently not used, so not supported */
- errno = ENOSYS;
+ tar_data->lasterrno = ENOSYS;
return -1;
}
static int
tar_sync(Walfile f)
{
+ int r;
+
Assert(f != NULL);
tar_clear_error();
if (tar_data->compression_level)
return 0;
- return fsync(tar_data->fd);
+ r = fsync(tar_data->fd);
+ if (r < 0)
+ tar_data->lasterrno = errno;
+ return r;
}
static int
* 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);
{
/* Flush the current buffer */
if (!tar_write_compressed_data(NULL, 0, true))
- {
- errno = EINVAL;
return -1;
- }
}
#endif
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_level)
{
errno = 0;
if (write(tar_data->fd, tf->header, TAR_BLOCK_SIZE) != TAR_BLOCK_SIZE)
{
- /* 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;
}
}
/* 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)
+ {
+ /* XXX this seems pretty bogus; why is only this case fatal? */
+ pg_log_fatal("could not fsync file \"%s\": %s",
+ tf->pathname, tar_getlasterror());
exit(1);
+ }
/* Clean up and done */
pg_free(tf->pathname);
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;
}
}
* 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;
}
}
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) != 0)
- return false;
- if (fsync_parent_path(tar_data->tarfilename) != 0)
+ if (fsync_fname(tar_data->tarfilename, false) != 0 ||
+ fsync_parent_path(tar_data->tarfilename) != 0)
+ {
+ tar_data->lasterrno = errno;
return false;
+ }
}
return true;
pg_free(tar_data->zlibOut);
#endif
pg_free(tar_data);
+ tar_data = NULL;
}