@@ -340,6 +340,16 @@ pgFileComparePath(const void *f1, const void *f2)
340340return 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)
10411051opt_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 ,
10531199bool extract_tablespaces ,fio_location location )
10541200{
10551201parray * dirs ,
@@ -1234,6 +1380,8 @@ read_tablespace_map(parray *files, const char *backup_dir)
12341380file -> path = pgut_malloc (strlen (link_name )+ 1 );
12351381strcpy (file -> path ,link_name );
12361382
1383+ file -> name = file -> path ;
1384+
12371385file -> linked = pgut_malloc (strlen (path )+ 1 );
12381386strcpy (file -> linked ,path );
12391387