@@ -340,6 +340,16 @@ pgFileComparePath(const void *f1, const void *f2)
340340 return strcmp(f1p->path, f2p->path);
341341}
342342
343+ /* Compare two pgFile with their name in ascending order of ASCII code. */
344+ int
345+ pgFileCompareName(const void *f1, const void *f2)
346+ {
347+ pgFile *f1p = *(pgFile **)f1;
348+ pgFile *f2p = *(pgFile **)f2;
349+
350+ return strcmp(f1p->name, f2p->name);
351+ }
352+
343353/*
344354 * Compare two pgFile with their path and external_dir_num
345355 * in ascending order of ASCII code.
@@ -1041,6 +1051,142 @@ opt_externaldir_map(ConfigOption *opt, const char *arg)
10411051 opt_path_map(opt, arg, &external_remap_list, "external directory");
10421052}
10431053
1054+ /*
1055+ * Create backup directories from **backup_dir** to **data_dir**. Doesn't raise
1056+ * an error if target directories exist.
1057+ *
1058+ * If **extract_tablespaces** is true then try to extract tablespace data
1059+ * directories into their initial path using tablespace_map file.
1060+ *
1061+ * Enforce permissions from backup_content.control. The only
1062+ * problem now is with PGDATA itself, we must preserve PGDATA permissions
1063+ * somewhere.
1064+ *
1065+ * TODO: symlink handling. If user located symlink in PG_TBLSPC_DIR, it will
1066+ * be restored as directory.
1067+ */
1068+ void
1069+ create_data_directories(parray *dest_files, const char *data_dir, const char *backup_dir,
1070+ bool extract_tablespaces, fio_location location)
1071+ {
1072+ int i;
1073+ parray *links = NULL;
1074+ mode_t pg_tablespace_mode;
1075+ char to_path[MAXPGPATH];
1076+
1077+ /* Ugly: get PG_TBLSPC_DIR pemission mask */
1078+ for (i = 0; i < parray_num(dest_files); i++)
1079+ {
1080+ pgFile *file = (pgFile *) parray_get(dest_files, i);
1081+
1082+ if (!S_ISDIR(file->mode))
1083+ continue;
1084+
1085+ if (strcmp(file->rel_path, PG_TBLSPC_DIR) == 0)
1086+ {
1087+ if (file->external_dir_num == 0)
1088+ {
1089+ pg_tablespace_mode = file->mode;
1090+ break;
1091+ }
1092+ }
1093+ }
1094+
1095+ /* get tablespace map */
1096+ if (extract_tablespaces)
1097+ {
1098+ links = parray_new();
1099+ read_tablespace_map(links, backup_dir);
1100+ /* Sort links by a link name */
1101+ parray_qsort(links, pgFileCompareName);
1102+ }
1103+
1104+ /* Fun part is that backup_content.control is from beginning
1105+ * of a backup, and tablespace_map is from the end
1106+ * of a backup.
1107+ * If we trust tablspace_map, we would have to we create first
1108+ * tablespaces from it, then the start creating directories and files
1109+ * from backup_content.
1110+ * The problem if that backup_content could contain files from
1111+ * deleted tablespaces and so would have to
1112+ * check every file and directory if it comes from tablespace,
1113+ * not presented in tablespace_map and skip it restoring if it
1114+ * is not.
1115+ * Trusting backup_content.control is safest way, there is no risk
1116+ * of not restoring something.
1117+ */
1118+
1119+ elog(LOG, "Restore directories and symlinks...");
1120+
1121+ /* create directories */
1122+ for (i = 0; i < parray_num(dest_files); i++)
1123+ {
1124+ char parent_dir[MAXPGPATH];
1125+ pgFile *dir = (pgFile *) parray_get(dest_files, i);
1126+
1127+ if (!S_ISDIR(dir->mode))
1128+ continue;
1129+
1130+ /* skip external directory content */
1131+ if (dir->external_dir_num != 0)
1132+ continue;
1133+
1134+ /* tablespace_map exists */
1135+ if (links)
1136+ {
1137+ /* get parent dir of rel_path */
1138+ strncpy(parent_dir, dir->rel_path, MAXPGPATH);
1139+ get_parent_directory(parent_dir);
1140+
1141+ /* check if directory is actually link to tablespace */
1142+ if (strcmp(parent_dir, PG_TBLSPC_DIR) == 0)
1143+ {
1144+ /* this directory located in pg_tblspc
1145+ * check it against tablespace map
1146+ */
1147+ pgFile **link = (pgFile **) parray_bsearch(links, dir, pgFileCompareName);
1148+
1149+ /* got match */
1150+ if (link)
1151+ {
1152+ const char *linked_path = get_tablespace_mapping((*link)->linked);
1153+
1154+ if (!is_absolute_path(linked_path))
1155+ elog(ERROR, "Tablespace directory is not an absolute path: %s\n",
1156+ linked_path);
1157+
1158+ join_path_components(to_path, data_dir, dir->rel_path);
1159+
1160+ elog(VERBOSE, "Create directory \"%s\" and symbolic link \"%s\"",
1161+ linked_path, to_path);
1162+
1163+ /* create tablespace directory */
1164+ fio_mkdir(linked_path, pg_tablespace_mode, location);
1165+
1166+ /* create link to linked_path */
1167+ if (fio_symlink(linked_path, to_path, location) < 0)
1168+ elog(ERROR, "Could not create symbolic link \"%s\": %s",
1169+ to_path, strerror(errno));
1170+
1171+ continue;
1172+ }
1173+ }
1174+ }
1175+
1176+ /* This is not symlink, create directory */
1177+ elog(INFO, "Create directory \"%s\"", dir->rel_path);
1178+
1179+ join_path_components(to_path, data_dir, dir->rel_path);
1180+ fio_mkdir(to_path, dir->mode, location);
1181+ }
1182+
1183+ if (extract_tablespaces)
1184+ {
1185+ parray_walk(links, pgFileFree);
1186+ parray_free(links);
1187+ }
1188+ }
1189+
10441190/*
10451191 * Create backup directories from **backup_dir** to **data_dir**. Doesn't raise
10461192 * an error if target directories exist.
@@ -1049,7 +1195,7 @@ opt_externaldir_map(ConfigOption *opt, const char *arg)
10491195 * directories into their initial path using tablespace_map file.
10501196 */
10511197void
1052- create_data_directories (const char *data_dir, const char *backup_dir,
1198+ create_data_directories_manual (const char *data_dir, const char *backup_dir,
10531199 bool extract_tablespaces, fio_location location)
10541200{
10551201 parray *dirs,
@@ -1234,6 +1380,8 @@ read_tablespace_map(parray *files, const char *backup_dir)
12341380 file->path = pgut_malloc(strlen(link_name) + 1);
12351381 strcpy(file->path, link_name);
12361382
1383+ file->name = file->path;
1384+
12371385 file->linked = pgut_malloc(strlen(path) + 1);
12381386 strcpy(file->linked, path);
12391387
0 commit comments