Files
openGauss-server/src/bin/pg_upgrade/pg_format_cu.cpp
2021-09-23 15:19:37 +08:00

574 lines
15 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* pg_format_cu.cpp
*
* IDENTIFICATION
* src/bin/pg_upgrade/pg_format_cu.cpp
*
* -------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include "postgres.h"
#include "knl/knl_variable.h"
#ifdef __cplusplus
extern "C" {
#endif
extern char* optarg;
const int ALIGN_SIZE = 8192;
const int PERMISSION = 0600;
const int FILE_MAX_SIZE = 1073741824; // 1024*1024*1024
const int FILE_MAX_PATH = 1024; // reffer MAXPGPATH
const int PATH_DELIMITER_SIZE = 1;
const int STRING_TERMINATOR = 1;
#define CONFIRM_PATH(file_path) \
do { \
if (NULL == (file_path)) { \
printf("the file or path does not exist.\n"); \
exit(1); \
} \
} while (0)
typedef int (*oper_dir_entry_proc)(
const char* root, int buf_size, int root_len, const char* entry_name, const void* args);
static void help(const char* progname)
{
printf("%s is an upgrade tool about align column store files for openGauss.\n\n"
"Usage:\n"
" %s [OPTION]...\n"
"\nOptions:\n"
" -d DIRECTORY database directory absolutely path, align all the named file \n"
" -f FILE_NAME database file absolutely path, align the files \n"
" -a ALIGN_SIZE size to align \n"
" -?, -h, --help show this help, then exit\n",
progname,
progname);
}
/*
* @Description: check file exist
* @Param[IN/OUT] file_path: file path
* @Return: true exist; false not exist
* @See also:
*/
bool file_exist(const char* file_path)
{
int ret = 0;
struct stat stat_buf {};
CONFIRM_PATH(file_path);
ret = stat(file_path, &stat_buf);
if (ret != 0) {
printf("file(%s) not exist, OS errno=%d.\n", file_path, errno);
return false;
}
/* S_ISREG: judge whether it's a regular file or not by the flag */
if (S_ISREG(stat_buf.st_mode)) {
return true;
}
printf("file(%s) not regular file.\n", file_path);
return false;
}
/*
* @Description: check file name and judge whether is column store file
* @Param[IN/OUT] file_path: file path
* @Return: true cloumn store style file
* @See also:
*/
bool file_check_column_style(char* file_path)
{
// relate to CUStorage::InitFileNamePrefix
char* file_name = NULL;
char* str = NULL;
char* str1 = NULL;
CONFIRM_PATH(file_path);
file_name = strrchr(file_path, '/');
if (file_name == NULL) {
printf("failed to get file(%s) name.\n", file_path);
return false;
}
/* here can check number in file name */
str = strstr(file_name, "_C");
if (str == NULL) {
printf("file(%s) maybe not column store file.\n", file_path);
return false;
}
str1 = strchr(str, '.');
if (str1 == NULL) {
printf("file(%s) maybe not column store file.\n", file_path);
return false;
}
/* here can check number in file name */
return true;
}
/*
* @Description: get file size
* @Param[IN] file_path:file size
* @Param[IN/OUT] size: file size
* @Return: 0 :op succeed; others failed
* @See also:
*/
int file_size(const char* file_path, uint64* size)
{
int ret = 0;
struct stat64 stat_buf {};
CONFIRM_PATH(file_path);
ret = stat64(file_path, &stat_buf);
if (ret != 0) {
printf("failed to get file(%s) size, OS errno=%d.\n", file_path, errno);
return ret;
}
*size = (uint64)(stat_buf.st_size); /* size is long long */
if (*size > FILE_MAX_SIZE) {
printf("file(%s) is not database file, size(%lu).\n", file_path, *size);
*size = 0;
return 1;
}
return ret;
}
/*
* @Description: open file
* @Param[IN] fd: fd
* @Param[IN/OUT] file_path: file path
* @Return: 0 :op succeed; others failed
* @See also:
*/
int open_file(const char* file_path, int* fd)
{
int file_handle = 0;
int file_flag = O_RDWR; // relate to CUStorage::OpenFile
const int permission = PERMISSION;
CONFIRM_PATH(file_path);
if (fd == NULL) {
printf("the fd does not exist.\n");
exit(1);
}
file_handle = open(file_path, file_flag, permission);
if (file_handle == -1) {
printf("open file failed, file(%s), open flag(%d), OS errno=%d.\n", file_path, file_flag, errno);
return 1;
}
*fd = file_handle;
return 0;
}
/*
* @Description: wite file, atmost try 3 times
* @Param[IN] buf: write buffer
* @Param[IN] count: buffer count
* @Param[IN] fd: fd
* @Param[IN] offset: offset
* @Return: 0 :op succeed; others failed
* @See also:
*/
int write_file(int fd, const char* buf, int count, int offset)
{
int written_count = 0;
const int write_times = 3;
int try_times = 0;
for (; try_times < write_times; try_times++) {
written_count = pwrite(fd, buf, count, offset);
if (written_count == -1) {
printf("write file failed, request written count(%d), OS errno=%d.\n", count, errno);
return 1;
}
if (written_count == count) {
break;
} else if (written_count == 0) {
printf("write file 0 byte, request written count(%d), try again.\n", count);
(void)sleep(3);
continue;
} else {
printf(
"write file failed (%d BYTE), request written count(%d), OS errno=%d.\n", written_count, count, errno);
return 1;
}
}
if (write_times == try_times) {
printf("write file failed (%d BYTE), request written count(%d).\n", written_count, count);
return 1;
}
return 0;
}
/*
* @Description: append file
* @Param[IN] align_size: align size
* @Param[IN] file_path: file path
* @Return: 0 :op succeed; others failed
* @See also:
*/
int append_file(const char* file_path, int align_size)
{
int ret = 0;
int fd = 0;
int rc = 0;
char* buf = NULL;
int buf_size = 0;
uint64 size = 0;
uint64 remainder = 0;
CONFIRM_PATH(file_path);
ret = file_size(file_path, &size);
if (ret != 0) {
return ret;
}
remainder = size % align_size;
if (remainder == 0) {
return ret;
}
buf_size = align_size - remainder;
buf = (char*)malloc(buf_size);
if (buf == NULL) {
printf("failed to malloc size(%d) for file(%s).\n", buf_size, file_path);
return 1;
}
rc = memset_s(buf, buf_size, 0, buf_size);
securec_check_c(rc, "\0", "\0");
ret = open_file(file_path, &fd);
if (ret != 0) {
free(buf);
return ret;
}
ret = write_file(fd, buf, buf_size, size);
(void)close(fd);
free(buf);
if (ret != 0) {
return ret;
}
printf("successfully to align file(%s), original size(%lu) padding size(%d).\n", file_path, size, buf_size);
return ret;
}
/*
* @Description: recursive open file
* @Param[IN] args: args, not used
* @Param[IN] buf_size: dir path length
* @Param[IN] oper_proc: operate function
* @Param[IN] root: relative dir path
* @Param[IN] root_len: relative dir path length
* @Return: 0 :op succeed; others failed
* @See also:
*/
int oper_dir_recursive(const char* root, int buf_size, int root_len, oper_dir_entry_proc oper_proc, const void* args)
{
int ret = 0;
int tmp_ret = 0;
struct dirent* dir_entry = NULL;
struct dirent entry {};
DIR* dir = NULL;
CONFIRM_PATH(root);
if (oper_proc == NULL) {
printf("function pointer oper_dir_entry_proc not exist.\n");
exit(1);
}
/* this function do not care about this parameter, pass directory to oper_proc */
(void)args;
dir = opendir(root);
if (dir == NULL) {
printf("open directory failed, path=%s, OS errno=%d.\n", root, errno);
return 1;
}
dir_entry = &entry;
do {
/* in arm environment, readdir_r warning about deprecated-declarations,
but for thread safe keep using readdir_r. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
tmp_ret = readdir_r(dir, &entry, &dir_entry);
#pragma GCC diagnostic pop
if (dir_entry == NULL) {
break;
}
if (tmp_ret != 0) {
ret = 1;
break;
}
// skip "." and ".." entry
if ((0 == strcmp(".", dir_entry->d_name)) || (0 == strcmp("..", dir_entry->d_name))) {
continue;
}
tmp_ret = (*oper_proc)(root, buf_size, root_len, dir_entry->d_name, args);
if (tmp_ret != 0) {
ret = tmp_ret;
}
} while (true);
(void)closedir(dir);
return ret;
}
/*
* @Description: do align file
* @Param[IN] align_size: align size
* @Param[IN] file_path: file path
* @Return: 0 :op succeed; others failed
* @See also:
*/
static int do_align_file(char* file_path, int align_size)
{
// relate to CUStorage::OpenFile
CONFIRM_PATH(file_path);
if (!file_exist(file_path)) {
return 1;
}
if (file_check_column_style(file_path)) {
return append_file(file_path, align_size);
}
return 0;
}
/*
* @Description: filter file
* @Param[IN] args: not used
* @Param[IN] buf_size: entry path length
* @Param[IN] entry_name:entry path
* @Param[IN] root: relative dir path
* @Param[IN] root_len: relative dir path length
* @Return: 0 :op succeed; others failed
* @See also:
*/
int fliter_append_file_proc(const char* root, int buf_size, int root_len, const char* entry_name, const void* args)
{
int align_size = 0;
int rc = 0;
char file_path[FILE_MAX_PATH] = {0};
align_size = *(int*)args;
(void)buf_size;
if ((root_len + strlen(entry_name) + PATH_DELIMITER_SIZE + STRING_TERMINATOR) > FILE_MAX_PATH) {
printf("file path \"%s%c%s\"is too long, max file path is %d.\n", root, '/', entry_name, FILE_MAX_PATH);
return 1;
}
rc = snprintf_s(file_path, FILE_MAX_PATH, FILE_MAX_PATH - 1, "%s%c%s", root, '/', entry_name);
securec_check_ss_c(rc, "\0", "\0");
return do_align_file(file_path, align_size);
}
/*
* @Description: align files which in the directory recursive
* @Param[IN] align_size: align size
* @Param[IN] dir_path: dir path
* @Return: 0 :op succeed; others failed
* @See also:
*/
static int do_align_dir(const char* dir_path, int align_size)
{
int ret = 0;
CONFIRM_PATH(dir_path);
ret = oper_dir_recursive(
dir_path, (int)strlen(dir_path), (int)strlen(dir_path), fliter_append_file_proc, (const void*)&align_size);
if (ret != 0) {
printf("failed to get directory path(%s).\n", dir_path);
}
return ret;
}
/*
* @Description: align api function
* @Param[IN] align_size: align size
* @Param[IN] dir_path: dir path
* @Param[IN] file_path: file path
* @Return: 0 :op succeed; others failed
* @See also:
*/
static int do_align(const char* dir_path, char* file_path, int align_size)
{
int align = (align_size == 0) ? ALIGN_SIZE : align_size;
if (dir_path != NULL) {
return do_align_dir(dir_path, align);
}
if (file_path != NULL) {
return do_align_file(file_path, align);
}
return 0;
}
static void check_env_value(const char* input_env_value)
{
const char* danger_character_list[] = {"|",
";",
"&",
"$",
"<",
">",
"`",
"\\",
"'",
"\"",
"{",
"}",
"(",
")",
"[",
"]",
"~",
"*",
"?",
"!",
"\n",
NULL};
int i = 0;
for (i = 0; danger_character_list[i] != NULL; i++) {
if (strstr(input_env_value, danger_character_list[i]) != NULL) {
fprintf(stderr,
_("ERROR: Failed to check environment value: invalid token \"%s\".\n"),
danger_character_list[i]);
exit(1);
}
}
}
/*
* @Description: main entry
* @Param[IN] argc: param count
* @Param[IN] argv: param list
* @Return:
* @See also:
*/
int main(int argc, char* argv[])
{
int c;
const char* progname = "pg_format_cu";
char* dir_path = NULL;
char* file_path = NULL;
int align_size = 0;
bool conflit = false;
if (argc == 1) {
help(progname);
exit(0);
}
if (argc > 1) {
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0 || strcmp(argv[1], "-h") == 0) {
help(progname);
exit(0);
}
}
while ((c = getopt(argc, argv, "d:f:a:")) != -1) {
switch (c) {
case 'd':
if (conflit) {
printf("can not support combination -d and -f.\n");
exit(1);
}
check_env_value(optarg);
dir_path = optarg;
conflit = true;
break;
case 'f':
if (conflit) {
printf("can not support combination -d and -f.\n");
exit(1);
}
check_env_value(optarg);
file_path = optarg;
conflit = true;
break;
case 'a':
check_env_value(optarg);
align_size = atoi(optarg);
break;
default:
break;
}
}
if (dir_path == NULL && file_path == NULL) {
printf("you must give the direct path or file path.\n");
exit(1);
}
if (align_size < 0) {
printf("you must give the correct align size.\n");
exit(1);
}
if (align_size % ALIGN_SIZE != 0) {
printf("you must give the align size %d integer multiple .\n", ALIGN_SIZE);
exit(1);
}
if (do_align(dir_path, file_path, align_size) != 0) {
exit(1);
}
printf("successfully to align all files.\n");
return 0;
}
#ifdef __cplusplus
}
#endif