From ead629bfa7a051453522f64bf84a933320ea1b72 Mon Sep 17 00:00:00 2001 From: Grigory Smolkin Date: Mon, 27 May 2019 11:27:43 +0300 Subject: [PATCH] Issue 67: permission mask is not preserved by restore --- src/dir.c | 150 ++++++++++++++++++++++++++++++++++++++++++++- src/merge.c | 2 +- src/pg_probackup.h | 9 ++- src/restore.c | 2 +- 4 files changed, 159 insertions(+), 4 deletions(-) diff --git a/src/dir.c b/src/dir.c index b2afa3f6..f8637947 100644 --- a/src/dir.c +++ b/src/dir.c @@ -340,6 +340,16 @@ pgFileComparePath(const void *f1, const void *f2) return strcmp(f1p->path, f2p->path); } +/* Compare two pgFile with their name in ascending order of ASCII code. */ +int +pgFileCompareName(const void *f1, const void *f2) +{ + pgFile *f1p = *(pgFile **)f1; + pgFile *f2p = *(pgFile **)f2; + + return strcmp(f1p->name, f2p->name); +} + /* * Compare two pgFile with their path and external_dir_num * in ascending order of ASCII code. @@ -1047,9 +1057,145 @@ opt_externaldir_map(ConfigOption *opt, const char *arg) * * If **extract_tablespaces** is true then try to extract tablespace data * directories into their initial path using tablespace_map file. + * + * Enforce permissions from backup_content.control. The only + * problem now is with PGDATA itself, we must preserve PGDATA permissions + * somewhere. + * + * TODO: symlink handling. If user located symlink in PG_TBLSPC_DIR, it will + * be restored as directory. */ void -create_data_directories(const char *data_dir, const char *backup_dir, +create_data_directories(parray *dest_files, const char *data_dir, const char *backup_dir, + bool extract_tablespaces, fio_location location) +{ + int i; + parray *links = NULL; + mode_t pg_tablespace_mode; + char to_path[MAXPGPATH]; + + /* Ugly: get PG_TBLSPC_DIR pemission mask */ + for (i = 0; i < parray_num(dest_files); i++) + { + pgFile *file = (pgFile *) parray_get(dest_files, i); + + if (!S_ISDIR(file->mode)) + continue; + + if (strcmp(file->rel_path, PG_TBLSPC_DIR) == 0) + { + if (file->external_dir_num == 0) + { + pg_tablespace_mode = file->mode; + break; + } + } + } + + /* get tablespace map */ + if (extract_tablespaces) + { + links = parray_new(); + read_tablespace_map(links, backup_dir); + /* Sort links by a link name */ + parray_qsort(links, pgFileCompareName); + } + + /* Fun part is that backup_content.control is from beginning + * of a backup, and tablespace_map is from the end + * of a backup. + * If we trust tablspace_map, we would have to we create first + * tablespaces from it, then the start creating directories and files + * from backup_content. + * The problem if that backup_content could contain files from + * deleted tablespaces and so would have to + * check every file and directory if it comes from tablespace, + * not presented in tablespace_map and skip it restoring if it + * is not. + * Trusting backup_content.control is safest way, there is no risk + * of not restoring something. + */ + + elog(LOG, "Restore directories and symlinks..."); + + /* create directories */ + for (i = 0; i < parray_num(dest_files); i++) + { + char parent_dir[MAXPGPATH]; + pgFile *dir = (pgFile *) parray_get(dest_files, i); + + if (!S_ISDIR(dir->mode)) + continue; + + /* skip external directory content */ + if (dir->external_dir_num != 0) + continue; + + /* tablespace_map exists */ + if (links) + { + /* get parent dir of rel_path */ + strncpy(parent_dir, dir->rel_path, MAXPGPATH); + get_parent_directory(parent_dir); + + /* check if directory is actually link to tablespace */ + if (strcmp(parent_dir, PG_TBLSPC_DIR) == 0) + { + /* this directory located in pg_tblspc + * check it against tablespace map + */ + pgFile **link = (pgFile **) parray_bsearch(links, dir, pgFileCompareName); + + /* got match */ + if (link) + { + const char *linked_path = get_tablespace_mapping((*link)->linked); + + if (!is_absolute_path(linked_path)) + elog(ERROR, "Tablespace directory is not an absolute path: %s\n", + linked_path); + + join_path_components(to_path, data_dir, dir->rel_path); + + elog(VERBOSE, "Create directory \"%s\" and symbolic link \"%s\"", + linked_path, to_path); + + /* create tablespace directory */ + fio_mkdir(linked_path, pg_tablespace_mode, location); + + /* create link to linked_path */ + if (fio_symlink(linked_path, to_path, location) < 0) + elog(ERROR, "Could not create symbolic link \"%s\": %s", + to_path, strerror(errno)); + + continue; + } + } + } + + /* This is not symlink, create directory */ + elog(INFO, "Create directory \"%s\"", dir->rel_path); + + join_path_components(to_path, data_dir, dir->rel_path); + fio_mkdir(to_path, dir->mode, location); + } + + if (extract_tablespaces) + { + parray_walk(links, pgFileFree); + parray_free(links); + } +} + +/* + * Create backup directories from **backup_dir** to **data_dir**. Doesn't raise + * an error if target directories exist. + * + * If **extract_tablespaces** is true then try to extract tablespace data + * directories into their initial path using tablespace_map file. + */ +void +create_data_directories_manual(const char *data_dir, const char *backup_dir, bool extract_tablespaces, fio_location location) { parray *dirs, @@ -1234,6 +1380,8 @@ read_tablespace_map(parray *files, const char *backup_dir) file->path = pgut_malloc(strlen(link_name) + 1); strcpy(file->path, link_name); + file->name = file->path; + file->linked = pgut_malloc(strlen(path) + 1); strcpy(file->linked, path); diff --git a/src/merge.c b/src/merge.c index 3ac73cf7..43103865 100644 --- a/src/merge.c +++ b/src/merge.c @@ -255,7 +255,7 @@ merge_backups(pgBackup *to_backup, pgBackup *from_backup) write_backup_status(to_backup, BACKUP_STATUS_MERGING); write_backup_status(from_backup, BACKUP_STATUS_MERGING); - create_data_directories(to_database_path, from_backup_path, false, FIO_BACKUP_HOST); + create_data_directories(files, to_database_path, from_backup_path, false, FIO_BACKUP_HOST); threads = (pthread_t *) palloc(sizeof(pthread_t) * num_threads); threads_args = (merge_files_arg *) palloc(sizeof(merge_files_arg) * num_threads); diff --git a/src/pg_probackup.h b/src/pg_probackup.h index 4527a33e..9dc6d115 100644 --- a/src/pg_probackup.h +++ b/src/pg_probackup.h @@ -577,11 +577,17 @@ extern const char* deparse_compress_alg(int alg); extern void dir_list_file(parray *files, const char *root, bool exclude, bool omit_symlink, bool add_root, int external_dir_num, fio_location location); -extern void create_data_directories(const char *data_dir, +extern void create_data_directories_manual(const char *data_dir, const char *backup_dir, bool extract_tablespaces, fio_location location); +extern void create_data_directories(parray *dest_files, + const char *data_dir, + const char *backup_dir, + bool extract_tablespaces, + fio_location location); + extern void read_tablespace_map(parray *files, const char *backup_dir); extern void opt_tablespace_map(ConfigOption *opt, const char *arg); extern void opt_externaldir_map(ConfigOption *opt, const char *arg); @@ -614,6 +620,7 @@ extern void pgFileDelete(pgFile *file); extern void pgFileFree(void *file); extern pg_crc32 pgFileGetCRC(const char *file_path, bool use_crc32c, bool raise_on_deleted, size_t *bytes_read, fio_location location); +extern int pgFileCompareName(const void *f1, const void *f2); extern int pgFileComparePath(const void *f1, const void *f2); extern int pgFileComparePathWithExternal(const void *f1, const void *f2); extern int pgFileCompareRelPathWithExternal(const void *f1, const void *f2); diff --git a/src/restore.c b/src/restore.c index 0e1fde48..613e8035 100644 --- a/src/restore.c +++ b/src/restore.c @@ -442,7 +442,7 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt, */ pgBackupGetPath(dest_backup, dest_backup_path, lengthof(dest_backup_path), NULL); - create_data_directories(instance_config.pgdata, dest_backup_path, true, + create_data_directories(dest_files, instance_config.pgdata, dest_backup_path, true, FIO_DB_HOST); /*