diff options
| -rw-r--r-- | README.md | 16 | ||||
| -rw-r--r-- | src/options.cpp | 184 | ||||
| -rw-r--r-- | tests/002-missing_arguments.sh | 4 | ||||
| -rw-r--r-- | tests/003-too_many_arguments.sh | 4 | ||||
| -rw-r--r-- | tests/004-unknown_option.sh | 4 | ||||
| -rw-r--r-- | tests/005-missing_argument_for_option.sh | 8 | ||||
| -rw-r--r-- | tests/006-incorrect_buffer_size.sh | 12 | ||||
| -rw-r--r-- | tests/007-incorrect_sector_size.sh | 12 | ||||
| -rw-r--r-- | tests/100-cannot_open_files.sh | 6 | ||||
| -rw-r--r-- | tests/200-input_and_reference_size_differs.sh | 2 | ||||
| -rw-r--r-- | tests/201-input_or_reference_size_is_not_multiple_of_sector_size.sh | 2 | ||||
| -rw-r--r-- | tests/300-incorrect_reference_file.sh | 8 | ||||
| -rw-r--r-- | tests/400-successful_backup_restore.sh | 4 |
13 files changed, 164 insertions, 102 deletions
@@ -18,16 +18,16 @@ is read twice when restoring it. Because of that, it is slower. > diff-dd help -> diff-dd backup [-s SECTOR_SIZE] [-b BUFFER_SIZE] INFILE BASEFILE OUTFILE +> diff-dd backup [-S SECTOR_SIZE] [-B BUFFER_SIZE] -i INFILE -b BASEFILE -o OUTFILE -> diff-dd restore [-s SECTOR_SIZE] [-b BUFFER_SIZE] DIFFFILE OUTFILE +> diff-dd restore [-S SECTOR_SIZE] [-B BUFFER_SIZE] -d DIFFFILE -o OUTFILE ## Backup Using ```diff-dd ``` for backup requires the full backup image to exist. Differential backup is created with: -> diff-dd backup INFILE BASEFILE OUTFILE +> diff-dd backup -i INFILE -b BASEFILE -o OUTFILE The ```INFILE``` is a path to the file to backup differentially, the ```BASEFILE``` is the full image, and the ```OUTFILE``` is the file to @@ -39,15 +39,15 @@ which only the changed sectors of the ```INFILE```, compared to the The restoration means application of the changed sectors saved in the ```DIFFFILE```, which is the differential image, to the ```OUTFILE```: -> diff-dd restore DIFFFILE OUTFILE +> diff-dd restore -d DIFFFILE -o OUTFILE ## Options -```-s``` sets the sector size by which the files will be processed +```-S``` sets the sector size by which the files will be processed (default is 512 B). It can be used to control granularity of differential backup. -```-b``` sets the size of the buffer for the sectors of the input and +```-B``` sets the size of the buffer for the sectors of the input and output file (default is 4 MiB). The input data is always buffered. The output data are buffered only in backup mode. @@ -59,13 +59,13 @@ First, the full image of the partition to backup has to be created: When the user decides to create the differential image, he or she runs: -> diff-dd backup /dev/sda1 full.img diff.img +> diff-dd backup -i /dev/sda1 -b full.img -o diff.img If a data accident happens, the partition can be restored by running: > dd bs=4M if=full.img of=/dev/sda1 -> diff-dd restore diff.img /dev/sda1 +> diff-dd restore -d diff.img -o /dev/sda1 The first command restores the old full image. The second one applies the differences. diff --git a/src/options.cpp b/src/options.cpp index 280d5c6..66a8c69 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -86,11 +86,12 @@ OptionsRestore::getOutFilePath() const void OptionParser::printUsage() { - std::cout << "Usage: " << PROGRAM_NAME_STR << " backup [-s SECTOR_SIZE]"; - std::cout << " [-b BUFFER_SIZE] INFILE BASEFILE OUTFILE" << std::endl; + std::cout << "Usage: " << PROGRAM_NAME_STR << " backup [-S SECTOR_SIZE]"; + std::cout << " [-B BUFFER_SIZE] -i INFILE -b BASEFILE -o OUTFILE" + << std::endl; - std::cout << " Or: " << PROGRAM_NAME_STR << " restore [-s SECTOR_SIZE]"; - std::cout << "[-b BUFFER_SIZE] DIFFFILE OUTFILE" << std::endl; + std::cout << " Or: " << PROGRAM_NAME_STR << " restore [-S SECTOR_SIZE]"; + std::cout << "[-B BUFFER_SIZE] -d DIFFFILE -o OUTFILE" << std::endl; std::cout << " Or: " << PROGRAM_NAME_STR << " help" << std::endl; } @@ -118,82 +119,118 @@ OptionParser::parseBackup(int argc, char **argv) { OptionsBackup opts; - parse_common(&argc, &argv, opts); + // Skip the executable name. Do not skip the operation name. getopt expects + // to start at an argument immediately preceding the possible options. + argc -= 1; + argv += 1; - if (argc < 3) { - throw OptionError("missing arguments"); - } else if (argc > 3) { - throw OptionError("too many arguments"); - } else { - opts.in_file_path = next_arg(&argv); - opts.base_file_path = next_arg(&argv); - opts.out_file_path = next_arg(&argv); - } + int ch; + const char *arg_sector_size = NULL; + const char *arg_buffer_size = NULL; + const char *arg_input_file = NULL; + const char *arg_base_file = NULL; + const char *arg_output_file = NULL; - return opts; -} + while ((ch = getopt(argc, argv, ":B:S:i:b:o:")) != -1) { + switch (ch) { + case 'B': + arg_buffer_size = optarg; + break; -OptionsRestore -OptionParser::parseRestore(int argc, char **argv) -{ - OptionsRestore opts; + case 'S': + arg_sector_size = optarg; + break; - parse_common(&argc, &argv, opts); + case 'i': + arg_input_file = optarg; + break; - if (argc < 2) { - throw OptionError("missing arguments"); - } else if (argc > 2) { - throw OptionError("too many arguments"); - } else { - opts.diff_file_path = next_arg(&argv); - opts.out_file_path = next_arg(&argv); - } + case 'b': + arg_base_file = optarg; + break; - return opts; -} + case 'o': + arg_output_file = optarg; + break; -bool -OptionParser::isOperation(int argc, char **argv, std::string_view operationName) -{ - return ((argc >= 2) && - (strncmp(argv[1], operationName.data(), - OptionParser::MAX_OPERATION_NAME_LENGTH) == 0)); -} + case ':': + throw OptionError("missing argument for option '-" + + std::string(1, optopt) + "'"); + default: + throw OptionError("unknown option '-" + std::string(1, optopt) + + "'"); + } + } -int -OptionParser::parse_unsigned(const char *const arg, uint32_t *const value) -{ - char *end; + argc -= optind; + argv += optind; - errno = 0; + /* Convert numbers in the arguments */ + if ((arg_sector_size != NULL) && + parse_unsigned(arg_sector_size, &(opts.sector_size))) { + throw OptionError("incorrect sector size"); + } else if ((arg_buffer_size != NULL) && + parse_unsigned(arg_buffer_size, &(opts.buffer_size))) { + throw OptionError("incorrect buffer size"); + } else if (opts.sector_size == 0) { + throw OptionError("sector size cannot be 0"); + } else if (opts.buffer_size == 0) { + throw OptionError("buffer size cannot be 0"); + } else if (opts.sector_size > opts.buffer_size) { + throw OptionError("sector size cannot larger than buffer size"); + } else if ((opts.buffer_size % opts.sector_size) != 0) { + throw OptionError("buffer size is not multiple of sector size"); + } - *value = strtoul(arg, &end, 0); + if (arg_input_file == NULL) { + throw OptionError("missing input file"); + } else if (arg_base_file == NULL) { + throw OptionError("missing base file"); + } else if (arg_output_file == NULL) { + throw OptionError("missing output file"); + } else if (argc != 0) { + throw OptionError("too many arguments"); + } - return ((*end != '\0') || (errno != 0)) ? -1 : 0; + opts.in_file_path = arg_input_file; + opts.base_file_path = arg_base_file; + opts.out_file_path = arg_output_file; + + return opts; } -void -OptionParser::parse_common(int *const argc, char ***const argv, Options &opts) +OptionsRestore +OptionParser::parseRestore(int argc, char **argv) { - // Skip the executable name. Do not skip the operation name. getopt expects - // to start at an argument immediately preceding the possible options. - *argc -= 1; - *argv += 1; + OptionsRestore opts; + + argc -= 1; + argv += 1; int ch; const char *arg_sector_size = NULL; const char *arg_buffer_size = NULL; + const char *arg_diff_file = NULL; + const char *arg_output_file = NULL; - while ((ch = getopt(*argc, *argv, ":b:s:")) != -1) { + while ((ch = getopt(argc, argv, ":B:S:d:o:")) != -1) { switch (ch) { - case 'b': + case 'B': arg_buffer_size = optarg; break; - case 's': + case 'S': arg_sector_size = optarg; break; + case 'd': + arg_diff_file = optarg; + break; + + case 'o': + arg_output_file = optarg; + break; + case ':': throw OptionError("missing argument for option '-" + std::string(1, optopt) + "'"); @@ -203,8 +240,8 @@ OptionParser::parse_common(int *const argc, char ***const argv, Options &opts) } } - *argc -= optind; - *argv += optind; + argc -= optind; + argv += optind; /* Convert numbers in the arguments */ if ((arg_sector_size != NULL) && @@ -222,12 +259,37 @@ OptionParser::parse_common(int *const argc, char ***const argv, Options &opts) } else if ((opts.buffer_size % opts.sector_size) != 0) { throw OptionError("buffer size is not multiple of sector size"); } + + if (arg_diff_file == NULL) { + throw OptionError("missing diff file"); + } else if (arg_output_file == NULL) { + throw OptionError("missing output file"); + } else if (argc != 0) { + throw OptionError("too many arguments"); + } + + opts.diff_file_path = arg_diff_file; + opts.out_file_path = arg_output_file; + + return opts; } -const char * -OptionParser::next_arg(char ***const argv) +bool +OptionParser::isOperation(int argc, char **argv, std::string_view operationName) { - const char *arg = **argv; - ++(*argv); - return arg; + return ((argc >= 2) && + (strncmp(argv[1], operationName.data(), + OptionParser::MAX_OPERATION_NAME_LENGTH) == 0)); +} + +int +OptionParser::parse_unsigned(const char *const arg, uint32_t *const value) +{ + char *end; + + errno = 0; + + *value = strtoul(arg, &end, 0); + + return ((*end != '\0') || (errno != 0)) ? -1 : 0; } diff --git a/tests/002-missing_arguments.sh b/tests/002-missing_arguments.sh index a8cb13c..c3aaa1b 100644 --- a/tests/002-missing_arguments.sh +++ b/tests/002-missing_arguments.sh @@ -4,7 +4,7 @@ source ./assert.sh PROGRAM_EXEC="$1" -assert "Usage" "missing arguments" 1 $PROGRAM_EXEC backup -assert "Usage" "missing arguments" 1 $PROGRAM_EXEC restore +assert "Usage" "missing input file" 1 $PROGRAM_EXEC backup +assert "Usage" "missing diff file" 1 $PROGRAM_EXEC restore exit 0 diff --git a/tests/003-too_many_arguments.sh b/tests/003-too_many_arguments.sh index 3bdc55f..2891afd 100644 --- a/tests/003-too_many_arguments.sh +++ b/tests/003-too_many_arguments.sh @@ -4,7 +4,7 @@ source ./assert.sh PROGRAM_EXEC="$1" -assert "Usage" "too many arguments" 1 $PROGRAM_EXEC backup arg1 arg2 arg3 arg4 -assert "Usage" "too many arguments" 1 $PROGRAM_EXEC restore arg1 arg2 arg3 +assert "Usage" "too many arguments" 1 $PROGRAM_EXEC backup -i arg1 -b arg2 -o arg3 arg4 +assert "Usage" "too many arguments" 1 $PROGRAM_EXEC restore -d arg1 -o arg2 arg3 exit 0 diff --git a/tests/004-unknown_option.sh b/tests/004-unknown_option.sh index 4a03a37..452d904 100644 --- a/tests/004-unknown_option.sh +++ b/tests/004-unknown_option.sh @@ -4,7 +4,7 @@ source ./assert.sh PROGRAM_EXEC="$1" -assert "Usage" "unknown option '-x'" 1 $PROGRAM_EXEC backup -x in base out -assert "Usage" "unknown option '-x'" 1 $PROGRAM_EXEC restore -x diff out +assert "Usage" "unknown option '-x'" 1 $PROGRAM_EXEC backup -x -i in -b base -o out +assert "Usage" "unknown option '-x'" 1 $PROGRAM_EXEC restore -x -d diff -o out exit 0 diff --git a/tests/005-missing_argument_for_option.sh b/tests/005-missing_argument_for_option.sh index aa7a899..b519513 100644 --- a/tests/005-missing_argument_for_option.sh +++ b/tests/005-missing_argument_for_option.sh @@ -4,10 +4,10 @@ source ./assert.sh PROGRAM_EXEC="$1" -assert "Usage" "missing argument for option '-b'" 1 $PROGRAM_EXEC backup -b -assert "Usage" "missing argument for option '-s'" 1 $PROGRAM_EXEC backup -s +assert "Usage" "missing argument for option '-B'" 1 $PROGRAM_EXEC backup -B +assert "Usage" "missing argument for option '-S'" 1 $PROGRAM_EXEC backup -S -assert "Usage" "missing argument for option '-b'" 1 $PROGRAM_EXEC restore -b -assert "Usage" "missing argument for option '-s'" 1 $PROGRAM_EXEC restore -s +assert "Usage" "missing argument for option '-B'" 1 $PROGRAM_EXEC restore -B +assert "Usage" "missing argument for option '-S'" 1 $PROGRAM_EXEC restore -S exit 0 diff --git a/tests/006-incorrect_buffer_size.sh b/tests/006-incorrect_buffer_size.sh index 5e59952..1dfbc24 100644 --- a/tests/006-incorrect_buffer_size.sh +++ b/tests/006-incorrect_buffer_size.sh @@ -4,12 +4,12 @@ source ./assert.sh PROGRAM_EXEC="$1" -assert "Usage" "incorrect sector size" 1 $PROGRAM_EXEC backup -s abc123 in base out -assert "Usage" "sector size cannot be 0" 1 $PROGRAM_EXEC backup -s 0 in base out -assert "Usage" "sector size cannot larger than buffer size" 1 $PROGRAM_EXEC backup -s 2 -b 1 in base out +assert "Usage" "incorrect sector size" 1 $PROGRAM_EXEC backup -S abc123 -i in -b base -o out +assert "Usage" "sector size cannot be 0" 1 $PROGRAM_EXEC backup -S 0 -i in -b base -o out +assert "Usage" "sector size cannot larger than buffer size" 1 $PROGRAM_EXEC backup -S 2 -B 1 -i in -b base -o out -assert "Usage" "incorrect sector size" 1 $PROGRAM_EXEC restore -s abc123 diff out -assert "Usage" "sector size cannot be 0" 1 $PROGRAM_EXEC restore -s 0 diff out -assert "Usage" "sector size cannot larger than buffer size" 1 $PROGRAM_EXEC restore -s 2 -b 1 diff out +assert "Usage" "incorrect sector size" 1 $PROGRAM_EXEC restore -S abc123 -d diff -o out +assert "Usage" "sector size cannot be 0" 1 $PROGRAM_EXEC restore -S 0 -d diff -o out +assert "Usage" "sector size cannot larger than buffer size" 1 $PROGRAM_EXEC restore -S 2 -B 1 -d diff -o out exit 0 diff --git a/tests/007-incorrect_sector_size.sh b/tests/007-incorrect_sector_size.sh index a8dc956..5d8a689 100644 --- a/tests/007-incorrect_sector_size.sh +++ b/tests/007-incorrect_sector_size.sh @@ -4,12 +4,12 @@ source ./assert.sh PROGRAM_EXEC="$1" -assert "Usage" "incorrect buffer size" 1 $PROGRAM_EXEC backup -b abc123 in base out -assert "Usage" "buffer size cannot be 0" 1 $PROGRAM_EXEC backup -b 0 in base out -assert "Usage" "buffer size is not multiple of sector size" 1 $PROGRAM_EXEC backup -b 3 -s 2 in base out +assert "Usage" "incorrect buffer size" 1 $PROGRAM_EXEC backup -B abc123 -i in -b base -o out +assert "Usage" "buffer size cannot be 0" 1 $PROGRAM_EXEC backup -B 0 -i in -b base -o out +assert "Usage" "buffer size is not multiple of sector size" 1 $PROGRAM_EXEC backup -B 3 -S 2 -i in -b base -o out -assert "Usage" "incorrect buffer size" 1 $PROGRAM_EXEC restore -b abc123 diff out -assert "Usage" "buffer size cannot be 0" 1 $PROGRAM_EXEC restore -b 0 diff out -assert "Usage" "buffer size is not multiple of sector size" 1 $PROGRAM_EXEC restore -b 3 -s 2 diff out +assert "Usage" "incorrect buffer size" 1 $PROGRAM_EXEC restore -B abc123 -d diff -o out +assert "Usage" "buffer size cannot be 0" 1 $PROGRAM_EXEC restore -B 0 -d diff -o out +assert "Usage" "buffer size is not multiple of sector size" 1 $PROGRAM_EXEC restore -B 3 -S 2 -d diff -o out exit 0 diff --git a/tests/100-cannot_open_files.sh b/tests/100-cannot_open_files.sh index 64d2a87..34cf3e0 100644 --- a/tests/100-cannot_open_files.sh +++ b/tests/100-cannot_open_files.sh @@ -6,18 +6,18 @@ PROGRAM_EXEC="$1" rm -f input base out touch base out -assert "" "cannot get size of input file" 1 $PROGRAM_EXEC backup input base out +assert "" "cannot get size of input file" 1 $PROGRAM_EXEC backup -i input -b base -o out rm -f input base out touch input out -assert "" "cannot get size of base file" 1 $PROGRAM_EXEC backup input base out +assert "" "cannot get size of base file" 1 $PROGRAM_EXEC backup -i input -b base -o out rm -f input base out rmdir outdir 2>/dev/null touch input base mkdir outdir chmod -w outdir -assert "" "cannot open output file" 1 $PROGRAM_EXEC backup input base outdir/out +assert "" "cannot open output file" 1 $PROGRAM_EXEC backup -i input -b base -o outdir/out rm -f input base out rmdir outdir diff --git a/tests/200-input_and_reference_size_differs.sh b/tests/200-input_and_reference_size_differs.sh index ca0f368..dceb63b 100644 --- a/tests/200-input_and_reference_size_differs.sh +++ b/tests/200-input_and_reference_size_differs.sh @@ -8,7 +8,7 @@ rm -f input base dd if=/dev/zero of=input bs=500 count=1 1>/dev/null 2>&1 dd if=/dev/zero of=base bs=501 count=1 1>/dev/null 2>&1 -assert "" "input file and base file differ in size" 1 $PROGRAM_EXEC backup input base out +assert "" "input file and base file differ in size" 1 $PROGRAM_EXEC backup -i input -b base -o out rm -f input base out diff --git a/tests/201-input_or_reference_size_is_not_multiple_of_sector_size.sh b/tests/201-input_or_reference_size_is_not_multiple_of_sector_size.sh index abfb3c5..d7c6aa6 100644 --- a/tests/201-input_or_reference_size_is_not_multiple_of_sector_size.sh +++ b/tests/201-input_or_reference_size_is_not_multiple_of_sector_size.sh @@ -9,7 +9,7 @@ dd if=/dev/zero of=input bs=513 count=1 1>/dev/null 2>&1 dd if=/dev/zero of=base bs=513 count=1 1>/dev/null 2>&1 assert "" "size of input file and base file is not multiple of [0-9]" \ - 1 $PROGRAM_EXEC backup -s 512 input base out + 1 $PROGRAM_EXEC backup -S 512 -i input -b base -o out rm -f input base out diff --git a/tests/300-incorrect_reference_file.sh b/tests/300-incorrect_reference_file.sh index b15ee12..c250541 100644 --- a/tests/300-incorrect_reference_file.sh +++ b/tests/300-incorrect_reference_file.sh @@ -6,11 +6,11 @@ PROGRAM_EXEC="$1" rm -f diff out touch diff out -assert "" "diff file is empty" 1 $PROGRAM_EXEC restore diff out +assert "" "diff file is empty" 1 $PROGRAM_EXEC restore -d diff -o out dd if=/dev/zero of=diff bs=513 count=1 1>/dev/null 2>&1 assert "" "diff file has size that cannot contain valid diff data" \ - 1 $PROGRAM_EXEC restore -s 512 diff out + 1 $PROGRAM_EXEC restore -S 512 -d diff -o out rm -f diff out dd if=/dev/zero of=out bs=512 count=2 1>/dev/null 2>&1 @@ -21,7 +21,7 @@ printf '\x02' | dd of=diff bs=1 count=1 seek=0 conv=notrunc 1>/dev/null 2>&1 # The second offset will be 1 printf '\x01' | dd of=diff bs=1 count=1 seek=520 conv=notrunc 1>/dev/null 2>&1 assert "" "a sector offset points behind the previous offset" \ - 1 $PROGRAM_EXEC restore -s 512 diff out + 1 $PROGRAM_EXEC restore -S 512 -d diff -o out rm -f diff out dd if=/dev/zero of=out bs=512 count=1 1>/dev/null 2>&1 @@ -30,7 +30,7 @@ dd if=/dev/zero of=diff bs=$(( 512 + 8 )) count=2 1>/dev/null 2>&1 # The first offset will be 1 printf '\x01' | dd of=diff bs=1 count=1 seek=0 conv=notrunc 1>/dev/null 2>&1 assert "" "a sector offset points past the end of the output file" \ - 1 $PROGRAM_EXEC restore -s 512 diff out + 1 $PROGRAM_EXEC restore -S 512 -d diff -o out rm -f diff out diff --git a/tests/400-successful_backup_restore.sh b/tests/400-successful_backup_restore.sh index 8274964..fb44dda 100644 --- a/tests/400-successful_backup_restore.sh +++ b/tests/400-successful_backup_restore.sh @@ -29,7 +29,7 @@ printf '\xFF' | dd of=input bs=1 count=1 seek=$(( (512 * 3) - 1 )) conv=notrunc # The fourth sector will have the middle byte changed printf '\xFF' | dd of=input bs=1 count=1 seek=$(( (512 * 4) - (512 / 2) )) conv=notrunc 1>/dev/null 2>&1 -assert "" "" 0 $PROGRAM_EXEC backup -s 512 input base out +assert "" "" 0 $PROGRAM_EXEC backup -S 512 -i input -b base -o out if ! files_are_the_same out 400-expected_backup_output.bin; then echo "assert: Backup output file differs from the expected one" @@ -44,7 +44,7 @@ if ! files_are_the_same base input; then exit 1 fi -assert "" "" 0 $PROGRAM_EXEC restore -s 512 out input +assert "" "" 0 $PROGRAM_EXEC restore -S 512 -d out -o input if ! files_are_the_same input backedup_input; then echo "assert: Cannot restore the backup" |
