@@ -1105,6 +1105,75 @@ backup_cleanup(bool fatal, void *userdata)
1105
1105
}
1106
1106
}
1107
1107
1108
+ /* Count bytes in file */
1109
+ static long
1110
+ file_size (const char * file )
1111
+ {
1112
+ long r ;
1113
+ FILE * f = fopen (file , "r" );
1114
+
1115
+ if (!f )
1116
+ {
1117
+ elog (ERROR , "pg_probackup: could not open file \"%s\" for reading: %s\n" ,
1118
+ file , strerror (errno ));
1119
+ return -1 ;
1120
+ }
1121
+ fseek (f , 0 , SEEK_END );
1122
+ r = ftell (f );
1123
+ fclose (f );
1124
+ return r ;
1125
+ }
1126
+
1127
+ /*
1128
+ * Find corresponding file in previous backup.
1129
+ * Compare generations and return true if we don't need full copy
1130
+ * of the file, but just part of it.
1131
+ *
1132
+ * skip_size - size of the file in previous backup. We can skip it
1133
+ * and copy just remaining part of the file.
1134
+ */
1135
+ bool
1136
+ backup_compressed_file_partially (pgFile * file , void * arg , size_t * skip_size )
1137
+ {
1138
+ bool result = false;
1139
+ pgFile * prev_file = NULL ;
1140
+ size_t current_file_size ;
1141
+ backup_files_args * arguments = (backup_files_args * ) arg ;
1142
+
1143
+ if (arguments -> prev_files )
1144
+ {
1145
+ pgFile * * p = (pgFile * * ) parray_bsearch (arguments -> prev_files ,
1146
+ file , pgFileComparePath );
1147
+ if (p )
1148
+ prev_file = * p ;
1149
+
1150
+ /* If file's gc generation has changed since last backup, just copy it*/
1151
+ if (prev_file && prev_file -> generation == file -> generation )
1152
+ {
1153
+ current_file_size = file_size (file -> path );
1154
+
1155
+ if (prev_file -> write_size == BYTES_INVALID )
1156
+ return false;
1157
+
1158
+ * skip_size = prev_file -> write_size ;
1159
+
1160
+ if (current_file_size >= prev_file -> write_size )
1161
+ {
1162
+ elog (LOG , "Backup file %s partially: prev_size %lu, current_size %lu" ,
1163
+ file -> path , prev_file -> write_size , current_file_size );
1164
+ result = true;
1165
+ }
1166
+ else
1167
+ elog (ERROR , "Something is wrong with %s. current_file_size %lu, prev %lu" ,
1168
+ file -> path , current_file_size , prev_file -> write_size );
1169
+ }
1170
+ else
1171
+ elog (LOG , "Copy full %s." , file -> path );
1172
+ }
1173
+
1174
+ return result ;
1175
+ }
1176
+
1108
1177
/*
1109
1178
* Take differential backup at page level.
1110
1179
*/
@@ -1203,9 +1272,47 @@ backup_files(void *arg)
1203
1272
}
1204
1273
1205
1274
/* copy the file into backup */
1206
- if (!(file -> is_datafile
1207
- ? backup_data_file (arguments -> from_root , arguments -> to_root , file , arguments -> lsn )
1208
- : copy_file (arguments -> from_root , arguments -> to_root , file )))
1275
+ if (file -> is_datafile )
1276
+ {
1277
+ if (!backup_data_file (arguments -> from_root ,
1278
+ arguments -> to_root , file ,
1279
+ arguments -> lsn ))
1280
+ {
1281
+ /* record as skipped file in file_xxx.txt */
1282
+ file -> write_size = BYTES_INVALID ;
1283
+ elog (LOG , "skip" );
1284
+ continue ;
1285
+ }
1286
+ }
1287
+ else if (is_compressed_data_file (file ))
1288
+ {
1289
+ size_t skip_size = 0 ;
1290
+ if (backup_compressed_file_partially (file , arguments , & skip_size ))
1291
+ {
1292
+ /* backup cfs segment partly */
1293
+ if (!copy_file_partly (arguments -> from_root ,
1294
+ arguments -> to_root ,
1295
+ file , skip_size ))
1296
+ {
1297
+ /* record as skipped file in file_xxx.txt */
1298
+ file -> write_size = BYTES_INVALID ;
1299
+ elog (LOG , "skip" );
1300
+ continue ;
1301
+ }
1302
+ }
1303
+ else if (!copy_file (arguments -> from_root ,
1304
+ arguments -> to_root ,
1305
+ file ))
1306
+ {
1307
+ /* record as skipped file in file_xxx.txt */
1308
+ file -> write_size = BYTES_INVALID ;
1309
+ elog (LOG , "skip" );
1310
+ continue ;
1311
+ }
1312
+ }
1313
+ else if (!copy_file (arguments -> from_root ,
1314
+ arguments -> to_root ,
1315
+ file ))
1209
1316
{
1210
1317
/* record as skipped file in file_xxx.txt */
1211
1318
file -> write_size = BYTES_INVALID ;
@@ -1254,14 +1361,14 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1254
1361
relative = file -> path + strlen (root ) + 1 ;
1255
1362
if (is_pgdata &&
1256
1363
!path_is_prefix_of_path ("base" , relative ) &&
1257
- /*!path_is_prefix_of_path("global", relative) &&*/
1364
+ /*!path_is_prefix_of_path("global", relative) &&*/ //TODO What's wrong with this line?
1258
1365
!path_is_prefix_of_path ("pg_tblspc" , relative ))
1259
1366
continue ;
1260
1367
1261
1368
/* Get file name from path */
1262
1369
fname = last_dir_separator (relative );
1263
1370
1264
- /* Remove temp tables */
1371
+ /* Remove temp tables from the list */
1265
1372
if (fname [0 ] == 't' && isdigit (fname [1 ]))
1266
1373
{
1267
1374
pgFileFree (file );
@@ -1271,7 +1378,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1271
1378
}
1272
1379
1273
1380
path_len = strlen (file -> path );
1274
- /* Get link ptrack file to realations files */
1381
+ /* Get link ptrack file to relations files */
1275
1382
if (path_len > 6 && strncmp (file -> path + (path_len - 6 ), "ptrack" , 6 ) == 0 )
1276
1383
{
1277
1384
pgFile * search_file ;
@@ -1280,12 +1387,15 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1280
1387
while (true) {
1281
1388
pgFile tmp_file ;
1282
1389
tmp_file .path = pg_strdup (file -> path );
1283
- /* I hope segno not more than 999999 */
1390
+
1391
+ /* Segno fits into 6 digits since it is not more than 4000 */
1284
1392
if (segno > 0 )
1285
1393
sprintf (tmp_file .path + path_len - 7 , ".%d" , segno );
1286
1394
else
1287
1395
tmp_file .path [path_len - 7 ] = '\0' ;
1396
+
1288
1397
pre_search_file = (pgFile * * ) parray_bsearch (list_file , & tmp_file , pgFileComparePath );
1398
+
1289
1399
if (pre_search_file != NULL )
1290
1400
{
1291
1401
search_file = * pre_search_file ;
@@ -1299,6 +1409,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1299
1409
segno ++ ;
1300
1410
}
1301
1411
1412
+ /* Remove ptrack file itself from backup list */
1302
1413
pgFileFree (file );
1303
1414
parray_remove (list_file , i );
1304
1415
i -- ;
@@ -1307,12 +1418,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1307
1418
1308
1419
/* compress map file it is not data file */
1309
1420
if (path_len > 4 && strncmp (file -> path + (path_len - 4 ), ".cfm" , 4 ) == 0 )
1310
- {
1311
- if (current .backup_mode == BACKUP_MODE_DIFF_PTRACK ||
1312
- current .backup_mode == BACKUP_MODE_DIFF_PAGE )
1313
- elog (ERROR , "You can't use incremental backup with compress tablespace" );
1314
1421
continue ;
1315
- }
1316
1422
1317
1423
/* name of data file start with digit */
1318
1424
if (fname == NULL )
@@ -1358,11 +1464,34 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1358
1464
pgFile tmp_file ;
1359
1465
tmp_file .path = pg_strdup (file -> path );
1360
1466
tmp_file .path [path_len - 4 ] = '\0' ;
1361
- pre_search_file = (pgFile * * ) parray_bsearch (list_file , & tmp_file , pgFileComparePath );
1467
+ pre_search_file = (pgFile * * ) parray_bsearch (list_file ,
1468
+ & tmp_file , pgFileComparePath );
1362
1469
if (pre_search_file != NULL )
1363
1470
{
1471
+ FileMap * map ;
1472
+ int md = open (file -> path , O_RDWR |PG_BINARY , 0 );
1473
+ if (md < 0 )
1474
+ elog (ERROR , "add_files(). cannot open cfm file '%s'" , file -> path );
1475
+
1476
+ map = cfs_mmap (md );
1477
+ if (map == MAP_FAILED )
1478
+ {
1479
+ elog (LOG , "add_files(). cfs_compression_ration failed to map file %s: %m" , file -> path );
1480
+ close (md );
1481
+ break ;
1482
+ }
1483
+
1484
+ (* pre_search_file )-> generation = map -> generation ;
1364
1485
(* pre_search_file )-> is_datafile = false;
1486
+
1487
+ if (cfs_munmap (map ) < 0 )
1488
+ elog (LOG , "add_files(). CFS failed to unmap file %s: %m" , file -> path );
1489
+ if (close (md ) < 0 )
1490
+ elog (LOG , "add_files(). CFS failed to close file %s: %m" , file -> path );
1365
1491
}
1492
+ else
1493
+ elog (ERROR , "corresponding segment '%s' is not found" , tmp_file .path );
1494
+
1366
1495
pg_free (tmp_file .path );
1367
1496
}
1368
1497
}
@@ -1636,3 +1765,34 @@ StreamLog(void *arg)
1636
1765
PQfinish (conn );
1637
1766
conn = NULL ;
1638
1767
}
1768
+
1769
+
1770
+ FileMap * cfs_mmap (int md )
1771
+ {
1772
+ FileMap * map ;
1773
+ #ifdef WIN32
1774
+ HANDLE mh = CreateFileMapping (_get_osfhandle (md ), NULL , PAGE_READWRITE ,
1775
+ 0 , (DWORD )sizeof (FileMap ), NULL );
1776
+ if (mh == NULL )
1777
+ return (FileMap * )MAP_FAILED ;
1778
+
1779
+ map = (FileMap * )MapViewOfFile (mh , FILE_MAP_ALL_ACCESS , 0 , 0 , 0 );
1780
+ CloseHandle (mh );
1781
+ if (map == NULL )
1782
+ return (FileMap * )MAP_FAILED ;
1783
+
1784
+ #else
1785
+ map = (FileMap * )mmap (NULL , sizeof (FileMap ),
1786
+ PROT_WRITE | PROT_READ , MAP_SHARED , md , 0 );
1787
+ #endif
1788
+ return map ;
1789
+ }
1790
+
1791
+ int cfs_munmap (FileMap * map )
1792
+ {
1793
+ #ifdef WIN32
1794
+ return UnmapViewOfFile (map ) ? 0 : -1 ;
1795
+ #else
1796
+ return munmap (map , sizeof (FileMap ));
1797
+ #endif
1798
+ }
0 commit comments