Skip to content

Commit 87fb4fa

Browse files
committed
Fix bug: get_control_value() should use signed long int; refactor restore_data_file(), so the loop will break in first truncated block
1 parent 4c3a86f commit 87fb4fa

File tree

3 files changed

+90
-48
lines changed

3 files changed

+90
-48
lines changed

src/data.c

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,8 @@ backup_data_file(backup_files_args* arguments,
513513
* Read each page, verify checksum and write it to backup.
514514
* If page map is empty or file is not present in previous backup
515515
* backup all pages of the relation.
516+
*
517+
* We will enter here if backup_mode is FULL or DELTA.
516518
*/
517519
if (file->pagemap.bitmapsize == PageBitmapIsEmpty ||
518520
file->pagemap_isabsent || !file->exists_in_prev)
@@ -525,11 +527,17 @@ backup_data_file(backup_files_args* arguments,
525527
compress_and_backup_page(file, blknum, in, out, &(file->crc),
526528
page_state, curr_page);
527529
n_blocks_read++;
530+
if (page_state == PageIsTruncated)
531+
break;
528532
}
529533
if (backup_mode == BACKUP_MODE_DIFF_DELTA)
530534
file->n_blocks = n_blocks_read;
531535
}
532-
/* If page map is not empty we scan only changed blocks, */
536+
/*
537+
* If page map is not empty we scan only changed blocks.
538+
*
539+
* We will enter here if backup_mode is PAGE or PTRACK.
540+
*/
533541
else
534542
{
535543
datapagemap_iterator_t *iter;
@@ -542,6 +550,8 @@ backup_data_file(backup_files_args* arguments,
542550
compress_and_backup_page(file, blknum, in, out, &(file->crc),
543551
page_state, curr_page);
544552
n_blocks_read++;
553+
if (page_state == PageIsTruncated)
554+
break;
545555
}
546556

547557
pg_free(file->pagemap.bitmap);
@@ -596,8 +606,9 @@ restore_data_file(const char *from_root,
596606
FILE *in = NULL;
597607
FILE *out = NULL;
598608
BackupPageHeader header;
599-
BlockNumber blknum;
600-
size_t file_size;
609+
BlockNumber blknum = 0,
610+
truncate_from = 0;
611+
bool need_truncate = false;
601612

602613
/* BYTES_INVALID allowed only in case of restoring file from DELTA backup */
603614
if (file->write_size != BYTES_INVALID)
@@ -628,7 +639,7 @@ restore_data_file(const char *from_root,
628639
to_path, strerror(errno_tmp));
629640
}
630641

631-
for (blknum = 0; ; blknum++)
642+
while (true)
632643
{
633644
size_t read_len;
634645
DataPage compressed_page; /* used as read buffer */
@@ -638,6 +649,21 @@ restore_data_file(const char *from_root,
638649
if (file->write_size == BYTES_INVALID)
639650
break;
640651

652+
/*
653+
* We need to truncate result file if data file in a incremental backup
654+
* less than data file in a full backup. We know it thanks to n_blocks.
655+
*
656+
* It may be equal to -1, then we don't want to truncate the result
657+
* file.
658+
*/
659+
if (file->n_blocks != BLOCKNUM_INVALID &&
660+
(blknum + 1) > file->n_blocks)
661+
{
662+
truncate_from = blknum;
663+
need_truncate = true;
664+
break;
665+
}
666+
641667
/* read BackupPageHeader */
642668
read_len = fread(&header, 1, sizeof(header), in);
643669
if (read_len != sizeof(header))
@@ -658,17 +684,16 @@ restore_data_file(const char *from_root,
658684
elog(ERROR, "backup is broken at file->path %s block %u",
659685
file->path, blknum);
660686

687+
blknum = header.block;
688+
661689
if (header.compressed_size == PageIsTruncated)
662690
{
663691
/*
664692
* Backup contains information that this block was truncated.
665-
* Truncate file to this length.
693+
* We need to truncate file to this length.
666694
*/
667-
if (ftruncate(fileno(out), header.block * BLCKSZ) != 0)
668-
elog(ERROR, "cannot truncate \"%s\": %s",
669-
file->path, strerror(errno));
670-
elog(VERBOSE, "truncate file %s to block %u",
671-
file->path, header.block);
695+
truncate_from = blknum;
696+
need_truncate = true;
672697
break;
673698
}
674699

@@ -697,7 +722,6 @@ restore_data_file(const char *from_root,
697722
/*
698723
* Seek and write the restored page.
699724
*/
700-
blknum = header.block;
701725
if (fseek(out, blknum * BLCKSZ, SEEK_SET) < 0)
702726
elog(ERROR, "cannot seek block %u of \"%s\": %s",
703727
blknum, to_path, strerror(errno));
@@ -724,25 +748,34 @@ restore_data_file(const char *from_root,
724748
* So when restoring file from DELTA backup we, knowning it`s size at
725749
* a time of a backup, can truncate file to this size.
726750
*/
727-
728-
if (backup->backup_mode == BACKUP_MODE_DIFF_DELTA)
751+
if (backup->backup_mode == BACKUP_MODE_DIFF_DELTA &&
752+
file->n_blocks != BLOCKNUM_INVALID && !need_truncate)
729753
{
754+
size_t file_size = 0;
755+
730756
/* get file current size */
731757
fseek(out, 0, SEEK_END);
732758
file_size = ftell(out);
759+
733760
if (file_size > file->n_blocks * BLCKSZ)
734761
{
735-
/*
736-
* Truncate file to this length.
737-
*/
738-
if (ftruncate(fileno(out), file->n_blocks * BLCKSZ) != 0)
739-
elog(ERROR, "cannot truncate \"%s\": %s",
740-
file->path, strerror(errno));
741-
elog(INFO, "Delta truncate file %s to block %u",
742-
file->path, file->n_blocks);
762+
truncate_from = file->n_blocks;
763+
need_truncate = true;
743764
}
744765
}
745766

767+
if (need_truncate)
768+
{
769+
/*
770+
* Truncate file to this length.
771+
*/
772+
if (ftruncate(fileno(out), truncate_from * BLCKSZ) != 0)
773+
elog(ERROR, "cannot truncate \"%s\": %s",
774+
file->path, strerror(errno));
775+
elog(INFO, "Delta truncate file %s to block %u",
776+
file->path, truncate_from);
777+
}
778+
746779
/* update file permission */
747780
if (chmod(to_path, file->mode) == -1)
748781
{

src/dir.c

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ pgFileInit(const char *path)
186186

187187
file->is_cfs = false;
188188
file->exists_in_prev = false; /* can change only in Incremental backup. */
189-
file->n_blocks = -1; /* can change only in DELTA backup. Number of blocks readed during backup */
189+
/* Number of blocks readed during backup */
190+
file->n_blocks = BLOCKNUM_INVALID;
190191
file->compress_alg = NOT_DEFINED_COMPRESS;
191192
return file;
192193
}
@@ -836,7 +837,7 @@ print_file_list(FILE *out, const parray *files, const char *root)
836837
#endif
837838
fprintf(out, ",\"linked\":\"%s\"", file->linked);
838839

839-
if (file->n_blocks != -1)
840+
if (file->n_blocks != BLOCKNUM_INVALID)
840841
fprintf(out, ",\"n_blocks\":\"%i\"", file->n_blocks);
841842

842843
fprintf(out, "}\n");
@@ -858,23 +859,25 @@ print_file_list(FILE *out, const parray *files, const char *root)
858859
* {"name1":"value1", "name2":"value2"}
859860
*
860861
* The value will be returned to "value_str" as string if it is not NULL. If it
861-
* is NULL the value will be returned to "value_ulong" as unsigned long.
862+
* is NULL the value will be returned to "value_uint64" as int64.
863+
*
864+
* Returns true if the value was found in the line.
862865
*/
863-
static void
866+
static bool
864867
get_control_value(const char *str, const char *name,
865-
char *value_str, uint64 *value_uint64, bool is_mandatory)
868+
char *value_str, int64 *value_int64, bool is_mandatory)
866869
{
867870
int state = CONTROL_WAIT_NAME;
868871
char *name_ptr = (char *) name;
869872
char *buf = (char *) str;
870-
char buf_uint64[32], /* Buffer for "value_uint64" */
871-
*buf_uint64_ptr = buf_uint64;
873+
char buf_int64[32], /* Buffer for "value_int64" */
874+
*buf_int64_ptr = buf_int64;
872875

873876
/* Set default values */
874877
if (value_str)
875878
*value_str = '\0';
876-
else if (value_uint64)
877-
*value_uint64 = 0;
879+
else if (value_int64)
880+
*value_int64 = 0;
878881

879882
while (*buf)
880883
{
@@ -909,7 +912,7 @@ get_control_value(const char *str, const char *name,
909912
if (*buf == '"')
910913
{
911914
state = CONTROL_INVALUE;
912-
buf_uint64_ptr = buf_uint64;
915+
buf_int64_ptr = buf_int64;
913916
}
914917
else if (IsAlpha(*buf))
915918
goto bad_format;
@@ -922,19 +925,19 @@ get_control_value(const char *str, const char *name,
922925
{
923926
*value_str = '\0';
924927
}
925-
else if (value_uint64)
928+
else if (value_int64)
926929
{
927930
/* Length of buf_uint64 should not be greater than 31 */
928-
if (buf_uint64_ptr - buf_uint64 >= 32)
931+
if (buf_int64_ptr - buf_int64 >= 32)
929932
elog(ERROR, "field \"%s\" is out of range in the line %s of the file %s",
930933
name, str, DATABASE_FILE_LIST);
931934

932-
*buf_uint64_ptr = '\0';
933-
if (!parse_uint64(buf_uint64, value_uint64, 0))
935+
*buf_int64_ptr = '\0';
936+
if (!parse_int64(buf_int64, value_int64, 0))
934937
goto bad_format;
935938
}
936939

937-
return;
940+
return true;
938941
}
939942
else
940943
{
@@ -945,8 +948,8 @@ get_control_value(const char *str, const char *name,
945948
}
946949
else
947950
{
948-
*buf_uint64_ptr = *buf;
949-
buf_uint64_ptr++;
951+
*buf_int64_ptr = *buf;
952+
buf_int64_ptr++;
950953
}
951954
}
952955
break;
@@ -970,11 +973,12 @@ get_control_value(const char *str, const char *name,
970973
if (is_mandatory)
971974
elog(ERROR, "field \"%s\" is not found in the line %s of the file %s",
972975
name, str, DATABASE_FILE_LIST);
973-
return;
976+
return false;
974977

975978
bad_format:
976979
elog(ERROR, "%s file has invalid format in line %s",
977980
DATABASE_FILE_LIST, str);
981+
return false; /* Make compiler happy */
978982
}
979983

980984
/*
@@ -1001,7 +1005,7 @@ dir_read_file_list(const char *root, const char *file_txt)
10011005
char filepath[MAXPGPATH];
10021006
char linked[MAXPGPATH];
10031007
char compress_alg_string[MAXPGPATH];
1004-
uint64 write_size,
1008+
int64 write_size,
10051009
mode, /* bit length of mode_t depends on platforms */
10061010
is_datafile,
10071011
is_cfs,
@@ -1016,12 +1020,7 @@ dir_read_file_list(const char *root, const char *file_txt)
10161020
get_control_value(buf, "is_datafile", NULL, &is_datafile, true);
10171021
get_control_value(buf, "is_cfs", NULL, &is_cfs, false);
10181022
get_control_value(buf, "crc", NULL, &crc, true);
1019-
1020-
/* optional fields */
1021-
get_control_value(buf, "linked", linked, NULL, false);
1022-
get_control_value(buf, "segno", NULL, &segno, false);
10231023
get_control_value(buf, "compress_alg", compress_alg_string, NULL, false);
1024-
get_control_value(buf, "n_blocks", NULL, &n_blocks, false);
10251024

10261025
if (root)
10271026
join_path_components(filepath, root, path);
@@ -1036,10 +1035,19 @@ dir_read_file_list(const char *root, const char *file_txt)
10361035
file->is_cfs = is_cfs ? true : false;
10371036
file->crc = (pg_crc32) crc;
10381037
file->compress_alg = parse_compress_alg(compress_alg_string);
1039-
if (linked[0])
1038+
1039+
/*
1040+
* Optional fields
1041+
*/
1042+
1043+
if (get_control_value(buf, "linked", linked, NULL, false) && linked[0])
10401044
file->linked = pgut_strdup(linked);
1041-
file->segno = (int) segno;
1042-
file->n_blocks = (int) n_blocks;
1045+
1046+
if (get_control_value(buf, "segno", NULL, &segno, false))
1047+
file->segno = (int) segno;
1048+
1049+
if (get_control_value(buf, "n_blocks", NULL, &n_blocks, false))
1050+
file->n_blocks = (int) n_blocks;
10431051

10441052
parray_append(files, file);
10451053
}

src/pg_probackup.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ typedef enum ShowFormat
165165
/* special values of pgBackup fields */
166166
#define INVALID_BACKUP_ID 0 /* backup ID is not provided by user */
167167
#define BYTES_INVALID (-1)
168+
#define BLOCKNUM_INVALID (-1)
168169

169170
typedef struct pgBackupConfig
170171
{

0 commit comments

Comments
 (0)