@@ -1109,6 +1109,76 @@ backup_cleanup(bool fatal, void *userdata)
1109
1109
}
1110
1110
}
1111
1111
1112
+ /* Count bytes in file */
1113
+ static long
1114
+ file_size (const char * file )
1115
+ {
1116
+ long r ;
1117
+ FILE * f = fopen (file , "r" );
1118
+
1119
+ if (!f )
1120
+ {
1121
+ elog (ERROR , "pg_probackup: could not open file \"%s\" for reading: %s\n" ,
1122
+ file , strerror (errno ));
1123
+ return -1 ;
1124
+ }
1125
+ fseek (f , 0 , SEEK_END );
1126
+ r = ftell (f );
1127
+ fclose (f );
1128
+ return r ;
1129
+ }
1130
+
1131
+ /*
1132
+ * Find corresponding file in previous backup.
1133
+ * Compare generations and return true if we don't need full copy
1134
+ * of the file, but just part of it.
1135
+ *
1136
+ * skip_size - size of the file in previous backup. We can skip it
1137
+ * and copy just remaining part of the file.
1138
+ */
1139
+ bool
1140
+ backup_compressed_file_partially (pgFile * file , void * arg , size_t * skip_size )
1141
+ {
1142
+ bool result = false;
1143
+ pgFile * prev_file = NULL ;
1144
+ size_t current_file_size ;
1145
+ backup_files_args * arguments = (backup_files_args * ) arg ;
1146
+
1147
+ if (arguments -> prev_files )
1148
+ {
1149
+ pgFile * * p = (pgFile * * ) parray_bsearch (arguments -> prev_files ,
1150
+ file , pgFileComparePath );
1151
+ if (p )
1152
+ prev_file = * p ;
1153
+
1154
+ /* If file's gc generation has changed since last backup, just copy it*/
1155
+ if (prev_file && prev_file -> generation == file -> generation )
1156
+ {
1157
+ current_file_size = file_size (file -> path );
1158
+
1159
+ if (prev_file -> write_size == BYTES_INVALID )
1160
+ return false;
1161
+
1162
+ * skip_size = prev_file -> write_size ;
1163
+
1164
+ if (current_file_size >= prev_file -> write_size )
1165
+ {
1166
+ elog (LOG , "Backup file %s partially: prev_size %lu, current_size %lu" ,
1167
+ file -> path , prev_file -> write_size , current_file_size );
1168
+ result = true;
1169
+ }
1170
+ else
1171
+ elog (ERROR , "Something is wrong with %s. current_file_size %lu, prev %lu" ,
1172
+ file -> path , current_file_size , prev_file -> write_size );
1173
+ }
1174
+ else
1175
+ elog (LOG , "Copy full %s. Generations are different: old=%d; new=%d" ,
1176
+ file -> path , prev_file -> generation , file -> generation );
1177
+ }
1178
+
1179
+ return result ;
1180
+ }
1181
+
1112
1182
/*
1113
1183
* Take differential backup at page level.
1114
1184
*/
@@ -1207,9 +1277,47 @@ backup_files(void *arg)
1207
1277
}
1208
1278
1209
1279
/* copy the file into backup */
1210
- if (!(file -> is_datafile
1211
- ? backup_data_file (arguments -> from_root , arguments -> to_root , file , arguments -> lsn )
1212
- : copy_file (arguments -> from_root , arguments -> to_root , file )))
1280
+ if (file -> is_datafile )
1281
+ {
1282
+ if (!backup_data_file (arguments -> from_root ,
1283
+ arguments -> to_root , file ,
1284
+ arguments -> lsn ))
1285
+ {
1286
+ /* record as skipped file in file_xxx.txt */
1287
+ file -> write_size = BYTES_INVALID ;
1288
+ elog (LOG , "skip" );
1289
+ continue ;
1290
+ }
1291
+ }
1292
+ else if (is_compressed_data_file (file ))
1293
+ {
1294
+ size_t skip_size = 0 ;
1295
+ if (backup_compressed_file_partially (file , arguments , & skip_size ))
1296
+ {
1297
+ /* backup cfs segment partly */
1298
+ if (!copy_file_partly (arguments -> from_root ,
1299
+ arguments -> to_root ,
1300
+ file , skip_size ))
1301
+ {
1302
+ /* record as skipped file in file_xxx.txt */
1303
+ file -> write_size = BYTES_INVALID ;
1304
+ elog (LOG , "skip" );
1305
+ continue ;
1306
+ }
1307
+ }
1308
+ else if (!copy_file (arguments -> from_root ,
1309
+ arguments -> to_root ,
1310
+ file ))
1311
+ {
1312
+ /* record as skipped file in file_xxx.txt */
1313
+ file -> write_size = BYTES_INVALID ;
1314
+ elog (LOG , "skip" );
1315
+ continue ;
1316
+ }
1317
+ }
1318
+ else if (!copy_file (arguments -> from_root ,
1319
+ arguments -> to_root ,
1320
+ file ))
1213
1321
{
1214
1322
/* record as skipped file in file_xxx.txt */
1215
1323
file -> write_size = BYTES_INVALID ;
@@ -1258,14 +1366,14 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1258
1366
relative = file -> path + strlen (root ) + 1 ;
1259
1367
if (is_pgdata &&
1260
1368
!path_is_prefix_of_path ("base" , relative ) &&
1261
- /*!path_is_prefix_of_path("global", relative) &&*/
1369
+ /*!path_is_prefix_of_path("global", relative) &&*/ //TODO What's wrong with this line?
1262
1370
!path_is_prefix_of_path ("pg_tblspc" , relative ))
1263
1371
continue ;
1264
1372
1265
1373
/* Get file name from path */
1266
1374
fname = last_dir_separator (relative );
1267
1375
1268
- /* Remove temp tables */
1376
+ /* Remove temp tables from the list */
1269
1377
if (fname [0 ] == 't' && isdigit (fname [1 ]))
1270
1378
{
1271
1379
pgFileFree (file );
@@ -1275,7 +1383,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1275
1383
}
1276
1384
1277
1385
path_len = strlen (file -> path );
1278
- /* Get link ptrack file to realations files */
1386
+ /* Get link ptrack file to relations files */
1279
1387
if (path_len > 6 && strncmp (file -> path + (path_len - 6 ), "ptrack" , 6 ) == 0 )
1280
1388
{
1281
1389
pgFile * search_file ;
@@ -1284,12 +1392,15 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1284
1392
while (true) {
1285
1393
pgFile tmp_file ;
1286
1394
tmp_file .path = pg_strdup (file -> path );
1287
- /* I hope segno not more than 999999 */
1395
+
1396
+ /* Segno fits into 6 digits since it is not more than 4000 */
1288
1397
if (segno > 0 )
1289
1398
sprintf (tmp_file .path + path_len - 7 , ".%d" , segno );
1290
1399
else
1291
1400
tmp_file .path [path_len - 7 ] = '\0' ;
1401
+
1292
1402
pre_search_file = (pgFile * * ) parray_bsearch (list_file , & tmp_file , pgFileComparePath );
1403
+
1293
1404
if (pre_search_file != NULL )
1294
1405
{
1295
1406
search_file = * pre_search_file ;
@@ -1303,6 +1414,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1303
1414
segno ++ ;
1304
1415
}
1305
1416
1417
+ /* Remove ptrack file itself from backup list */
1306
1418
pgFileFree (file );
1307
1419
parray_remove (list_file , i );
1308
1420
i -- ;
@@ -1312,9 +1424,9 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1312
1424
/* compress map file it is not data file */
1313
1425
if (path_len > 4 && strncmp (file -> path + (path_len - 4 ), ".cfm" , 4 ) == 0 )
1314
1426
{
1315
- if (current .backup_mode == BACKUP_MODE_DIFF_PTRACK ||
1316
- current . backup_mode == BACKUP_MODE_DIFF_PAGE )
1317
- elog ( ERROR , "You can't use incremental backup with compress tablespace " );
1427
+ if (current .backup_mode == BACKUP_MODE_DIFF_PAGE )
1428
+ elog ( ERROR , "You can't use PAGE mode backup with compressed tablespace.\n"
1429
+ "Try FULL or PTRACK mode instead. " );
1318
1430
continue ;
1319
1431
}
1320
1432
@@ -1362,11 +1474,34 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1362
1474
pgFile tmp_file ;
1363
1475
tmp_file .path = pg_strdup (file -> path );
1364
1476
tmp_file .path [path_len - 4 ] = '\0' ;
1365
- pre_search_file = (pgFile * * ) parray_bsearch (list_file , & tmp_file , pgFileComparePath );
1477
+ pre_search_file = (pgFile * * ) parray_bsearch (list_file ,
1478
+ & tmp_file , pgFileComparePath );
1366
1479
if (pre_search_file != NULL )
1367
1480
{
1481
+ FileMap * map ;
1482
+ int md = open (file -> path , O_RDWR |PG_BINARY , 0 );
1483
+ if (md < 0 )
1484
+ elog (ERROR , "add_files(). cannot open cfm file '%s'" , file -> path );
1485
+
1486
+ map = cfs_mmap (md );
1487
+ if (map == MAP_FAILED )
1488
+ {
1489
+ elog (LOG , "add_files(). cfs_compression_ration failed to map file %s: %m" , file -> path );
1490
+ close (md );
1491
+ break ;
1492
+ }
1493
+
1494
+ (* pre_search_file )-> generation = map -> generation ;
1368
1495
(* pre_search_file )-> is_datafile = false;
1496
+
1497
+ if (cfs_munmap (map ) < 0 )
1498
+ elog (LOG , "add_files(). CFS failed to unmap file %s: %m" , file -> path );
1499
+ if (close (md ) < 0 )
1500
+ elog (LOG , "add_files(). CFS failed to close file %s: %m" , file -> path );
1369
1501
}
1502
+ else
1503
+ elog (ERROR , "corresponding segment '%s' is not found" , tmp_file .path );
1504
+
1370
1505
pg_free (tmp_file .path );
1371
1506
}
1372
1507
}
@@ -1638,3 +1773,34 @@ StreamLog(void *arg)
1638
1773
PQfinish (conn );
1639
1774
conn = NULL ;
1640
1775
}
1776
+
1777
+
1778
+ FileMap * cfs_mmap (int md )
1779
+ {
1780
+ FileMap * map ;
1781
+ #ifdef WIN32
1782
+ HANDLE mh = CreateFileMapping (_get_osfhandle (md ), NULL , PAGE_READWRITE ,
1783
+ 0 , (DWORD )sizeof (FileMap ), NULL );
1784
+ if (mh == NULL )
1785
+ return (FileMap * )MAP_FAILED ;
1786
+
1787
+ map = (FileMap * )MapViewOfFile (mh , FILE_MAP_ALL_ACCESS , 0 , 0 , 0 );
1788
+ CloseHandle (mh );
1789
+ if (map == NULL )
1790
+ return (FileMap * )MAP_FAILED ;
1791
+
1792
+ #else
1793
+ map = (FileMap * )mmap (NULL , sizeof (FileMap ),
1794
+ PROT_WRITE | PROT_READ , MAP_SHARED , md , 0 );
1795
+ #endif
1796
+ return map ;
1797
+ }
1798
+
1799
+ int cfs_munmap (FileMap * map )
1800
+ {
1801
+ #ifdef WIN32
1802
+ return UnmapViewOfFile (map ) ? 0 : -1 ;
1803
+ #else
1804
+ return munmap (map , sizeof (FileMap ));
1805
+ #endif
1806
+ }
0 commit comments