@@ -1109,6 +1109,27 @@ backup_cleanup(bool fatal, void *userdata)
11091109}
11101110}
11111111
1112+ /*
1113+ * Count bytes in file
1114+ */
1115+ static long
1116+ file_size (const char * file )
1117+ {
1118+ long r ;
1119+ FILE * f = fopen (file ,"r" );
1120+
1121+ if (!f )
1122+ {
1123+ fprintf (stderr ,_ ("%s: could not open file \"%s\" for reading: %s\n" ),
1124+ progname ,file ,strerror (errno ));
1125+ return -1 ;
1126+ }
1127+ fseek (f ,0 ,SEEK_END );
1128+ r = ftell (f );
1129+ fclose (f );
1130+ return r ;
1131+ }
1132+
11121133/*
11131134 * Take differential backup at page level.
11141135 */
@@ -1117,7 +1138,6 @@ backup_files(void *arg)
11171138{
11181139int i ;
11191140struct timeval tv ;
1120-
11211141backup_files_args * arguments = (backup_files_args * )arg ;
11221142
11231143gettimeofday (& tv ,NULL );
@@ -1221,39 +1241,56 @@ backup_files(void *arg)
12211241}
12221242else
12231243{
1224- /* Check if the file is a cfs relation's segment */
1244+ /* Checkfirst if the file is a cfs relation's segment */
12251245bool is_cfs_relation_segment = false;
12261246pgFile tmp_file ;
12271247pgFile * * pre_search_file ;
1248+ pgFile * prev_file = NULL ;
1249+
12281250tmp_file .path = psprintf ("%s.cfm" ,file -> path );
12291251pre_search_file = (pgFile * * )parray_bsearch (arguments -> files ,& tmp_file ,pgFileComparePath );
12301252if (pre_search_file != NULL )
12311253{
1232- is_cfs_relation_segment = true;
1233- /* TODO If we don't have ptrack simply copy the file */
1234- if (file -> pagemap .bitmapsize == 0 )
1254+ /* Now check if it's generation has changed since last backup */
1255+ if (arguments -> prev_files )
12351256{
1236- is_cfs_relation_segment = false;
1237- elog (NOTICE ,"1 file '%s' is a cfs relation's segment, bitmapsize == 0 \n" ,file -> path );
1257+ pgFile * * p = (pgFile * * )parray_bsearch (arguments -> prev_files ,file ,pgFileComparePath );
1258+ if (p )
1259+ prev_file = * p ;
1260+ elog (NOTICE ,"file '%s' is a cfs relation's segment generation prev %d, now %d" ,
1261+ file -> path ,prev_file -> generation ,file -> generation );
1262+
1263+ if (prev_file && prev_file -> generation == file -> generation )
1264+ {
1265+ elog (NOTICE ,"prev->write_size %lu, file_size %lu" ,prev_file -> write_size ,file_size (file -> path ));
1266+ if (prev_file -> write_size == file_size (file -> path ))
1267+ {
1268+ elog (NOTICE ,"File hasn't changed since last backup. Don't copy at all" );
1269+ is_cfs_relation_segment = true;
1270+ }
1271+ else
1272+ {
1273+ elog (NOTICE ,"Backup part of the file. %s" ,file -> path );
1274+ is_cfs_relation_segment = true;
1275+ }
1276+ }
12381277}
12391278}
12401279pg_free (tmp_file .path );
12411280
12421281
12431282if (is_cfs_relation_segment )
12441283{
1245- /*
1246- * TODO backup cfs segment
1247- * see backup_data_file()
1248- */
1249- elog (NOTICE ,"2 file '%s' is a cfs relation's segment \n" ,file -> path );
1250- elog (NOTICE ,"2 file->pagemap.bitmapsize = %d" ,file -> pagemap .bitmapsize );
1251- datapagemap_iterator_t * iter ;
1252- BlockNumber blknum = 0 ;
1253-
1254- iter = datapagemap_iterate (& file -> pagemap );
1255- while (datapagemap_next (iter ,& blknum ))
1256- elog (NOTICE ,"2 blknum %u" ,blknum );
1284+ /* backup cfs segment partly */
1285+ if (!copy_file_partly (arguments -> from_root ,
1286+ arguments -> to_root ,
1287+ file ,prev_file -> write_size ))
1288+ {
1289+ /* record as skipped file in file_xxx.txt */
1290+ file -> write_size = BYTES_INVALID ;
1291+ elog (LOG ,"skip" );
1292+ continue ;
1293+ }
12571294}
12581295else if (!copy_file (arguments -> from_root ,
12591296arguments -> to_root ,
@@ -1338,16 +1375,32 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
13381375sprintf (tmp_file .path + path_len - 7 ,".%d" ,segno );
13391376else
13401377tmp_file .path [path_len - 7 ]= '\0' ;
1378+
13411379pre_search_file = (pgFile * * )parray_bsearch (list_file ,& tmp_file ,pgFileComparePath );
1342- if (pre_search_file != NULL )
1380+ /* Use another scheme for compressed files */
1381+ pgFile map_file ;
1382+ pgFile * * map_search_file ;
1383+ map_file .path = pg_strdup (file -> path );
1384+ if (segno > 0 )
1385+ sprintf (map_file .path + path_len - 7 ,".%d.cfm" ,segno );
1386+ else
1387+ sprintf (map_file .path + path_len - 7 ,".cfm" );
1388+ map_search_file = (pgFile * * )parray_bsearch (list_file ,& map_file ,pgFileComparePath );
1389+
1390+ if (pre_search_file != NULL
1391+ && map_search_file == NULL )
13431392{
13441393search_file = * pre_search_file ;
13451394search_file -> ptrack_path = pg_strdup (file -> path );
13461395search_file -> segno = segno ;
1347- }else {
1396+ }
1397+ else
1398+ {
13481399pg_free (tmp_file .path );
1400+ pg_free (map_file .path );
13491401break ;
13501402}
1403+ pg_free (map_file .path );
13511404pg_free (tmp_file .path );
13521405segno ++ ;
13531406}
@@ -1361,12 +1414,9 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
13611414/* compress map file it is not data file */
13621415if (path_len > 4 && strncmp (file -> path + (path_len - 4 ),".cfm" ,4 )== 0 )
13631416{
1364- if (current .backup_mode == BACKUP_MODE_DIFF_PTRACK ||
1365- current .backup_mode == BACKUP_MODE_DIFF_PAGE )
1366- {
1367- elog (NOTICE ,"You can't use incremental backup with compress tablespace" );
1368- /* TODO Add here incremental backup for compressed tablespaces */
1369- }
1417+ if (current .backup_mode == BACKUP_MODE_DIFF_PAGE )
1418+ elog (ERROR ,"You can't use PAGE mode backup with compressed tablespace.\n"
1419+ "Try PTRACK mode instead." );
13701420continue ;
13711421}
13721422
@@ -1420,8 +1470,34 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
14201470pre_search_file = (pgFile * * )parray_bsearch (list_file ,& tmp_file ,pgFileComparePath );
14211471if (pre_search_file != NULL )
14221472{
1473+ FileMap * map ;
1474+ int md = open (file -> path ,O_RDWR |PG_BINARY ,0 );
1475+ if (md < 0 )
1476+ elog (ERROR ,"add_files(). cannot open cfm file '%s'" ,file -> path );
1477+
1478+ map = cfs_mmap (md );
1479+ if (map == MAP_FAILED )
1480+ {
1481+ elog (LOG ,"add_files(). cfs_compression_ration failed to map file %s: %m" ,file -> path );
1482+ close (md );
1483+ break ;
1484+ }
1485+
1486+ (* pre_search_file )-> last_backup_write_size = map -> last_backup_write_size ;
1487+ (* pre_search_file )-> generation = map -> generation ;
14231488(* pre_search_file )-> is_datafile = false;
1489+ map -> last_backup_write_size = map -> physSize ;
1490+
1491+ if (cfs_msync (map )< 0 )
1492+ elog (LOG ,"add_files(). CFS failed to sync map %s: %m" ,file -> path );
1493+ if (cfs_munmap (map )< 0 )
1494+ elog (LOG ,"add_files(). CFS failed to unmap file %s: %m" ,file -> path );
1495+ if (close (md )< 0 )
1496+ elog (LOG ,"add_files(). CFS failed to close file %s: %m" ,file -> path );
14241497}
1498+ else
1499+ elog (ERROR ,"corresponding segment '%s' is not found" ,tmp_file .path );
1500+
14251501pg_free (tmp_file .path );
14261502}
14271503}
@@ -1693,3 +1769,42 @@ StreamLog(void *arg)
16931769PQfinish (conn );
16941770conn = NULL ;
16951771}
1772+
1773+
1774+ FileMap * cfs_mmap (int md )
1775+ {
1776+ FileMap * map ;
1777+ #ifdef WIN32
1778+ HANDLE mh = CreateFileMapping (_get_osfhandle (md ),NULL ,PAGE_READWRITE ,
1779+ 0 , (DWORD )sizeof (FileMap ),NULL );
1780+ if (mh == NULL ) {
1781+ return (FileMap * )MAP_FAILED ;
1782+ }
1783+ map = (FileMap * )MapViewOfFile (mh ,FILE_MAP_ALL_ACCESS ,0 ,0 ,0 );
1784+ CloseHandle (mh );
1785+ if (map == NULL ) {
1786+ return (FileMap * )MAP_FAILED ;
1787+ }
1788+ #else
1789+ map = (FileMap * )mmap (NULL ,sizeof (FileMap ),PROT_WRITE |PROT_READ ,MAP_SHARED ,md ,0 );
1790+ #endif
1791+ return map ;
1792+ }
1793+
1794+ int cfs_munmap (FileMap * map )
1795+ {
1796+ #ifdef WIN32
1797+ return UnmapViewOfFile (map ) ?0 :-1 ;
1798+ #else
1799+ return munmap (map ,sizeof (FileMap ));
1800+ #endif
1801+ }
1802+
1803+ int cfs_msync (FileMap * map )
1804+ {
1805+ #ifdef WIN32
1806+ return FlushViewOfFile (map ,sizeof (FileMap )) ?0 :-1 ;
1807+ #else
1808+ return msync (map ,sizeof (FileMap ),MS_SYNC );
1809+ #endif
1810+ }