Files
openGauss-server/src/bin/pg_probackup/file.cpp

2469 lines
74 KiB
C++

/*-------------------------------------------------------------------------
*
* file.c
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
*
*-------------------------------------------------------------------------
*/
#include "pg_probackup.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include "file.h"
#include "storage/checksum.h"
#include "storage/file/fio_device.h"
#include "common/fe_memutils.h"
#define PRINTF_BUF_SIZE 1024
static __thread unsigned long fio_fdset = 0;
static __thread void* fio_stdin_buffer;
__thread int fio_stdout = 0;
__thread int fio_stdin = 0;
static __thread int fio_stderr = 0;
fio_location MyLocation;
typedef struct
{
BlockNumber nblocks;
BlockNumber segmentno;
XLogRecPtr horizonLsn;
uint32 checksumVersion;
int calg;
int clevel;
int bitmapsize;
int path_len;
} fio_send_request;
typedef struct
{
char path[MAXPGPATH];
bool exclude;
bool follow_symlink;
bool add_root;
bool backup_logs;
bool exclusive_backup;
bool skip_hidden;
int external_dir_num;
bool backup_replslots;
} fio_list_dir_request;
typedef struct
{
mode_t mode;
size_t size;
time_t mtime;
bool is_datafile;
bool compressed_file;
uint16 compressed_chunk_size;
uint8 compressed_algorithm;
bool is_database;
Oid tblspcOid;
Oid dbOid;
Oid relOid;
ForkName forkName;
int segno;
int external_dir_num;
int linked_len;
} fio_pgFile;
typedef struct
{
BlockNumber n_blocks;
BlockNumber segmentno;
XLogRecPtr stop_lsn;
uint32 checksumVersion;
} fio_checksum_map_request;
typedef struct
{
BlockNumber n_blocks;
BlockNumber segmentno;
XLogRecPtr shift_lsn;
uint32 checksumVersion;
} fio_lsn_map_request;
/* Convert FIO pseudo handle to index in file descriptor array */
#define fio_fileno(f) (((size_t)f - 1) | FIO_PIPE_MARKER)
#if defined(WIN32)
#undef open(a, b, c)
#undef fopen(a, b)
#endif
/* Use specified file descriptors as stdin/stdout for FIO functions */
void fio_redirect(int in, int out, int err)
{
fio_stdin = in;
fio_stdout = out;
fio_stderr = err;
}
void fio_error(int rc, int size, char const* file, int line)
{
if (remote_agent)
{
fprintf(stderr, "%s:%d: processed %d bytes instead of %d: %s\n", file, line, rc, size, rc >= 0 ? "end of data" : strerror(errno));
exit(EXIT_FAILURE);
}
else
{
char buf[PRINTF_BUF_SIZE+1];
int err_size = read(fio_stderr, buf, PRINTF_BUF_SIZE);
if (err_size > 0)
{
buf[err_size] = '\0';
elog(ERROR, "Agent error: %s", buf);
}
else
elog(ERROR, "Communication error: %s", rc >= 0 ? "end of data" : strerror(errno));
}
}
/* Check if file descriptor is local or remote (created by FIO) */
static bool fio_is_remote_fd(int fd)
{
return (fd & FIO_PIPE_MARKER) != 0;
}
#ifdef WIN32
#undef stat
/*
* The stat() function in win32 is not guaranteed to update the st_size
* field when run. So we define our own version that uses the Win32 API
* to update this field.
*/
static int
fio_safestat(const char *path, struct stat *buf)
{
int r;
WIN32_FILE_ATTRIBUTE_DATA attr;
r = stat(path, buf);
if (r < 0)
return r;
if (!GetFileAttributesEx(path, GetFileExInfoStandard, &attr))
{
errno = ENOENT;
return -1;
}
/*
* XXX no support for large files here, but we don't do that in general on
* Win32 yet.
*/
buf->st_size = attr.nFileSizeLow;
return 0;
}
#define stat(x, y) fio_safestat(x, y)
/* TODO: use real pread on Linux */
static ssize_t pread(int fd, void* buf, size_t size, off_t off)
{
off_t rc = lseek(fd, off, SEEK_SET);
if (rc != off)
return -1;
return read(fd, buf, size);
}
static int remove_file_or_dir(char const* path)
{
int rc = remove(path);
#ifdef WIN32
if (rc < 0 && errno == EACCESS)
rc = rmdir(path);
#endif
return rc;
}
#else
#define remove_file_or_dir(path) remove(path)
#endif
/* Check if specified location is local for current node */
bool fio_is_remote(fio_location location)
{
bool is_remote = MyLocation != FIO_LOCAL_HOST
&& location != FIO_LOCAL_HOST
&& location != MyLocation;
if (is_remote && !fio_stdin && !launch_agent())
elog(ERROR, "Failed to establish SSH connection: %s", strerror(errno));
return is_remote;
}
/* Check if specified location is local for current node */
bool fio_is_remote_simple(fio_location location)
{
bool is_remote = MyLocation != FIO_LOCAL_HOST
&& location != FIO_LOCAL_HOST
&& location != MyLocation;
return is_remote;
}
/* Check if specified location is for current node */
bool fio_is_dss(fio_location location)
{
return location == FIO_DSS_HOST;
}
/* Try to read specified amount of bytes unless error or EOF are encountered */
ssize_t fio_read_all(int fd, void* buf, size_t size)
{
size_t offs = 0;
while (offs < size)
{
ssize_t rc = read(fd, (char*)buf + offs, size - offs);
if (rc < 0)
{
if (errno == EINTR)
continue;
elog(ERROR, "fio_read_all error, fd %i: %s", fd, strerror(errno));
return rc;
}
else if (rc == 0)
break;
offs += rc;
}
return offs;
}
/* Try to write specified amount of bytes unless error is encountered */
ssize_t fio_write_all(int fd, void const* buf, size_t size)
{
size_t offs = 0;
while (offs < size)
{
ssize_t rc = write(fd, (const char*)buf + offs, size - offs);
if (rc <= 0)
{
if (errno == EINTR)
continue;
elog(ERROR, "fio_write_all error, fd %i: %s", fd, strerror(errno));
return rc;
}
offs += rc;
}
return offs;
}
/* Get version of remote agent */
int fio_get_agent_version(void)
{
fio_header hdr;
hdr.cop = FIO_AGENT_VERSION;
hdr.size = 0;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
return hdr.arg;
}
/* Open input stream. Remote file is fetched to the in-memory buffer and then accessed through Linux fmemopen */
FILE* fio_open_stream(char const* path, fio_location location)
{
FILE* f;
if (fio_is_remote(location))
{
fio_header hdr;
hdr.cop = FIO_LOAD;
hdr.size = strlen(path) + 1;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, hdr.size), hdr.size);
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_SEND);
if (hdr.size > 0)
{
Assert(fio_stdin_buffer == NULL);
fio_stdin_buffer = pgut_malloc(hdr.size);
IO_CHECK(fio_read_all(fio_stdin, fio_stdin_buffer, hdr.size), hdr.size);
#ifdef WIN32
f = tmpfile();
IO_CHECK(fwrite(f, 1, hdr.size, fio_stdin_buffer), hdr.size);
SYS_CHECK(fseek(f, 0, SEEK_SET));
#else
f = fmemopen(fio_stdin_buffer, hdr.size, "r");
#endif
}
else
{
f = NULL;
}
}
else
{
f = fopen(path, "rt");
}
return f;
}
/* Close input stream */
int fio_close_stream(FILE* f)
{
if (fio_stdin_buffer)
{
free(fio_stdin_buffer);
fio_stdin_buffer = NULL;
}
return fclose(f);
}
/* Open directory */
DIR* fio_opendir(char const* path, fio_location location)
{
DIR* dir;
if (fio_is_remote(location))
{
int i;
fio_header hdr;
unsigned long mask;
mask = fio_fdset;
for (i = 0; (mask & 1) != 0; i++, mask >>= 1);
if (i == FIO_FDMAX) {
elog(ERROR, "Descriptor pool for remote files is exhausted, "
"probably too many remote directories are opened");
}
hdr.cop = FIO_OPENDIR;
hdr.handle = i;
hdr.size = strlen(path) + 1;
fio_fdset |= 1 << i;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, hdr.size), hdr.size);
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.arg != 0)
{
errno = hdr.arg;
fio_fdset &= ~(1 << hdr.handle);
return NULL;
}
dir = (DIR*)(size_t)(i + 1);
}
else
{
dir = opendir(path);
}
return dir;
}
/* Get next directory entry */
struct dirent* fio_readdir(DIR *dir)
{
if (fio_is_remote_file((FILE*)dir))
{
fio_header hdr;
static __thread struct dirent entry;
hdr.cop = FIO_READDIR;
hdr.handle = (size_t)dir - 1;
hdr.size = 0;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_SEND);
if (hdr.size) {
Assert(hdr.size == sizeof(entry));
IO_CHECK(fio_read_all(fio_stdin, &entry, sizeof(entry)), sizeof(entry));
}
return hdr.size ? &entry : NULL;
}
else
{
return readdir(dir);
}
}
/* Close directory */
int fio_closedir(DIR *dir)
{
if (fio_is_remote_file((FILE*)dir))
{
fio_header hdr;
hdr.cop = FIO_CLOSEDIR;
hdr.handle = (size_t)dir - 1;
hdr.size = 0;
fio_fdset &= ~(1 << hdr.handle);
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
return 0;
}
else
{
return closedir(dir);
}
}
/* Open file */
int fio_open(char const* path, int mode, fio_location location)
{
int fd;
if (fio_is_remote(location))
{
int i;
fio_header hdr;
unsigned long mask;
mask = fio_fdset;
for (i = 0; (mask & 1) != 0; i++, mask >>= 1);
if (i == FIO_FDMAX)
elog(ERROR, "Descriptor pool for remote files is exhausted, "
"probably too many remote files are opened");
hdr.cop = FIO_OPEN;
hdr.handle = i;
hdr.size = strlen(path) + 1;
hdr.arg = mode;
fio_fdset |= 1 << i;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, hdr.size), hdr.size);
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.arg != 0)
{
errno = hdr.arg;
fio_fdset &= ~(1 << hdr.handle);
return -1;
}
fd = i | FIO_PIPE_MARKER;
}
else
{
fd = open(path, mode, FILE_PERMISSIONS);
}
return fd;
}
/* Close ssh session */
void
fio_disconnect(void)
{
if (fio_stdin)
{
fio_header hdr;
hdr.cop = FIO_DISCONNECT;
hdr.size = 0;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_DISCONNECTED);
SYS_CHECK(close(fio_stdin));
SYS_CHECK(close(fio_stdout));
fio_stdin = 0;
fio_stdout = 0;
wait_ssh();
}
}
/* Open stdio file */
FILE* fio_fopen(char const* path, char const* mode, fio_location location)
{
FILE *f = NULL;
if (fio_is_remote(location))
{
int flags = 0;
int fd;
if (strcmp(mode, PG_BINARY_W) == 0) {
flags = O_TRUNC|PG_BINARY|O_RDWR|O_CREAT;
} else if (strcmp(mode, "w") == 0) {
flags = O_TRUNC|O_RDWR|O_CREAT;
} else if (strcmp(mode, PG_BINARY_R) == 0) {
flags = O_RDONLY|PG_BINARY;
} else if (strcmp(mode, "r") == 0) {
flags = O_RDONLY;
} else if (strcmp(mode, PG_BINARY_R "+") == 0) {
/* stdio fopen("rb+") actually doesn't create unexisted file, but probackup frequently
* needs to open existed file or create new one if not exists.
* In stdio it can be done using two fopen calls: fopen("r+") and if failed then fopen("w").
* But to eliminate extra call which especially critical in case of remote connection
* we change r+ semantic to create file if not exists.
*/
flags = O_RDWR|O_CREAT|PG_BINARY;
} else if (strcmp(mode, "r+") == 0) { /* see comment above */
flags |= O_RDWR|O_CREAT;
} else if (strcmp(mode, "a") == 0) {
flags |= O_CREAT|O_RDWR|O_APPEND;
} else {
Assert(false);
}
fd = fio_open(path, flags, location);
if (fd >= 0)
f = (FILE*)(size_t)((fd + 1) & ~FIO_PIPE_MARKER);
}
else
{
f = fopen(path, mode);
if (f == NULL && strcmp(mode, PG_BINARY_R "+") == 0)
f = fopen(path, PG_BINARY_W);
}
return f;
}
int fio_fprintf(FILE* f, char const* format, ...) __attribute__ ((format (printf, 2, 3)));
static char *ProcessErrorIn(int out, fio_header &hdr, const char *fromFullpath);
/* Format output to file stream */
int fio_fprintf(FILE* f, char const* format, ...)
{
int rc;
va_list args;
va_start (args, format);
if (fio_is_remote_file(f))
{
char buf[PRINTF_BUF_SIZE];
#ifdef HAS_VSNPRINTF
rc = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, format, args);
securec_check_ss_c(rc, "\0", "\0");
#else
rc = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, format, args);
securec_check_ss_c(rc, "\0", "\0");
#endif
if (rc > 0) {
fio_fwrite(f, buf, rc);
}
}
else
{
rc = vfprintf(f, format, args);
}
va_end (args);
return rc;
}
/* Flush stream data (does nothing for remote file and dss file) */
int fio_fflush(FILE* f)
{
int rc = 0;
if (!fio_is_remote_file(f))
rc = fflush(f);
return rc;
}
/* Sync file to the disk (does nothing for remote file) */
int fio_flush(int fd)
{
return fio_is_remote_fd(fd) ? 0 : fsync(fd);
}
/* Close output stream */
int fio_fclose(FILE* f)
{
return fio_is_remote_file(f)
? fio_close(fio_fileno(f))
: fclose(f);
}
/* Close file */
int fio_close(int fd)
{
if (fio_is_remote_fd(fd))
{
fio_header hdr;
hdr.cop = FIO_CLOSE;
hdr.handle = fd & ~FIO_PIPE_MARKER;
hdr.size = 0;
fio_fdset &= ~(1 << hdr.handle);
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
/* Note, that file is closed without waiting for confirmation */
return 0;
}
else
{
return close(fd);
}
}
/* Truncate stdio file */
int fio_ftruncate(FILE* f, off_t size)
{
return fio_is_remote_file(f)
? fio_truncate(fio_fileno(f), size)
: ftruncate(fileno(f), size);
}
/* Truncate file */
int fio_truncate(int fd, off_t size)
{
if (fio_is_remote_fd(fd))
{
fio_header hdr;
hdr.cop = FIO_TRUNCATE;
hdr.handle = fd & ~FIO_PIPE_MARKER;
hdr.size = 0;
hdr.arg = size;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
return 0;
}
else
{
return ftruncate(fd, size);
}
}
/*
* Read file from specified location.
*/
int fio_pread(FILE* f, void* buf, off_t offs, PageCompression* pageCompression, int size)
{
if (fio_is_remote_file(f))
{
int fd = fio_fileno(f);
fio_header hdr;
hdr.cop = FIO_PREAD;
hdr.handle = fd & ~FIO_PIPE_MARKER;
hdr.size = 0;
hdr.arg = offs;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_SEND);
if (hdr.size != 0 && hdr.size <= BLCKSZ)
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
/* TODO: error handling */
return hdr.arg;
}
else
{
/* For local file, opened by fopen, we should use stdio functions */
if (pageCompression) {
return (int)pageCompression->ReadCompressedBuffer((BlockNumber)(offs / BLCKSZ), (char*)buf, size, true);
} else {
int rc = fseek(f, offs, SEEK_SET);
if (rc < 0) {
return rc;
}
return fread(buf, 1, size, f);
}
}
}
/* Set position in stdio file */
int fio_fseek(FILE* f, off_t offs)
{
return fio_is_remote_file(f)
? fio_seek(fio_fileno(f), offs)
: fseek(f, offs, SEEK_SET);
}
/* Set position in file */
int fio_seek(int fd, off_t offs)
{
if (fio_is_remote_fd(fd))
{
fio_header hdr;
hdr.cop = FIO_SEEK;
hdr.handle = fd & ~FIO_PIPE_MARKER;
hdr.size = 0;
hdr.arg = offs;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
return 0;
}
else
{
return lseek(fd, offs, SEEK_SET);
}
}
/* Write data to stdio file */
size_t fio_fwrite(FILE* f, void const* buf, size_t size)
{
if (fio_is_remote_file(f))
{
return (size_t)fio_write(fio_fileno(f), buf, size);
}
else if (is_dss_file_dec(f))
{
/* size must be multiples of ALIGNOF_BUFFER in dss */
char align_buf[size] __attribute__((__aligned__(ALIGNOF_BUFFER))); /* need to be aligned */
errno_t rc = memcpy_s(align_buf, size, buf, size);
securec_check_c(rc, "\0", "\0");
return dss_fwrite_file(align_buf, 1, size, f);
}
else
{
return fwrite(buf, 1, size, f);
}
}
/* Write data to the file */
ssize_t fio_write(int fd, void const* buf, size_t size)
{
if (fio_is_remote_fd(fd))
{
fio_header hdr;
hdr.cop = FIO_WRITE;
hdr.handle = fd & ~FIO_PIPE_MARKER;
hdr.size = size;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, buf, size), size);
return size;
}
else
{
return write(fd, buf, size);
}
}
int32
fio_decompress(void* dst, void const* src, size_t size, int compress_alg)
{
const char *errormsg = NULL;
int32 uncompressed_size = do_decompress(dst, BLCKSZ, src, size, (CompressAlg)compress_alg, &errormsg);
if (uncompressed_size < 0 && errormsg != NULL)
{
elog(WARNING, "An error occured during decompressing block: %s", errormsg);
return -1;
}
if (uncompressed_size != BLCKSZ)
{
elog(ERROR, "Page uncompressed to %d bytes != BLCKSZ",
uncompressed_size);
return -1;
}
return uncompressed_size;
}
/* Write data to the file */
ssize_t fio_fwrite_compressed(FILE* f, void const* buf, size_t size, int compress_alg, const char *to_fullpath, char *preWriteBuf, int *preWriteOff, int *targetSize)
{
if (fio_is_remote_file(f))
{
fio_header hdr;
hdr.cop = FIO_WRITE_COMPRESSED;
hdr.handle = fio_fileno(f) & ~FIO_PIPE_MARKER;
hdr.size = size;
hdr.arg = compress_alg;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, buf, size), size);
return size;
}
else
{
if (preWriteBuf != NULL)
{
int32 uncompressed_size = fio_decompress(preWriteBuf + (*preWriteOff), buf, size, compress_alg);
*preWriteOff += uncompressed_size;
if (*preWriteOff > DSS_BLCKSZ)
{
pg_free(preWriteBuf);
elog(ERROR, "Offset %d is bigger than preWriteBuf size %d", *preWriteOff, DSS_BLCKSZ);
}
if (*preWriteOff == DSS_BLCKSZ)
{
int write_len = fio_fwrite(f, preWriteBuf, DSS_BLCKSZ);
if (write_len != DSS_BLCKSZ)
{
pg_free(preWriteBuf);
elog(ERROR, "Cannot write block of \"%s\": %s, size: %u",
to_fullpath, strerror(errno), DSS_BLCKSZ);
}
*preWriteOff = 0;
*targetSize -= DSS_BLCKSZ;
}
return uncompressed_size;
}
else
{
char uncompressed_buf[BLCKSZ];
int32 uncompressed_size = fio_decompress(uncompressed_buf, buf, size, compress_alg);
return (uncompressed_size < 0)
? uncompressed_size
: fio_fwrite(f, uncompressed_buf, uncompressed_size);
}
}
}
void fio_construct_compressed(void const *buf, size_t size)
{
fio_header hdr;
hdr.cop = (uint32)FIO_COSTRUCT_COMPRESSED;
hdr.size = (unsigned)size;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, buf, size), size);
}
static ssize_t
fio_write_compressed_impl(int fd, void const* buf, size_t size, int compress_alg)
{
char uncompressed_buf[BLCKSZ];
int32 uncompressed_size = fio_decompress(uncompressed_buf, buf, size, compress_alg);
return fio_write_all(fd, uncompressed_buf, uncompressed_size);
}
/* Read data from stdio file */
ssize_t fio_fread(FILE* f, void* buf, size_t size)
{
size_t rc;
if (fio_is_remote_file(f))
return fio_read(fio_fileno(f), buf, size);
rc = fread(buf, 1, size, f);
return rc == 0 && !feof(f) ? -1 : rc;
}
/* Read data from file */
ssize_t fio_read(int fd, void* buf, size_t size)
{
if (fio_is_remote_fd(fd))
{
fio_header hdr;
hdr.cop = FIO_READ;
hdr.handle = fd & ~FIO_PIPE_MARKER;
hdr.size = 0;
hdr.arg = size;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_SEND);
if (hdr.size > 0 && hdr.size <= size)
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
return hdr.size;
}
else
{
return read(fd, buf, size);
}
}
/* Get information about file */
int fio_stat(char const* path, struct stat* st, bool follow_symlink, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t path_len = strlen(path) + 1;
hdr.cop = FIO_STAT;
hdr.handle = -1;
hdr.arg =(unsigned) follow_symlink;
hdr.size = path_len;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, path_len), path_len);
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_STAT);
IO_CHECK(fio_read_all(fio_stdin, st, sizeof(*st)), sizeof(*st));
if (hdr.arg != 0)
{
errno = hdr.arg;
return -1;
}
return 0;
}
else
{
return follow_symlink ? stat(path, st) : lstat(path, st);
}
}
/* Check presence of the file */
int fio_access(char const* path, int mode, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t path_len = strlen(path) + 1;
hdr.cop = FIO_ACCESS;
hdr.handle = -1;
hdr.size = path_len;
hdr.arg = mode;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, path_len), path_len);
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_ACCESS);
if (hdr.arg != 0)
{
errno = hdr.arg;
return -1;
}
return 0;
}
else
{
return access(path, mode);
}
}
/* Create symbolic link */
int fio_symlink(char const* target, char const* link_path, bool overwrite, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t target_len = strlen(target) + 1;
size_t link_path_len = strlen(link_path) + 1;
hdr.cop = FIO_SYMLINK;
hdr.handle = -1;
hdr.size = target_len + link_path_len;
hdr.arg = overwrite ? 1 : 0;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, target, target_len), target_len);
IO_CHECK(fio_write_all(fio_stdout, link_path, link_path_len), link_path_len);
return 0;
}
else
{
if (overwrite)
remove_file_or_dir(link_path);
return symlink(target, link_path);
}
}
static void fio_symlink_impl(int out, char *buf, bool overwrite)
{
char *linked_path = buf;
char *link_path = buf + strlen(buf) + 1;
if (overwrite)
remove_file_or_dir(link_path);
if (symlink(linked_path, link_path))
elog(ERROR, "Could not create symbolic link \"%s\": %s",
link_path, strerror(errno));
}
/* Rename file */
int fio_rename(char const* old_path, char const* new_path, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t old_path_len = strlen(old_path) + 1;
size_t new_path_len = strlen(new_path) + 1;
hdr.cop = FIO_RENAME;
hdr.handle = -1;
hdr.size = old_path_len + new_path_len;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, old_path, old_path_len), old_path_len);
IO_CHECK(fio_write_all(fio_stdout, new_path, new_path_len), new_path_len);
//TODO: wait for confirmation.
return 0;
}
else
{
return rename(old_path, new_path);
}
}
/* Sync file to disk */
int fio_sync(char const* path, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t path_len = strlen(path) + 1;
hdr.cop = FIO_SYNC;
hdr.handle = -1;
hdr.size = path_len;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, path_len), path_len);
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.arg != 0)
{
errno = hdr.arg;
return -1;
}
return 0;
}
else if (is_dss_file(path))
{
/* nothing to do in dss mode, data are already sync to disk */
return 0;
}
else
{
int fd;
fd = open(path, O_WRONLY | PG_BINARY, FILE_PERMISSIONS);
if (fd < 0)
return -1;
if (fsync(fd) < 0)
{
close(fd);
return -1;
}
close(fd);
return 0;
}
}
/* Get crc32 of file */
pg_crc32 fio_get_crc32(const char *file_path, fio_location location, bool decompress)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t path_len = strlen(file_path) + 1;
pg_crc32 crc = 0;
hdr.cop = FIO_GET_CRC32;
hdr.handle = -1;
hdr.size = path_len;
hdr.arg = 0;
if (decompress)
hdr.arg = 1;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, file_path, path_len), path_len);
IO_CHECK(fio_read_all(fio_stdin, &crc, sizeof(crc)), sizeof(crc));
return crc;
}
else
{
#ifdef HAVE_LIBZ
if (decompress && !IsDssMode())
return pgFileGetCRCgz(file_path, true, true);
else
#endif
return pgFileGetCRC(file_path, true, true);
}
}
/* Remove file */
int fio_unlink(char const* path, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t path_len = strlen(path) + 1;
hdr.cop = FIO_UNLINK;
hdr.handle = -1;
hdr.size = path_len;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, path_len), path_len);
// TODO: error is swallowed ?
return 0;
}
else
{
return remove_file_or_dir(path);
}
}
/* Create directory */
int fio_mkdir(const char* path, int mode, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t path_len = strlen(path) + 1;
hdr.cop = FIO_MKDIR;
hdr.handle = -1;
hdr.size = path_len;
hdr.arg = mode;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, path_len), path_len);
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
Assert(hdr.cop == FIO_MKDIR);
return hdr.arg;
}
else
{
/* operate is same in local mode and dss mode */
return dir_create_dir(path, mode);
}
}
/* Change file mode */
int fio_chmod(char const* path, int mode, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
size_t path_len = strlen(path) + 1;
hdr.cop = FIO_CHMOD;
hdr.handle = -1;
hdr.size = path_len;
hdr.arg = mode;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, path, path_len), path_len);
return 0;
}
else
{
return chmod(path, mode);
}
}
/* Send file content
* Note: it should not be used for large files.
*/
static void fio_load_file(int out, char const* path)
{
int fd = open(path, O_RDONLY, 0);
fio_header hdr;
void* buf = NULL;
hdr.cop = FIO_SEND;
hdr.size = 0;
if (fd >= 0)
{
off_t size = lseek(fd, 0, SEEK_END);
buf = pgut_malloc(size);
lseek(fd, 0, SEEK_SET);
IO_CHECK(fio_read_all(fd, buf, size), size);
hdr.size = size;
SYS_CHECK(close(fd));
}
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (buf)
{
IO_CHECK(fio_write_all(out, buf, hdr.size), hdr.size);
free(buf);
}
}
/*
* Return number of actually(!) readed blocks, attempts or
* half-readed block are not counted.
* Return values in case of error:
* FILE_MISSING
* OPEN_FAILED
* READ_ERROR
* PAGE_CORRUPTION
* WRITE_FAILED
*
* If none of the above, this function return number of blocks
* readed by remote agent.
*
* In case of DELTA mode horizonLsn must be a valid lsn,
* otherwise it should be set to InvalidXLogRecPtr.
*/
int fio_send_pages(const char *to_fullpath, const char *from_fullpath, pgFile *file,
XLogRecPtr horizonLsn, int calg, int clevel, uint32 checksum_version,
bool use_pagemap, BlockNumber* err_blknum, char **errormsg,
BackupPageHeader2 **headers)
{
FILE *out = NULL;
char *out_buf = NULL;
struct {
fio_header hdr;
fio_send_request arg;
} req;
BlockNumber n_blocks_read = 0;
BlockNumber blknum = 0;
int nRet = 0;
/* send message with header
8bytes 24bytes var var
--------------------------------------------------------------
| fio_header | fio_send_request | FILE PATH | BITMAP(if any) |
--------------------------------------------------------------
*/
req.hdr.cop = FIO_SEND_PAGES;
if (use_pagemap)
{
req.hdr.size = sizeof(fio_send_request) + (*file).pagemap.bitmapsize + strlen(from_fullpath) + 1;
req.arg.bitmapsize = (*file).pagemap.bitmapsize;
/* TODO: add optimization for the case of pagemap
* containing small number of blocks with big serial numbers:
* https://github.com/postgrespro/pg_probackup/blob/remote_page_backup/src/utils/file.c#L1211
*/
}
else
{
req.hdr.size = sizeof(fio_send_request) + strlen(from_fullpath) + 1;
req.arg.bitmapsize = 0;
}
req.arg.nblocks = file->size/BLCKSZ;
req.arg.segmentno = file->segno * RELSEG_SIZE;
req.arg.horizonLsn = horizonLsn;
req.arg.checksumVersion = checksum_version;
req.arg.calg = calg;
req.arg.clevel = clevel;
req.arg.path_len = strlen(from_fullpath) + 1;
file->compress_alg = (CompressAlg)calg; /* TODO: wtf? why here? */
/* send header */
IO_CHECK(fio_write_all(fio_stdout, &req, sizeof(req)), sizeof(req));
/* send file path */
IO_CHECK(fio_write_all(fio_stdout, from_fullpath, req.arg.path_len), req.arg.path_len);
/* send pagemap if any */
if (use_pagemap)
IO_CHECK(fio_write_all(fio_stdout, (*file).pagemap.bitmap, (*file).pagemap.bitmapsize), (*file).pagemap.bitmapsize);
while (true)
{
fio_header hdr;
char buf[BLCKSZ + sizeof(BackupPageHeader)];
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (interrupted)
elog(ERROR, "Interrupted during page reading");
if (hdr.cop == FIO_ERROR)
{
bool valid = hdr.size > 0 && hdr.size <= sizeof(buf);
/* FILE_MISSING, OPEN_FAILED and READ_FAILED */
if (valid)
{
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
*errormsg = (char *)pgut_malloc(hdr.size);
nRet = snprintf_s(*errormsg, hdr.size, hdr.size - 1, "%s", buf);
securec_check_ss_c(nRet, "\0", "\0");
}
return hdr.arg;
}
else if (hdr.cop == FIO_SEND_FILE_CORRUPTION)
{
*err_blknum = hdr.arg;
bool valid = hdr.size > 0 && hdr.size <= sizeof(buf);
if (valid)
{
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
*errormsg = (char *)pgut_malloc(hdr.size);
nRet = snprintf_s(*errormsg, hdr.size, hdr.size - 1, "%s", buf);
securec_check_ss_c(nRet, "\0", "\0");
}
return PAGE_CORRUPTION;
}
else if (hdr.cop == FIO_SEND_FILE_EOF)
{
/* n_blocks_read reported by EOF */
n_blocks_read = hdr.arg;
/* receive headers if any */
if (hdr.size > 0)
{
*headers = (BackupPageHeader2 *)pgut_malloc(hdr.size);
IO_CHECK(fio_read_all(fio_stdin, *headers, hdr.size), hdr.size);
file->n_headers = (hdr.size / sizeof(BackupPageHeader2)) -1;
}
break;
}
else if (hdr.cop == FIO_PAGE)
{
blknum = hdr.arg;
Assert(hdr.size <= sizeof(buf));
if (hdr.size > sizeof(buf)) {
hdr.size = sizeof(buf);
}
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
COMP_FILE_CRC32(true, file->crc, buf, hdr.size);
/* lazily open backup file */
if (!out)
out = open_local_file_rw(to_fullpath, &out_buf, STDIO_BUFSIZE);
if (fio_fwrite(out, buf, hdr.size) != hdr.size)
{
fio_fclose(out);
*err_blknum = blknum;
return WRITE_FAILED;
}
file->write_size += hdr.size;
file->uncompressed_size += BLCKSZ;
}
else
elog(ERROR, "Remote agent returned message of unexpected type: %i", hdr.cop);
}
if (out)
fclose(out);
pg_free(out_buf);
return n_blocks_read;
}
/* TODO: read file using large buffer
* Return codes:
* FIO_ERROR:
* FILE_MISSING (-1)
* OPEN_FAILED (-2)
* READ_FAILED (-3)
* FIO_SEND_FILE_CORRUPTION
* FIO_SEND_FILE_EOF
*/
static void fio_send_pages_impl(int out, char* buf)
{
FILE *in = NULL;
BlockNumber blknum = 0;
int current_pos = 0;
BlockNumber n_blocks_read = 0;
PageState page_st;
char read_buffer[BLCKSZ+1];
char in_buf[STDIO_BUFSIZE];
fio_header hdr;
fio_send_request *req = (fio_send_request*) buf;
char *from_fullpath = (char*) buf + sizeof(fio_send_request);
bool with_pagemap = req->bitmapsize > 0;
/* error reporting */
char *errormsg = NULL;
/* parse buffer */
datapagemap_t *map = NULL;
datapagemap_iterator_t *iter = NULL;
/* page headers */
int32 hdr_num = -1;
int32 cur_pos_out = 0;
BackupPageHeader2 *headers = NULL;
PageCompression* pageCompression = NULL;
int nRet = 0;
if (PageCompression::SkipCompressedFile(from_fullpath, strlen(from_fullpath))) {
/* init pageCompression and return pcdFd for error check */
pageCompression = new(std::nothrow) PageCompression();
if (pageCompression == NULL) {
elog(ERROR, "Decompression page init failed");
return;
}
COMPRESS_ERROR_STATE result = pageCompression->Init(from_fullpath, req->segmentno / RELSEG_SIZE);
if (result == SUCCESS) {
in = pageCompression->GetCompressionFile();
} else {
delete pageCompression;
pageCompression = NULL;
elog(ERROR, "Decompression page init failed \"%s\": %d", from_fullpath, (int)result);
return;
}
} else {
/* open source file */
in = fopen(from_fullpath, PG_BINARY_R);
}
if (!in)
{
errormsg = ProcessErrorIn(out, hdr, from_fullpath);
goto cleanup;
}
if (with_pagemap)
{
map = (datapagemap_t *)pgut_malloc(sizeof(datapagemap_t));
map->bitmapsize = req->bitmapsize;
map->bitmap = (unsigned char*) buf + sizeof(fio_send_request) + req->path_len;
/* get first block */
iter = datapagemap_iterate(map);
datapagemap_next(iter, &blknum);
setvbuf(in, NULL, _IONBF, BUFSIZ);
}
else
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
/* TODO: what is this barrier for? */
read_buffer[BLCKSZ] = 1; /* barrier */
while (blknum < req->nblocks)
{
int rc = 0;
size_t read_len = 0;
int retry_attempts = PAGE_READ_ATTEMPTS;
/* TODO: handle signals on the agent */
if (interrupted)
elog(ERROR, "Interrupted during remote page reading");
/* read page, check header and validate checksumms */
for (;;)
{
if (pageCompression) {
read_len = pageCompression->ReadCompressedBuffer(blknum, read_buffer, BLCKSZ, true);
if (read_len > MIN_COMPRESS_ERROR_RT) {
elog(ERROR, "can not read actual block %u, error code: %lu,", blknum, read_len);
}
} else {
/*
* Optimize stdio buffer usage, fseek only when current position
* does not match the position of requested block.
*/
if (current_pos != (int)(blknum * BLCKSZ))
{
current_pos = blknum*BLCKSZ;
if (fseek(in, current_pos, SEEK_SET) != 0)
elog(ERROR, "fseek to position %u is failed on remote file '%s': %s",
current_pos, from_fullpath, strerror(errno));
}
read_len = fread(read_buffer, 1, BLCKSZ, in);
current_pos += read_len;
}
/* report error */
if (ferror(in))
{
hdr.cop = FIO_ERROR;
hdr.arg = READ_FAILED;
errormsg = (char *)pgut_malloc(ERRMSG_MAX_LEN);
/* Construct the error message */
nRet = snprintf_s(errormsg, ERRMSG_MAX_LEN,ERRMSG_MAX_LEN - 1, "Cannot read block %u of '%s': %s",
blknum, from_fullpath, strerror(errno));
securec_check_ss_c(nRet, "\0", "\0");
hdr.size = strlen(errormsg) + 1;
/* send header and message */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
goto cleanup;
}
if (read_len == BLCKSZ)
{
rc = validate_one_page(read_buffer, req->segmentno + blknum,
InvalidXLogRecPtr, &page_st,
req->checksumVersion);
if (rc == PAGE_MAYBE_COMPRESSED && pageCompression != NULL) {
rc = PAGE_IS_VALID;
}
/* TODO: optimize copy of zeroed page */
if (rc == PAGE_IS_ZEROED)
break;
else if (rc == PAGE_IS_VALID)
break;
}
if (feof(in))
goto eof;
// else /* readed less than BLKSZ bytes, retry */
/* File is either has insane header or invalid checksum,
* retry. If retry attempts are exhausted, report corruption.
*/
if (--retry_attempts == 0)
{
hdr.cop = FIO_SEND_FILE_CORRUPTION;
hdr.arg = blknum;
/* Construct the error message */
if (rc == PAGE_HEADER_IS_INVALID)
get_header_errormsg(read_buffer, &errormsg);
else if (rc == PAGE_CHECKSUM_MISMATCH)
get_checksum_errormsg(read_buffer, &errormsg,
req->segmentno + blknum);
/* if error message is not empty, set payload size to its length */
hdr.size = errormsg ? strlen(errormsg) + 1 : 0;
/* send header */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
/* send error message if any */
if (errormsg)
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
goto cleanup;
}
}
n_blocks_read++;
/*
* horizonLsn is not 0 only in case of delta backup.
* As far as unsigned number are always greater or equal than zero,
* there is no sense to add more checks.
*/
if ((req->horizonLsn == InvalidXLogRecPtr) || /* full, page, ptrack */
(page_st.lsn == InvalidXLogRecPtr) || /* zeroed page */
(req->horizonLsn > 0 && page_st.lsn > req->horizonLsn)) /* delta */
{
int compressed_size = 0;
char write_buffer[BLCKSZ*2];
BackupPageHeader* bph = (BackupPageHeader*)write_buffer;
/* compress page */
hdr.cop = FIO_PAGE;
hdr.arg = blknum;
compressed_size = do_compress(write_buffer + sizeof(BackupPageHeader),
sizeof(write_buffer) - sizeof(BackupPageHeader),
read_buffer, BLCKSZ, (CompressAlg)req->calg, req->clevel,
NULL);
if (compressed_size <= 0 || compressed_size >= BLCKSZ)
{
/* Do not compress page */
errno_t rtc;
rtc = memcpy_s(write_buffer + sizeof(BackupPageHeader), BLCKSZ, read_buffer, BLCKSZ);
securec_check_c(rtc, "\0", "\0");
compressed_size = BLCKSZ;
}
bph->block = blknum;
bph->compressed_size = compressed_size;
hdr.size = compressed_size + sizeof(BackupPageHeader);
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(out, write_buffer, hdr.size), hdr.size);
/* set page header for this file */
hdr_num++;
if (!headers)
headers = (BackupPageHeader2 *) pgut_malloc(sizeof(BackupPageHeader2));
else
headers = (BackupPageHeader2 *) pgut_realloc(headers,
(hdr_num) * sizeof(BackupPageHeader2),
(hdr_num + 1) * sizeof(BackupPageHeader2));
headers[hdr_num].block = blknum;
headers[hdr_num].lsn = page_st.lsn;
headers[hdr_num].checksum = page_st.checksum;
headers[hdr_num].pos = cur_pos_out;
cur_pos_out += hdr.size;
}
/* next block */
if (with_pagemap)
{
/* exit if pagemap is exhausted */
if (!datapagemap_next(iter, &blknum))
break;
}
else
blknum++;
}
eof:
/* We are done, send eof */
hdr.cop = FIO_SEND_FILE_EOF;
hdr.arg = n_blocks_read;
hdr.size = 0;
if (headers)
{
hdr.size = (hdr_num+2) * sizeof(BackupPageHeader2);
/* add dummy header */
headers = (BackupPageHeader2 *) pgut_realloc(headers,
(hdr_num + 1) * sizeof(BackupPageHeader2),
(hdr_num + 2) * sizeof(BackupPageHeader2));
headers[hdr_num+1].pos = cur_pos_out;
}
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (headers)
IO_CHECK(fio_write_all(out, headers, hdr.size), hdr.size);
cleanup:
pg_free(map);
pg_free(iter);
pg_free(errormsg);
pg_free(headers);
if (pageCompression) {
/* in will be closed */
delete pageCompression;
} else {
if (in)
fclose(in);
}
return;
}
static char *ProcessErrorIn(int out, fio_header &hdr, const char *fromFullpath)
{
char *errormsg = NULL;
hdr.cop = FIO_ERROR;
/* do not send exact wording of ENOENT error message
* because it is a very common error in our case, so
* error code is enough.
*/
if (errno == ENOENT) {
hdr.arg = FILE_MISSING;
hdr.size = 0;
} else {
hdr.arg = OPEN_FAILED;
errormsg = (char *)pgut_malloc(ERRMSG_MAX_LEN);
/* Construct the error message */
error_t nRet = snprintf_s(errormsg, ERRMSG_MAX_LEN, ERRMSG_MAX_LEN - 1, "Cannot open file \"%s\": %s",
fromFullpath, strerror(errno));
securec_check_ss_c(nRet, "\0", "\0");
hdr.size = strlen(errormsg) + 1;
}
/* send header and message */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (errormsg)
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
return errormsg;
}
/* Receive chunks of data and write them to destination file.
* Return codes:
* SEND_OK (0)
* FILE_MISSING (-1)
* OPEN_FAILED (-2)
* READ_FAILED (-3)
* WRITE_FAILED (-4)
*
* OPEN_FAILED and READ_FAIL should also set errormsg.
* If pgFile is not NULL then we must calculate crc and read_size for it.
*/
int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out,
pgFile *file, char **errormsg)
{
fio_header hdr;
int exit_code = SEND_OK;
size_t path_len = strlen(from_fullpath) + 1;
char *buf = (char *)pgut_malloc(CHUNK_SIZE); /* buffer */
int nRet = 0;
hdr.cop = FIO_SEND_FILE;
hdr.size = path_len;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, from_fullpath, path_len), path_len);
for (;;)
{
/* receive data */
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.cop == FIO_SEND_FILE_EOF)
{
break;
}
else if (hdr.cop == FIO_ERROR)
{
/* handle error, reported by the agent */
if (hdr.size > 0 && hdr.size <= CHUNK_SIZE)
{
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
*errormsg = (char *)pgut_malloc(hdr.size);
nRet = snprintf_s(*errormsg, hdr.size, hdr.size - 1, "%s", buf);
securec_check_ss_c(nRet, "\0", "\0");
}
exit_code = hdr.arg;
break;
}
else if (hdr.cop == FIO_PAGE)
{
Assert(hdr.size <= CHUNK_SIZE);
if (hdr.size > CHUNK_SIZE) {
hdr.size = CHUNK_SIZE;
}
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
/* We have received a chunk of data data, lets write it out */
if (fwrite(buf, 1, hdr.size, out) != hdr.size)
{
exit_code = WRITE_FAILED;
break;
}
if (file)
{
file->read_size += hdr.size;
COMP_FILE_CRC32(true, file->crc, buf, hdr.size);
}
}
else
{
/* TODO: fio_disconnect may get assert fail when running after this */
elog(ERROR, "Remote agent returned message of unexpected type: %i", hdr.cop);
}
}
if (exit_code < OPEN_FAILED)
fio_disconnect(); /* discard possible pending data in pipe */
pg_free(buf);
return exit_code;
}
/* Send file content
* On error we return FIO_ERROR message with following codes
* FIO_ERROR:
* FILE_MISSING (-1)
* OPEN_FAILED (-2)
* READ_FAILED (-3)
*
* FIO_PAGE
* FIO_SEND_FILE_EOF
*
*/
static void fio_send_file_impl(int out, char const* path)
{
FILE *fp;
fio_header hdr;
char *buf = (char *)pgut_malloc(CHUNK_SIZE);
size_t read_len = 0;
char *errormsg = NULL;
int nRet = 0;
/* open source file for read */
/* TODO: check that file is regular file */
fp = fopen(path, PG_BINARY_R);
if (!fp)
{
hdr.cop = FIO_ERROR;
/* do not send exact wording of ENOENT error message
* because it is a very common error in our case, so
* error code is enough.
*/
if (errno == ENOENT)
{
hdr.arg = FILE_MISSING;
hdr.size = 0;
}
else
{
hdr.arg = OPEN_FAILED;
errormsg = (char *)pgut_malloc(ERRMSG_MAX_LEN);
/* Construct the error message */
nRet = snprintf_s(errormsg, ERRMSG_MAX_LEN, ERRMSG_MAX_LEN - 1, "Cannot open file '%s': %s", path, strerror(errno));
securec_check_ss_c(nRet, "\0", "\0");
hdr.size = strlen(errormsg) + 1;
}
/* send header and message */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (errormsg)
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
goto cleanup;
}
/* disable stdio buffering */
setvbuf(fp, NULL, _IONBF, BUFSIZ);
/* copy content */
for (;;)
{
read_len = fread(buf, 1, CHUNK_SIZE, fp);
/* report error */
if (ferror(fp))
{
hdr.cop = FIO_ERROR;
errormsg = (char *)pgut_malloc(ERRMSG_MAX_LEN);
hdr.arg = READ_FAILED;
/* Construct the error message */
nRet = snprintf_s(errormsg, ERRMSG_MAX_LEN, ERRMSG_MAX_LEN -1, "Cannot read from file '%s': %s", path, strerror(errno));
securec_check_ss_c(nRet, "\0", "\0");
hdr.size = strlen(errormsg) + 1;
/* send header and message */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
goto cleanup;
}
if (read_len > 0)
{
/* send chunk */
hdr.cop = FIO_PAGE;
hdr.size = read_len;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(out, buf, read_len), read_len);
}
if (feof(fp))
break;
}
/* we are done, send eof */
hdr.cop = FIO_SEND_FILE_EOF;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
cleanup:
if (fp)
fclose(fp);
pg_free(buf);
pg_free(errormsg);
return;
}
/* Compile the array of files located on remote machine in directory root */
void fio_list_dir(parray *files, const char *root, bool exclude,
bool follow_symlink, bool add_root, bool backup_logs,
bool skip_hidden, int external_dir_num, bool backup_replslots)
{
fio_header hdr;
fio_list_dir_request req;
int nRet = 0;
char *buf = (char *)pgut_malloc(CHUNK_SIZE);
/* Send to the agent message with parameters for directory listing */
nRet = snprintf_s(req.path, MAXPGPATH,MAXPGPATH - 1, "%s", root);
securec_check_ss_c(nRet, "\0", "\0");
req.exclude = exclude;
req.follow_symlink = follow_symlink;
req.add_root = add_root;
req.backup_logs = backup_logs;
req.exclusive_backup = exclusive_backup;
req.skip_hidden = skip_hidden;
req.external_dir_num = external_dir_num;
req.backup_replslots = backup_replslots;
hdr.cop = FIO_LIST_DIR;
hdr.size = sizeof(req);
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, &req, hdr.size), hdr.size);
for (;;)
{
/* receive data */
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.cop == FIO_SEND_FILE_EOF)
{
/* the work is done */
break;
}
else if (hdr.cop == FIO_SEND_FILE)
{
pgFile *file = NULL;
fio_pgFile fio_file;
if (hdr.size > CHUNK_SIZE) {
hdr.size = CHUNK_SIZE;
}
/* receive rel_path */
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
file = pgFileInit(buf);
/* receive metainformation */
IO_CHECK(fio_read_all(fio_stdin, &fio_file, sizeof(fio_file)), sizeof(fio_file));
file->mode = fio_file.mode;
file->size = fio_file.size;
file->mtime = fio_file.mtime;
file->is_datafile = fio_file.is_datafile;
file->is_database = fio_file.is_database;
file->tblspcOid = fio_file.tblspcOid;
file->dbOid = fio_file.dbOid;
file->relOid = fio_file.relOid;
file->forkName = fio_file.forkName;
file->segno = fio_file.segno;
file->external_dir_num = fio_file.external_dir_num;
file->compressed_file = fio_file.compressed_file;
file->compressed_chunk_size = fio_file.compressed_chunk_size;
file->compressed_algorithm = fio_file.compressed_algorithm;
if (fio_file.linked_len > 0)
{
if (fio_file.linked_len > CHUNK_SIZE) {
fio_file.linked_len = CHUNK_SIZE;
}
IO_CHECK(fio_read_all(fio_stdin, buf, fio_file.linked_len), fio_file.linked_len);
file->linked = (char *)pgut_malloc(fio_file.linked_len);
nRet = snprintf_s(file->linked, fio_file.linked_len, fio_file.linked_len - 1, "%s", buf);
securec_check_ss_c(nRet, "\0", "\0");
}
/*
* Check file that under pg_replslot and judge whether it
* belonged to logical replication slots for subscriptions.
*/
if (backup_replslots && strcmp(buf, PG_REPLSLOT_DIR) != 0 &&
path_is_prefix_of_path(PG_REPLSLOT_DIR, buf) && check_logical_replslot_dir(file->rel_path) != 1) {
continue;
}
parray_append(files, file);
}
else
{
/* TODO: fio_disconnect may get assert fail when running after this */
elog(ERROR, "Remote agent returned message of unexpected type: %i", hdr.cop);
}
}
pg_free(buf);
}
/*
* To get the arrays of files we use the same function dir_list_file(),
* that is used for local backup.
* After that we iterate over arrays and for every file send at least
* two messages to main process:
* 1. rel_path
* 2. metainformation (size, mtime, etc)
* 3. link path (optional)
*
* TODO: replace FIO_SEND_FILE and FIO_SEND_FILE_EOF with dedicated messages
*/
static void fio_list_dir_impl(int out, char* buf)
{
int i;
fio_header hdr;
fio_list_dir_request *req = (fio_list_dir_request*) buf;
parray *file_files = parray_new();
/*
* Disable logging into console any messages with exception of ERROR messages,
* because currently we have no mechanism to notify the main process
* about then message been sent.
* TODO: correctly send elog messages from agent to main process.
*/
instance_config.logger.log_level_console = ERROR;
exclusive_backup = req->exclusive_backup;
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
req->add_root, req->backup_logs, req->skip_hidden,
req->external_dir_num, FIO_LOCAL_HOST, req->backup_replslots);
/* send information about files to the main process */
for (i = 0; i < (int)parray_num(file_files); i++)
{
fio_pgFile fio_file;
pgFile *file = (pgFile *) parray_get(file_files, i);
fio_file.mode = file->mode;
fio_file.size = file->size;
fio_file.mtime = file->mtime;
fio_file.is_datafile = file->is_datafile;
fio_file.is_database = file->is_database;
fio_file.tblspcOid = file->tblspcOid;
fio_file.dbOid = file->dbOid;
fio_file.relOid = file->relOid;
fio_file.forkName = file->forkName;
fio_file.segno = file->segno;
fio_file.external_dir_num = file->external_dir_num;
fio_file.compressed_file = file->compressed_file;
fio_file.compressed_chunk_size = file->compressed_chunk_size;
fio_file.compressed_algorithm = file->compressed_algorithm;
if (file->linked)
fio_file.linked_len = strlen(file->linked) + 1;
else
fio_file.linked_len = 0;
hdr.cop = FIO_SEND_FILE;
hdr.size = strlen(file->rel_path) + 1;
/* send rel_path first */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(out, file->rel_path, hdr.size), hdr.size);
/* now send file metainformation */
IO_CHECK(fio_write_all(out, &fio_file, sizeof(fio_file)), sizeof(fio_file));
/* If file is a symlink, then send link path */
if (file->linked)
IO_CHECK(fio_write_all(out, file->linked, fio_file.linked_len), fio_file.linked_len);
pgFileFree(file);
}
parray_free(file_files);
hdr.cop = FIO_SEND_FILE_EOF;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
}
PageState *
fio_get_checksum_map(const char *fullpath, uint32 checksum_version, int n_blocks,
XLogRecPtr dest_stop_lsn, BlockNumber segmentno, fio_location location)
{
errno_t rc = 0;
if (fio_is_remote(location))
{
fio_header hdr;
fio_checksum_map_request req_hdr;
PageState *checksum_map = NULL;
size_t path_len = strlen(fullpath) + 1;
req_hdr.n_blocks = n_blocks;
req_hdr.segmentno = segmentno;
req_hdr.stop_lsn = dest_stop_lsn;
req_hdr.checksumVersion = checksum_version;
hdr.cop = FIO_GET_CHECKSUM_MAP;
hdr.size = sizeof(req_hdr) + path_len;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, &req_hdr, sizeof(req_hdr)), sizeof(req_hdr));
IO_CHECK(fio_write_all(fio_stdout, fullpath, path_len), path_len);
/* receive data */
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.size > 0)
{
checksum_map = (PageState *)pgut_malloc(n_blocks * sizeof(PageState));
rc = memset_s(checksum_map, n_blocks * sizeof(PageState), 0, n_blocks * sizeof(PageState));
securec_check(rc, "\0", "\0");
if (hdr.size > (unsigned)n_blocks) {
hdr.size = (unsigned)n_blocks;
}
IO_CHECK(fio_read_all(fio_stdin, checksum_map, hdr.size * sizeof(PageState)), hdr.size * sizeof(PageState));
}
return checksum_map;
}
else
{
return get_checksum_map(fullpath, checksum_version,
n_blocks, dest_stop_lsn, segmentno);
}
}
static void fio_get_checksum_map_impl(int out, char *buf)
{
fio_header hdr;
PageState *checksum_map = NULL;
char *fullpath = (char*) buf + sizeof(fio_checksum_map_request);
fio_checksum_map_request *req = (fio_checksum_map_request*) buf;
checksum_map = get_checksum_map(fullpath, req->checksumVersion,
req->n_blocks, req->stop_lsn, req->segmentno);
hdr.size = req->n_blocks;
/* send array of PageState`s to main process */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.size > 0)
IO_CHECK(fio_write_all(out, checksum_map, hdr.size * sizeof(PageState)), hdr.size * sizeof(PageState));
pg_free(checksum_map);
}
datapagemap_t *
fio_get_lsn_map(const char *fullpath, uint32 checksum_version,
int n_blocks, XLogRecPtr shift_lsn, BlockNumber segmentno,
fio_location location)
{
datapagemap_t* lsn_map = NULL;
errno_t rc = 0;
if (fio_is_remote(location))
{
fio_header hdr;
fio_lsn_map_request req_hdr;
size_t path_len = strlen(fullpath) + 1;
req_hdr.n_blocks = n_blocks;
req_hdr.segmentno = segmentno;
req_hdr.shift_lsn = shift_lsn;
req_hdr.checksumVersion = checksum_version;
hdr.cop = FIO_GET_LSN_MAP;
hdr.size = sizeof(req_hdr) + path_len;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, &req_hdr, sizeof(req_hdr)), sizeof(req_hdr));
IO_CHECK(fio_write_all(fio_stdout, fullpath, path_len), path_len);
/* receive data */
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.size > 0)
{
lsn_map = (datapagemap_t *)pgut_malloc(sizeof(datapagemap_t));
rc = memset_s(lsn_map, sizeof(datapagemap_t), 0, sizeof(datapagemap_t));
securec_check(rc, "\0", "\0");
lsn_map->bitmap = (unsigned char *)pgut_malloc(hdr.size);
lsn_map->bitmapsize = hdr.size;
IO_CHECK(fio_read_all(fio_stdin, lsn_map->bitmap, hdr.size), hdr.size);
}
}
else
{
/* operate is same in local mode and dss mode */
lsn_map = get_lsn_map(fullpath, checksum_version, n_blocks, shift_lsn, segmentno);
}
return lsn_map;
}
static void fio_get_lsn_map_impl(int out, char *buf)
{
fio_header hdr;
datapagemap_t *lsn_map = NULL;
char *fullpath = (char*) buf + sizeof(fio_lsn_map_request);
fio_lsn_map_request *req = (fio_lsn_map_request*) buf;
lsn_map = get_lsn_map(fullpath, req->checksumVersion, req->n_blocks,
req->shift_lsn, req->segmentno);
if (lsn_map)
hdr.size = lsn_map->bitmapsize;
else
hdr.size = 0;
/* send bitmap to main process */
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.size > 0)
IO_CHECK(fio_write_all(out, lsn_map->bitmap, hdr.size), hdr.size);
if (lsn_map)
{
pg_free(lsn_map->bitmap);
pg_free(lsn_map);
}
}
/*
* Go to the remote host and get postmaster pid from file postmaster.pid
* and check that process is running, if process is running, return its pid number.
*/
pid_t fio_check_postmaster(const char *pgdata, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
hdr.cop = FIO_CHECK_POSTMASTER;
hdr.size = strlen(pgdata) + 1;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, pgdata, hdr.size), hdr.size);
/* receive result */
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
return hdr.arg;
}
else
/* operate is same in local mode and dss mode */
return check_postmaster(pgdata);
}
static void fio_check_postmaster_impl(int out, char *buf)
{
fio_header hdr;
pid_t postmaster_pid;
char *pgdata = (char*) buf;
postmaster_pid = check_postmaster(pgdata);
/* send arrays of checksums to main process */
hdr.arg = postmaster_pid;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
}
/*
* Delete file pointed by the pgFile.
* If the pgFile points directory, the directory must be empty.
*/
void
fio_delete(mode_t mode, const char *fullpath, fio_location location)
{
if (fio_is_remote(location))
{
fio_header hdr;
hdr.cop = FIO_DELETE;
hdr.size = strlen(fullpath) + 1;
hdr.arg = mode;
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(fio_stdout, fullpath, hdr.size), hdr.size);
}
else
/* operate is same in local mode and dss mode */
pgFileDelete(mode, fullpath);
}
static void
fio_delete_impl(mode_t mode, char *buf)
{
char *fullpath = (char*) buf;
pgFileDelete(mode, fullpath);
}
/* Execute commands at remote host */
void fio_communicate(int in, int out)
{
/*
* Map of file and directory descriptors.
* The same mapping is used in agent and master process, so we
* can use the same index at both sides.
*/
int fd[FIO_FDMAX];
DIR* dir[FIO_FDMAX];
struct dirent* entry;
size_t buf_size = 128*1024;
char* buf = (char*)pgut_malloc(buf_size);
fio_header hdr;
struct stat st;
int rc;
int tmp_fd;
pg_crc32 crc;
#ifdef WIN32
SYS_CHECK(setmode(in, _O_BINARY));
SYS_CHECK(setmode(out, _O_BINARY));
#endif
/* Main loop until end of processing all master commands */
while ((rc = fio_read_all(in, &hdr, sizeof(hdr))) == sizeof(hdr)) {
if (hdr.size != 0) {
if (hdr.size > buf_size) {
/* Extend buffer on demand */
size_t oldSize = buf_size;
buf_size = hdr.size;
buf = (char*)pgut_realloc(buf, oldSize, buf_size);
}
IO_CHECK(fio_read_all(in, buf, hdr.size), hdr.size);
}
switch (hdr.cop) {
case FIO_LOAD: /* Send file content */
fio_load_file(out, buf);
break;
case FIO_OPENDIR: /* Open directory for traversal */
dir[hdr.handle] = opendir(buf);
hdr.arg = dir[hdr.handle] == NULL ? errno : 0;
hdr.size = 0;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break;
case FIO_READDIR: /* Get next directory entry */
hdr.cop = FIO_SEND;
entry = readdir(dir[hdr.handle]);
if (entry != NULL)
{
hdr.size = sizeof(*entry);
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(out, entry, hdr.size), hdr.size);
}
else
{
hdr.size = 0;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
}
break;
case FIO_CLOSEDIR: /* Finish directory traversal */
SYS_CHECK(closedir(dir[hdr.handle]));
break;
case FIO_OPEN: /* Open file */
fd[hdr.handle] = open(buf, hdr.arg, FILE_PERMISSIONS);
hdr.arg = fd[hdr.handle] < 0 ? errno : 0;
hdr.size = 0;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break;
case FIO_CLOSE: /* Close file */
SYS_CHECK(close(fd[hdr.handle]));
break;
case FIO_WRITE: /* Write to the current position in file */
IO_CHECK(fio_write_all(fd[hdr.handle], buf, hdr.size), hdr.size);
break;
case FIO_WRITE_COMPRESSED: /* Write to the current position in file */
IO_CHECK(fio_write_compressed_impl(fd[hdr.handle], buf, hdr.size, hdr.arg), BLCKSZ);
break;
case FIO_COSTRUCT_COMPRESSED: {
CompressCommunicate *cm = (CompressCommunicate *)(void *)buf;
COMPRESS_ERROR_STATE result = ConstructCompressedFile(cm->path,
(uint16)cm->chunkSize,
(uint8)cm->algorithm);
IO_CHECK((int)result, (int)SUCCESS);
break;
}
case FIO_READ: /* Read from the current position in file */
if ((size_t)hdr.arg > buf_size) {
size_t oldSize = buf_size;
buf_size = hdr.arg;
buf = (char*)pgut_realloc(buf, oldSize, buf_size);
}
rc = read(fd[hdr.handle], buf, hdr.arg);
hdr.cop = FIO_SEND;
hdr.size = rc > 0 ? rc : 0;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.size != 0)
IO_CHECK(fio_write_all(out, buf, hdr.size), hdr.size);
break;
case FIO_PREAD: /* Read from specified position in file, ignoring pages beyond horizon of delta backup */
rc = pread(fd[hdr.handle], buf, BLCKSZ, hdr.arg);
hdr.cop = FIO_SEND;
hdr.arg = rc;
hdr.size = rc >= 0 ? rc : 0;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
if (hdr.size != 0)
IO_CHECK(fio_write_all(out, buf, hdr.size), hdr.size);
break;
case FIO_AGENT_VERSION:
hdr.arg = AGENT_PROTOCOL_VERSION;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break;
case FIO_STAT: /* Get information about file with specified path */
hdr.size = sizeof(st);
rc = hdr.arg ? stat(buf, &st) : lstat(buf, &st);
hdr.arg = rc < 0 ? errno : 0;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
IO_CHECK(fio_write_all(out, &st, sizeof(st)), sizeof(st));
break;
case FIO_ACCESS: /* Check presence of file with specified name */
hdr.size = 0;
hdr.arg = access(buf, hdr.arg) < 0 ? errno : 0;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break;
case FIO_RENAME: /* Rename file */
SYS_CHECK(rename(buf, buf + strlen(buf) + 1));
break;
case FIO_SYMLINK: /* Create symbolic link */
fio_symlink_impl(out, buf, hdr.arg > 0 ? true : false);
break;
case FIO_UNLINK: /* Remove file or directory (TODO: Win32) */
SYS_CHECK(remove_file_or_dir(buf));
break;
case FIO_MKDIR: /* Create directory */
hdr.size = 0;
hdr.arg = dir_create_dir(buf, hdr.arg);
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break;
case FIO_CHMOD: /* Change file mode */
SYS_CHECK(chmod(buf, hdr.arg));
break;
case FIO_SEEK: /* Set current position in file */
SYS_CHECK(lseek(fd[hdr.handle], hdr.arg, SEEK_SET));
break;
case FIO_TRUNCATE: /* Truncate file */
SYS_CHECK(ftruncate(fd[hdr.handle], hdr.arg));
break;
case FIO_LIST_DIR:
fio_list_dir_impl(out, buf);
break;
case FIO_SEND_PAGES:
// buf contain fio_send_request header and bitmap.
fio_send_pages_impl(out, buf);
break;
case FIO_SEND_FILE:
fio_send_file_impl(out, buf);
break;
case FIO_SYNC:
/* open file and fsync it */
tmp_fd = open(buf, O_WRONLY | PG_BINARY, FILE_PERMISSIONS);
if (tmp_fd < 0)
hdr.arg = errno;
else
{
if (fsync(tmp_fd) == 0)
hdr.arg = 0;
else
hdr.arg = errno;
close(tmp_fd);
}
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break;
case FIO_GET_CRC32:
/* calculate crc32 for a file */
#ifdef HAVE_LIBZ
if (hdr.arg == 1)
crc = pgFileGetCRCgz(buf, true, true);
else
#endif
crc = pgFileGetCRC(buf, true, true);
IO_CHECK(fio_write_all(out, &crc, sizeof(crc)), sizeof(crc));
break;
case FIO_GET_CHECKSUM_MAP:
/* calculate crc32 for a file */
fio_get_checksum_map_impl(out, buf);
break;
case FIO_GET_LSN_MAP:
/* calculate crc32 for a file */
fio_get_lsn_map_impl(out, buf);
break;
case FIO_CHECK_POSTMASTER:
/* calculate crc32 for a file */
fio_check_postmaster_impl(out, buf);
break;
case FIO_DELETE:
/* delete file */
fio_delete_impl(hdr.arg, buf);
break;
case FIO_DISCONNECT:
hdr.cop = FIO_DISCONNECTED;
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break;
default:
Assert(false);
}
}
free(buf);
if (rc != 0) { /* Not end of stream: normal pipe close */
perror("read");
exit(EXIT_FAILURE);
}
}