aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md19
-rw-r--r--src/Makefile2
-rw-r--r--src/backup.c41
-rw-r--r--src/backup.h3
-rw-r--r--src/main.c65
-rw-r--r--src/operation_id.h37
-rw-r--r--src/options.c222
-rw-r--r--src/options.h26
-rw-r--r--src/resources.c187
-rw-r--r--src/resources.h31
-rw-r--r--src/restore.c94
-rw-r--r--src/restore.h3
-rw-r--r--tests/001-missing_arguments.sh10
-rw-r--r--tests/001-no_arguments.sh9
-rw-r--r--tests/002-missing_arguments.sh10
-rw-r--r--tests/002-too_many_arguments.sh9
-rw-r--r--tests/003-too_many_arguments.sh10
-rw-r--r--tests/003-unknown_option.sh9
-rw-r--r--tests/004-missing_argument_for_option.sh10
-rw-r--r--tests/004-unknown_option.sh10
-rw-r--r--tests/005-incorrect_buffer_size.sh11
-rw-r--r--tests/005-missing_argument_for_option.sh13
-rw-r--r--tests/006-incorrect_buffer_size.sh15
-rw-r--r--tests/006-incorrect_sector_size.sh11
-rw-r--r--tests/007-incorrect_sector_size.sh15
-rw-r--r--tests/008-help_option.sh11
-rw-r--r--tests/100-cannot_open_files.sh6
-rw-r--r--tests/200-input_and_reference_size_differs.sh2
-rw-r--r--tests/201-input_or_reference_size_is_not_multiple_of_sector_size.sh4
-rw-r--r--tests/300-incorrect_reference_file.sh14
-rw-r--r--tests/400-successful_backup_restore.sh4
-rw-r--r--tests/assert.sh67
32 files changed, 641 insertions, 339 deletions
diff --git a/README.md b/README.md
index 9d6e155..cfb6a4e 100644
--- a/README.md
+++ b/README.md
@@ -9,21 +9,18 @@ e.g. the differential image file is read twice when restoring it.
## Synopsis
-> diff-dd [-s SECTOR_SIZE] [-b BUFFER_SIZE] [INFILE] REFFILE OUTFILE
+> diff-dd help
-## Usage
+> diff-dd backup [-s SECTOR_SIZE] [-b BUFFER_SIZE] INFILE REFFILE OUTFILE
-The utility is used for creating and restoring differential images
-created by it. Meaning of the ```INFILE``` and ```REFFILE``` depends
-on whether backup mode or restore mode is requested. Providing ```INFILE```
-selects the backup mode. Omitting it selects the restore mode.
+> diff-dd restore [-s SECTOR_SIZE] [-b BUFFER_SIZE] INFILE OUTFILE
## Backup
Using ```diff-dd ``` for backup requires the full backup image to
exist. Differential backup is created with:
-> diff-dd INFILE REFFILE OUTFILE
+> diff-dd backup INFILE REFFILE OUTFILE
The ```INFILE``` is a path to the file to backup differentially, the
```REFFILE``` is the full image, and the ```OUTFILE``` is the file to
@@ -33,9 +30,9 @@ which only the changed sectors of the ```INFILE```, compared to the
## Restore
The restoration means application of the changed sectors saved in the
-```REFFILE```, which is the differential image, to the ```OUTFILE```:
+```INFILE```, which is the differential image, to the ```OUTFILE```:
-> diff-dd REFFILE OUTFILE
+> diff-dd restore INFILE OUTFILE
## Options
@@ -55,13 +52,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 /dev/sda1 full.img diff.img
+> diff-dd backup /dev/sda1 full.img 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 diff.img /dev/sda1
+> diff-dd restore diff.img /dev/sda1
The first command restores the old full image. The second one applies
the differences.
diff --git a/src/Makefile b/src/Makefile
index 8b24b35..4deaaec 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,5 @@
CC=gcc
-CFLAGS=-Wall
+CFLAGS=-Wall -fmax-errors=2
SOURCES=*.c
HEADERS=*.h
diff --git a/src/backup.c b/src/backup.c
index 4691bac..4c9dfbd 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -34,15 +34,14 @@
#include <stdlib.h>
#include <string.h>
-static int check_files(const options_t *const opts,
- const resources_t *const res);
-static int allocate_buffers(const options_t *const opts, size_t out_buffer_size,
- resources_t *const res);
+static int check_files(const options_backup_t *const opts,
+ const resources_backup_t *const res);
static int write_out_buffer(const char *const buffer, size_t size,
FILE *const file);
static int
-check_files(const options_t *const opts, const resources_t *const res)
+check_files(const options_backup_t *const opts,
+ const resources_backup_t *const res)
{
const long in_size = file_size(res->in_file);
@@ -73,25 +72,6 @@ check_files(const options_t *const opts, const resources_t *const res)
}
static int
-allocate_buffers(const options_t *const opts, size_t out_buffer_size,
- resources_t *const res)
-{
-
- if ((res->in_buffer = (char *)malloc(opts->buffer_size)) == NULL) {
- print_error("cannot allocate buffer for input file data");
- return 1;
- } else if ((res->ref_buffer = (char *)malloc(opts->buffer_size)) == NULL) {
- print_error("cannot allocate buffer for reference file data");
- return 1;
- } else if ((res->out_buffer = (char *)malloc(out_buffer_size)) == NULL) {
- print_error("cannot allocate buffer for output file data");
- return 1;
- }
-
- return 0;
-}
-
-static int
write_out_buffer(const char *const buffer, size_t size, FILE *const file)
{
const size_t bytes_written = fwrite(buffer, 1U, size, file);
@@ -105,21 +85,12 @@ write_out_buffer(const char *const buffer, size_t size, FILE *const file)
}
int
-backup(const options_t *const opts, resources_t *const res)
+backup(const options_backup_t *const opts, const resources_backup_t *const res)
{
if (check_files(opts, res) != 0) {
return 1;
}
- /* The output buffer contains also the offsets */
- const size_t out_buffer_size =
- ((opts->buffer_size / opts->sector_size) * sizeof(uint64_t)) +
- opts->buffer_size;
-
- if (allocate_buffers(opts, out_buffer_size, res) != 0) {
- return 1;
- }
-
size_t out_buffer_index = 0;
uint64_t input_file_offset = 0;
@@ -148,7 +119,7 @@ backup(const options_t *const opts, resources_t *const res)
res->ref_buffer + buffer_offset,
opts->sector_size) != 0) {
/* Backup the changed sector */
- if (out_buffer_index >= out_buffer_size) {
+ if (out_buffer_index >= res->out_buffer_size) {
/* The output buffer is full. Write it to the output file */
if (write_out_buffer(res->out_buffer, out_buffer_index,
res->out_file) != 0) {
diff --git a/src/backup.h b/src/backup.h
index 057aa15..9f14559 100644
--- a/src/backup.h
+++ b/src/backup.h
@@ -30,6 +30,7 @@
#include "options.h"
#include "resources.h"
-int backup(const options_t *const opts, resources_t *const res);
+int backup(const options_backup_t *const opts,
+ const resources_backup_t *const res);
#endif /* BACKUP_H */
diff --git a/src/main.c b/src/main.c
index 80e6129..35e5c3d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -25,19 +25,11 @@
*/
#include "backup.h"
-#include "file.h"
#include "options.h"
-#include "print.h"
#include "resources.h"
#include "restore.h"
-#include <endian.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
static void
clean_exit(resources_t *const res, int exit_code)
@@ -46,67 +38,26 @@ clean_exit(resources_t *const res, int exit_code)
exit(exit_code);
}
-static int
-open_files(const options_t *const opts, resources_t *const res,
- bool is_action_backup)
-{
- /* Open the input file */
- if ((opts->in_file_path != NULL) &&
- ((res->in_file = fopen(opts->in_file_path, "r")) == NULL)) {
- print_error("cannot open input file: %s", strerror(errno));
- return 1;
- }
-
- /* Open the reference file */
- if ((res->ref_file = fopen(opts->ref_file_path, "r")) == NULL) {
- print_error("cannot open reference file: %s", strerror(errno));
- return 1;
- }
-
- /* Open the output file
- *
- * When restoring, the file must be opened for writing and not
- * truncated
- */
- char out_mode[] = "r+";
-
- if (is_action_backup) {
- /* When backing up, the output file is truncated to hold the
- * new data
- */
- out_mode[0] = 'w';
- out_mode[1] = '\0';
- }
-
- if ((res->out_file = fopen(opts->out_file_path, out_mode)) == NULL) {
- print_error("cannot open output file: %s", strerror(errno));
- return 1;
- }
-
- return 0;
-}
-
int
main(int argc, char **argv)
{
options_t opts;
- if (options_parse(argc, argv, &opts)) {
+ if (!options_parse(argc, argv, &opts)) {
options_usage(1);
- } else if (opts.help) {
+ } else if (options_is_operation(&opts, OPERATION_ID_HELP)) {
options_usage(0);
}
- const bool is_action_backup = (opts.in_file_path != NULL);
resources_t res;
- resources_init(&res);
-
- if (open_files(&opts, &res, is_action_backup) != 0) {
+ if (resources_allocate(&opts, &res)) {
clean_exit(&res, 1);
- } else if (is_action_backup) {
- clean_exit(&res, backup(&opts, &res));
+ } else if (options_is_operation(&opts, OPERATION_ID_BACKUP)) {
+ clean_exit(&res, backup(options_get_for_backup(&opts),
+ resources_get_for_backup(&res)));
} else {
- clean_exit(&res, restore(&opts, &res));
+ clean_exit(&res, restore(options_get_for_restore(&opts),
+ resources_get_for_restore(&res)));
}
}
diff --git a/src/operation_id.h b/src/operation_id.h
new file mode 100644
index 0000000..99536af
--- /dev/null
+++ b/src/operation_id.h
@@ -0,0 +1,37 @@
+/* Copyright 2019 Ján Sučan <jan@jansucan.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPERATION_ID_H
+#define OPERATION_ID_H
+
+typedef enum {
+ OPERATION_ID_UNKNOWN,
+ OPERATION_ID_HELP,
+ OPERATION_ID_BACKUP,
+ OPERATION_ID_RESTORE,
+} operation_id_t;
+
+#endif /* OPERATION_ID_H */
diff --git a/src/options.c b/src/options.c
index 002b36c..e2fcfdb 100644
--- a/src/options.c
+++ b/src/options.c
@@ -38,22 +38,126 @@
*/
#include "program_info.h"
+#define OPTIONS_MAX_OPERATION_NAME_LENGTH 8
#define OPTIONS_DEFAULT_SECTOR_SIZE 512
#define OPTIONS_DEFAULT_BUFFER_SIZE (4 * 1024 * 1024)
-static void options_init(options_t *const opts);
+struct common_options {
+ bool help;
+ uint32_t sector_size;
+ uint32_t buffer_size;
+};
+
static int options_parse_unsigned(const char *const arg, uint32_t *const value);
+static void options_parse_operation(const char *const argv,
+ options_t *const opts);
+static bool options_parse_common(int *const argc, char ***const argv,
+ struct common_options *const opts);
+static bool options_parse_backup(int *const argc, char ***const argv,
+ options_t *const opts);
+static bool options_parse_restore(int *const argc, char ***const argv,
+ options_t *const opts);
-int
+bool
options_parse(int argc, char **argv, options_t *const opts)
{
- options_init(opts);
+ // Skip the executable name
+ --argc;
+ ++argv;
+
+ options_parse_operation(argv[0], opts);
+
+ if (options_is_operation(opts, OPERATION_ID_UNKNOWN)) {
+ return false;
+ }
+
+ if (options_is_operation(opts, OPERATION_ID_BACKUP)) {
+ if (!options_parse_backup(&argc, &argv, opts)) {
+ return false;
+ }
+ } else if (options_is_operation(opts, OPERATION_ID_RESTORE)) {
+ if (!options_parse_restore(&argc, &argv, opts)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+options_usage(int exit_code)
+{
+ printf("Usage: %s [-s SECTOR_SIZE] [-b BUFFER_SIZE] [INFILE] REFFILE "
+ "OUTFILE\n",
+ PROGRAM_NAME_STR);
+ exit(exit_code);
+}
+
+bool
+options_is_operation(const options_t *const opts, operation_id_t operation_id)
+{
+ return (opts->operation_id == operation_id);
+}
+
+const options_backup_t *
+options_get_for_backup(const options_t *const opts)
+{
+ return &(opts->op.backup);
+}
+
+const options_restore_t *
+options_get_for_restore(const options_t *const opts)
+{
+ return &(opts->op.restore);
+}
+
+static int
+options_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;
+}
+
+static void
+options_parse_operation(const char *const op_name, options_t *const opts)
+{
+ if (op_name == NULL) {
+ opts->operation_id = OPERATION_ID_UNKNOWN;
+ } else if (strncmp(op_name, "help", OPTIONS_MAX_OPERATION_NAME_LENGTH) ==
+ 0) {
+ opts->operation_id = OPERATION_ID_HELP;
+ } else if (strncmp(op_name, "backup", OPTIONS_MAX_OPERATION_NAME_LENGTH) ==
+ 0) {
+ opts->operation_id = OPERATION_ID_BACKUP;
+ } else if (strncmp(op_name, "restore", OPTIONS_MAX_OPERATION_NAME_LENGTH) ==
+ 0) {
+ opts->operation_id = OPERATION_ID_RESTORE;
+ } else {
+ opts->operation_id = OPERATION_ID_UNKNOWN;
+ }
+}
+
+static void
+options_init_common(struct common_options *const opts)
+{
+ opts->help = false;
+ opts->sector_size = OPTIONS_DEFAULT_SECTOR_SIZE;
+ opts->buffer_size = OPTIONS_DEFAULT_BUFFER_SIZE;
+}
+static bool
+options_parse_common(int *const argc, char ***const argv,
+ struct common_options *const opts)
+{
int ch;
char *arg_sector_size = NULL;
char *arg_buffer_size = NULL;
- while ((ch = getopt(argc, argv, ":b:hs:")) != -1) {
+ while ((ch = getopt(*argc, *argv, ":b:hs:")) != -1) {
switch (ch) {
case 'b':
arg_buffer_size = optarg;
@@ -69,90 +173,94 @@ options_parse(int argc, char **argv, options_t *const opts)
case ':':
print_error("missing argument for option '-%c'", optopt);
- return 1;
-
+ return false;
default:
print_error("unknown option '-%c'", optopt);
- return 1;
+ return false;
}
}
- argc -= optind;
- argv += optind;
-
- if (opts->help) {
- return 0;
- } else if (argc < 2) {
- print_error("missing arguments");
- return 1;
- } else if (argc > 3) {
- print_error("too many arguments");
- return 1;
- }
+ *argc -= optind;
+ *argv += optind;
/* Convert numbers in the arguments */
if ((arg_sector_size != NULL) &&
options_parse_unsigned(arg_sector_size, &(opts->sector_size))) {
print_error("incorrect sector size");
- return 1;
+ return false;
} else if ((arg_buffer_size != NULL) &&
options_parse_unsigned(arg_buffer_size, &(opts->buffer_size))) {
print_error("incorrect buffer size");
- return 1;
+ return false;
} else if (opts->sector_size == 0) {
print_error("sector size cannot be 0");
- return 1;
+ return false;
} else if (opts->buffer_size == 0) {
print_error("buffer size cannot be 0");
- return 1;
+ return false;
} else if (opts->sector_size > opts->buffer_size) {
print_error("sector size cannot larger than buffer size");
- return 1;
+ return false;
} else if ((opts->buffer_size % opts->sector_size) != 0) {
print_error("buffer size is not multiple of sector size");
- return 1;
+ return false;
}
- /* Pick the file paths */
- int last_path_index = argc - 1;
- opts->out_file_path = argv[last_path_index--];
- opts->ref_file_path = argv[last_path_index--];
- if (last_path_index >= 0) {
- opts->in_file_path = argv[last_path_index];
- }
-
- return 0;
+ return true;
}
-void
-options_usage(int exit_code)
+static bool
+options_parse_backup(int *const argc, char ***const argv, options_t *const opts)
{
- printf("Usage: %s [-s SECTOR_SIZE] [-b BUFFER_SIZE] [INFILE] REFFILE "
- "OUTFILE\n",
- PROGRAM_NAME_STR);
- exit(exit_code);
-}
+ struct common_options common_opts;
+ options_init_common(&common_opts);
+ if (!options_parse_common(argc, argv, &common_opts)) {
+ return false;
+ }
-static void
-options_init(options_t *const opts)
-{
- opts->help = false;
- opts->sector_size = OPTIONS_DEFAULT_SECTOR_SIZE;
- opts->buffer_size = OPTIONS_DEFAULT_BUFFER_SIZE;
+ if (common_opts.help) {
+ opts->operation_id = OPERATION_ID_HELP;
+ } else if (*argc < 3) {
+ print_error("missing arguments");
+ return false;
+ } else if (*argc > 3) {
+ print_error("too many arguments");
+ return false;
+ } else {
+ opts->op.backup.sector_size = common_opts.sector_size;
+ opts->op.backup.buffer_size = common_opts.buffer_size;
+ opts->op.backup.in_file_path = (*argv)[0];
+ opts->op.backup.ref_file_path = (*argv)[1];
+ opts->op.backup.out_file_path = (*argv)[2];
+ }
- opts->in_file_path = NULL;
- opts->ref_file_path = NULL;
- opts->out_file_path = NULL;
+ return true;
}
-static int
-options_parse_unsigned(const char *const arg, uint32_t *const value)
+static bool
+options_parse_restore(int *const argc, char ***const argv,
+ options_t *const opts)
{
- char *end;
-
- errno = 0;
+ struct common_options common_opts;
+ options_init_common(&common_opts);
+ if (!options_parse_common(argc, argv, &common_opts)) {
+ return false;
+ }
- *value = strtoul(arg, &end, 0);
+ if (common_opts.help) {
+ opts->operation_id = OPERATION_ID_HELP;
+ } else if (*argc < 2) {
+ print_error("missing arguments");
+ return false;
+ } else if (*argc > 2) {
+ print_error("too many arguments");
+ return false;
+ } else {
+ opts->op.restore.sector_size = common_opts.sector_size;
+ opts->op.restore.buffer_size = common_opts.buffer_size;
+ opts->op.restore.in_file_path = (*argv)[0];
+ opts->op.restore.out_file_path = (*argv)[1];
+ }
- return ((*end != '\0') || (errno != 0)) ? -1 : 0;
+ return true;
}
diff --git a/src/options.h b/src/options.h
index 9ffa14f..e162fbe 100644
--- a/src/options.h
+++ b/src/options.h
@@ -27,20 +27,40 @@
#ifndef OPTIONS_H
#define OPTIONS_H
+#include "operation_id.h"
+
#include <stdbool.h>
#include <stdint.h>
typedef struct {
- bool help;
uint32_t sector_size;
uint32_t buffer_size;
-
const char *in_file_path;
const char *ref_file_path;
const char *out_file_path;
+} options_backup_t;
+
+typedef struct {
+ uint32_t sector_size;
+ uint32_t buffer_size;
+ const char *in_file_path;
+ const char *out_file_path;
+} options_restore_t;
+
+typedef struct {
+ operation_id_t operation_id;
+
+ union {
+ options_backup_t backup;
+ options_restore_t restore;
+ } op;
} options_t;
-int options_parse(int argc, char **argv, options_t *const opts);
+bool options_parse(int argc, char **argv, options_t *const opts);
void options_usage(int exit_code);
+bool options_is_operation(const options_t *const opts,
+ operation_id_t operation_id);
+const options_backup_t *options_get_for_backup(const options_t *const opts);
+const options_restore_t *options_get_for_restore(const options_t *const opts);
#endif /* OPTIONS_H */
diff --git a/src/resources.c b/src/resources.c
index 65b7f71..0829c7c 100644
--- a/src/resources.c
+++ b/src/resources.c
@@ -26,45 +26,188 @@
#include "resources.h"
+#include "print.h"
+
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
-void
-resources_init(resources_t *const res)
+static void
+resources_init_for_backup(resources_backup_t *const res)
{
res->in_file = NULL;
res->ref_file = NULL;
res->out_file = NULL;
+
res->in_buffer = NULL;
res->ref_buffer = NULL;
res->out_buffer = NULL;
}
-void
-resources_free(resources_t *const res)
+static int
+resources_allocate_for_backup(const options_backup_t *const opts,
+ resources_backup_t *const res)
{
- if (res->in_file != NULL) {
- fclose(res->in_file);
- res->in_file = NULL;
+ if ((res->in_file = fopen(opts->in_file_path, "r")) == NULL) {
+ print_error("cannot open input file: %s", strerror(errno));
+ return 1;
}
- if (res->ref_file != NULL) {
- fclose(res->ref_file);
- res->ref_file = NULL;
+
+ if ((res->ref_file = fopen(opts->ref_file_path, "r")) == NULL) {
+ print_error("cannot open reference file: %s", strerror(errno));
+ return 1;
}
- if (res->out_file != NULL) {
- fclose(res->out_file);
- res->out_file = NULL;
+
+ /* When backing up, the output file is truncated to hold the
+ * new data
+ */
+ if ((res->out_file = fopen(opts->out_file_path, "w+")) == NULL) {
+ print_error("cannot open output file: %s", strerror(errno));
+ return 1;
}
- if (res->in_buffer != NULL) {
- free(res->in_buffer);
- res->in_buffer = NULL;
+
+ /* The output buffer contains also the offsets */
+ res->out_buffer_size =
+ ((opts->buffer_size / opts->sector_size) * sizeof(uint64_t)) +
+ opts->buffer_size;
+
+ // TODO: separate function
+ if ((res->in_buffer = (char *)malloc(opts->buffer_size)) == NULL) {
+ print_error("cannot allocate buffer for input file data");
+ return 1;
+ } else if ((res->ref_buffer = (char *)malloc(opts->buffer_size)) == NULL) {
+ print_error("cannot allocate buffer for reference file data");
+ return 1;
+ } else if ((res->out_buffer = (char *)malloc(res->out_buffer_size)) ==
+ NULL) {
+ print_error("cannot allocate buffer for output file data");
+ return 1;
}
- if (res->ref_buffer != NULL) {
- free(res->ref_buffer);
- res->ref_buffer = NULL;
+
+ return 0;
+}
+
+static void
+resources_init_for_restore(resources_restore_t *const res)
+{
+ res->in_file = NULL;
+ res->out_file = NULL;
+
+ res->in_buffer = NULL;
+ res->out_buffer = NULL;
+}
+
+static int
+resources_allocate_for_restore(const options_restore_t *const opts,
+ resources_restore_t *const res)
+{
+ if ((res->in_file = fopen(opts->in_file_path, "r")) == NULL) {
+ print_error("cannot open input file: %s", strerror(errno));
+ return 1;
}
- if (res->out_buffer != NULL) {
- free(res->out_buffer);
- res->out_buffer = NULL;
+
+ /* When restoring, the file must be opened for writing and not
+ * truncated
+ */
+ if ((res->out_file = fopen(opts->out_file_path, "r+")) == NULL) {
+ print_error("cannot open output file: %s", strerror(errno));
+ return 1;
}
+
+ /* Allocate the buffer for data from the input file */
+ /* The input buffer contains also the offsets */
+ res->in_sector_size = sizeof(uint64_t) + opts->sector_size;
+ const size_t in_buffer_sector_count =
+ opts->buffer_size / res->in_sector_size;
+ res->in_buffer_size = in_buffer_sector_count * res->in_sector_size;
+
+ if ((res->in_buffer = (char *)malloc(res->in_buffer_size)) == NULL) {
+ print_error("cannot allocate buffer for input file data");
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+resources_close_file(FILE **const file)
+{
+ if (*file != NULL) {
+ fclose(*file);
+ *file = NULL;
+ }
+}
+
+static void
+resources_close_buffer(char **const buffer)
+{
+ if (*buffer != NULL) {
+ free(*buffer);
+ *buffer = NULL;
+ }
+}
+
+static void
+resources_free_backup(resources_backup_t *const res)
+{
+ resources_close_file(&(res->in_file));
+ resources_close_file(&(res->ref_file));
+ resources_close_file(&(res->out_file));
+
+ resources_close_buffer(&(res->in_buffer));
+ resources_close_buffer(&(res->ref_buffer));
+ resources_close_buffer(&(res->out_buffer));
+}
+
+static void
+resources_free_restore(resources_restore_t *const res)
+{
+ resources_close_file(&(res->in_file));
+ resources_close_file(&(res->out_file));
+
+ resources_close_buffer(&(res->in_buffer));
+ resources_close_buffer(&(res->out_buffer));
+}
+
+int
+resources_allocate(const options_t *const opts, resources_t *const res)
+{
+ res->operation_id = opts->operation_id;
+
+ if (res->operation_id == OPERATION_ID_BACKUP) {
+ resources_init_for_backup(&(res->res.backup));
+ return resources_allocate_for_backup(&(opts->op.backup),
+ &(res->res.backup));
+ } else if (res->operation_id == OPERATION_ID_RESTORE) {
+ resources_init_for_restore(&(res->res.restore));
+ return resources_allocate_for_restore(&(opts->op.restore),
+ &(res->res.restore));
+ }
+
+ return 0;
+}
+
+const resources_backup_t *
+resources_get_for_backup(const resources_t *const res)
+{
+ return &(res->res.backup);
+}
+
+const resources_restore_t *
+resources_get_for_restore(const resources_t *const res)
+{
+ return &(res->res.restore);
+}
+
+void
+resources_free(resources_t *const res)
+{
+ if (res->operation_id == OPERATION_ID_BACKUP) {
+ resources_free_backup(&(res->res.backup));
+ } else if (res->operation_id == OPERATION_ID_RESTORE) {
+ resources_free_restore(&(res->res.restore));
+ }
+
+ res->operation_id = OPERATION_ID_UNKNOWN;
}
diff --git a/src/resources.h b/src/resources.h
index 3c6e768..3cc0dff 100644
--- a/src/resources.h
+++ b/src/resources.h
@@ -27,6 +27,9 @@
#ifndef RESOURCES_H
#define RESOURCES_H
+#include "operation_id.h"
+#include "options.h"
+
#include <stdio.h>
typedef struct {
@@ -37,9 +40,35 @@ typedef struct {
char *in_buffer;
char *ref_buffer;
char *out_buffer;
+
+ size_t out_buffer_size;
+} resources_backup_t;
+
+typedef struct {
+ FILE *in_file;
+ FILE *out_file;
+
+ char *in_buffer;
+ char *out_buffer;
+
+ size_t in_sector_size;
+ size_t in_buffer_size;
+} resources_restore_t;
+
+typedef struct {
+ operation_id_t operation_id;
+
+ union {
+ resources_backup_t backup;
+ resources_restore_t restore;
+ } res;
} resources_t;
-void resources_init(resources_t *const res);
+int resources_allocate(const options_t *const opts, resources_t *const res);
+const resources_backup_t *
+resources_get_for_backup(const resources_t *const res);
+const resources_restore_t *
+resources_get_for_restore(const resources_t *const res);
void resources_free(resources_t *const res);
#endif /* RESOURCES_H */
diff --git a/src/restore.c b/src/restore.c
index 6318766..df58ab8 100644
--- a/src/restore.c
+++ b/src/restore.c
@@ -37,22 +37,21 @@
#include <string.h>
static bool
-is_reference_file_valid(resources_t *const res, uint32_t sector_size)
+is_input_file_valid(const resources_restore_t *const res, uint32_t sector_size)
{
- const long ref_size = file_size(res->ref_file);
+ const long in_size = file_size(res->in_file);
- if (ref_size < 0) {
- print_error("cannot get size of reference file: %s", strerror(errno));
+ if (in_size < 0) {
+ print_error("cannot get size of input file: %s", strerror(errno));
return false;
- } else if (ref_size == 0) {
- print_error("reference file is empty");
+ } else if (in_size == 0) {
+ print_error("input file is empty");
return false;
- } else if ((ref_size % (sizeof(uint64_t) + sector_size)) != 0) {
- /* The reference file must hold equally sized sectors and the
+ } else if ((in_size % (sizeof(uint64_t) + sector_size)) != 0) {
+ /* The input file must hold equally sized sectors and the
* offset of each of them
*/
- print_error(
- "reference file has size that cannot contain valid diff data");
+ print_error("input file has size that cannot contain valid diff data");
return false;
}
@@ -66,18 +65,18 @@ is_reference_file_valid(resources_t *const res, uint32_t sector_size)
uint64_t prev_out_offset = 0;
bool is_first_reading = true;
- /* Scan the reference file and check */
+ /* Scan the input file and check */
for (;;) {
uint64_t out_offset;
/* Read the next offset */
- const size_t ref_read =
- fread(&out_offset, sizeof(out_offset), 1U, res->ref_file);
+ const size_t in_read =
+ fread(&out_offset, sizeof(out_offset), 1U, res->in_file);
out_offset = le64toh(out_offset);
- if (feof(res->ref_file)) {
+ if (feof(res->in_file)) {
break;
- } else if ((ref_read != 1U) || ferror(res->ref_file)) {
- print_error("cannot read from reference file: %s", strerror(errno));
+ } else if ((in_read != 1U) || ferror(res->in_file)) {
+ print_error("cannot read from input file: %s", strerror(errno));
return false;
} else if (!is_first_reading && (out_offset <= prev_out_offset)) {
print_error("a sector offset points behind the previous offset");
@@ -86,8 +85,8 @@ is_reference_file_valid(resources_t *const res, uint32_t sector_size)
print_error(
"a sector offset points past the end of the output file");
return false;
- } else if (fseek(res->ref_file, sector_size, SEEK_CUR) != 0) {
- print_error("cannot seek in reference file: %s", strerror(errno));
+ } else if (fseek(res->in_file, sector_size, SEEK_CUR) != 0) {
+ print_error("cannot seek in input file: %s", strerror(errno));
return false;
}
@@ -95,13 +94,13 @@ is_reference_file_valid(resources_t *const res, uint32_t sector_size)
prev_out_offset = out_offset;
}
- if (ftell(res->ref_file) != ref_size) {
- /* The reference file must be read completely */
- print_error("reference file is not valid");
+ if (ftell(res->in_file) != in_size) {
+ /* The input file must be read completely */
+ print_error("input file is not valid");
return false;
- } else if (fseek(res->ref_file, 0L, SEEK_SET) != 0) {
+ } else if (fseek(res->in_file, 0L, SEEK_SET) != 0) {
/* The file must be prepared for the restoring */
- print_error("cannot seek in reference file: %s", strerror(errno));
+ print_error("cannot seek in input file: %s", strerror(errno));
return false;
}
@@ -109,28 +108,18 @@ is_reference_file_valid(resources_t *const res, uint32_t sector_size)
}
int
-restore(const options_t *const opts, resources_t *const res)
+restore(const options_restore_t *const opts,
+ const resources_restore_t *const res)
{
- /* Check validity of the reference file */
- if (!is_reference_file_valid(res, opts->sector_size)) {
+ /* Check validity of the input file */
+ if (!is_input_file_valid(res, opts->sector_size)) {
return 1;
}
- const long ref_size = file_size(res->ref_file);
+ const long in_size = file_size(res->in_file);
- if (ref_size < 0) {
- print_error("cannot get size of reference file: %s", strerror(errno));
- return 1;
- }
-
- /* Allocate the buffer for data from the reference file */
- /* The reference buffer contains also the offsets */
- const size_t ref_sector_size = sizeof(uint64_t) + opts->sector_size;
- const size_t ref_buffer_sector_count = opts->buffer_size / ref_sector_size;
- const size_t ref_buffer_size = ref_buffer_sector_count * ref_sector_size;
-
- if ((res->ref_buffer = (char *)malloc(ref_buffer_size)) == NULL) {
- print_error("cannot allocate buffer for reference file data");
+ if (in_size < 0) {
+ print_error("cannot get size of input file: %s", strerror(errno));
return 1;
}
@@ -138,18 +127,19 @@ restore(const options_t *const opts, resources_t *const res)
for (;;) {
/* Read data of the offset and the next sector */
- const size_t ref_sectors_read = file_read_sectors(
- res->ref_file, res->ref_buffer, ref_buffer_size, ref_sector_size);
+ const size_t in_sectors_read =
+ file_read_sectors(res->in_file, res->in_buffer, res->in_buffer_size,
+ res->in_sector_size);
- if (ref_sectors_read == 0) {
+ if (in_sectors_read == 0) {
break;
}
- char *ref_buffer = res->ref_buffer;
+ char *in_buffer = res->in_buffer;
- for (size_t s = 0; s < ref_sectors_read; ++s) {
- const uint64_t out_offset = le64toh(*((uint64_t *)ref_buffer));
- ref_buffer += sizeof(uint64_t);
+ for (size_t s = 0; s < in_sectors_read; ++s) {
+ const uint64_t out_offset = le64toh(*((uint64_t *)in_buffer));
+ in_buffer += sizeof(uint64_t);
if (fseek(res->out_file, out_offset, SEEK_SET) != 0) {
print_error("cannot seek in output file: %s", strerror(errno));
@@ -157,8 +147,8 @@ restore(const options_t *const opts, resources_t *const res)
}
const size_t out_written =
- fwrite(ref_buffer, opts->sector_size, 1U, res->out_file);
- ref_buffer += opts->sector_size;
+ fwrite(in_buffer, opts->sector_size, 1U, res->out_file);
+ in_buffer += opts->sector_size;
if (out_written != 1U) {
print_error("cannot write to output file: %s", strerror(errno));
@@ -167,9 +157,9 @@ restore(const options_t *const opts, resources_t *const res)
}
}
- /* The reference file must be read completely */
- if (ftell(res->ref_file) != ref_size) {
- print_error("reference file is not valid");
+ /* The input file must be read completely */
+ if (ftell(res->in_file) != in_size) {
+ print_error("input file is not valid");
return 1;
}
diff --git a/src/restore.h b/src/restore.h
index 02d70ff..a6c09d1 100644
--- a/src/restore.h
+++ b/src/restore.h
@@ -30,6 +30,7 @@
#include "options.h"
#include "resources.h"
-int restore(const options_t *const opts, resources_t *const res);
+int restore(const options_restore_t *const opts,
+ const resources_restore_t *const res);
#endif /* RESTORE_H */
diff --git a/tests/001-missing_arguments.sh b/tests/001-missing_arguments.sh
deleted file mode 100644
index 8e00bf0..0000000
--- a/tests/001-missing_arguments.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-source ./assert.sh
-
-PROGRAM_EXEC="$1"
-
-assert_error "missing arguments" $PROGRAM_EXEC
-assert_error "missing arguments" $PROGRAM_EXEC arg
-
-exit 0
diff --git a/tests/001-no_arguments.sh b/tests/001-no_arguments.sh
new file mode 100644
index 0000000..150b0df
--- /dev/null
+++ b/tests/001-no_arguments.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+source ./assert.sh
+
+PROGRAM_EXEC="$1"
+
+assert "Usage:" "" 1 $PROGRAM_EXEC
+
+exit 0
diff --git a/tests/002-missing_arguments.sh b/tests/002-missing_arguments.sh
new file mode 100644
index 0000000..4e1d0e3
--- /dev/null
+++ b/tests/002-missing_arguments.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+source ./assert.sh
+
+PROGRAM_EXEC="$1"
+
+assert "Usage" "missing arguments" 1 $PROGRAM_EXEC backup
+assert "Usage" "missing arguments" 1 $PROGRAM_EXEC restore
+
+exit 0
diff --git a/tests/002-too_many_arguments.sh b/tests/002-too_many_arguments.sh
deleted file mode 100644
index b6a1990..0000000
--- a/tests/002-too_many_arguments.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-source ./assert.sh
-
-PROGRAM_EXEC="$1"
-
-assert_error "too many arguments" $PROGRAM_EXEC arg1 arg2 arg3 arg4
-
-exit 0
diff --git a/tests/003-too_many_arguments.sh b/tests/003-too_many_arguments.sh
new file mode 100644
index 0000000..fc51a42
--- /dev/null
+++ b/tests/003-too_many_arguments.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+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
+
+exit 0
diff --git a/tests/003-unknown_option.sh b/tests/003-unknown_option.sh
deleted file mode 100644
index 919f125..0000000
--- a/tests/003-unknown_option.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-source ./assert.sh
-
-PROGRAM_EXEC="$1"
-
-assert_error "unknown option '-x'" $PROGRAM_EXEC -x ref out
-
-exit 0
diff --git a/tests/004-missing_argument_for_option.sh b/tests/004-missing_argument_for_option.sh
deleted file mode 100644
index 071ea07..0000000
--- a/tests/004-missing_argument_for_option.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-source ./assert.sh
-
-PROGRAM_EXEC="$1"
-
-assert_error "missing argument for option '-b'" $PROGRAM_EXEC -b
-assert_error "missing argument for option '-s'" $PROGRAM_EXEC -s
-
-exit 0
diff --git a/tests/004-unknown_option.sh b/tests/004-unknown_option.sh
new file mode 100644
index 0000000..35ba4d6
--- /dev/null
+++ b/tests/004-unknown_option.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+source ./assert.sh
+
+PROGRAM_EXEC="$1"
+
+assert "Usage" "unknown option '-x'" 1 $PROGRAM_EXEC backup -x in ref out
+assert "Usage" "unknown option '-x'" 1 $PROGRAM_EXEC restore -x ref out
+
+exit 0
diff --git a/tests/005-incorrect_buffer_size.sh b/tests/005-incorrect_buffer_size.sh
deleted file mode 100644
index 8c88b54..0000000
--- a/tests/005-incorrect_buffer_size.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-source ./assert.sh
-
-PROGRAM_EXEC="$1"
-
-assert_error "incorrect sector size" $PROGRAM_EXEC -s abc123 ref out
-assert_error "sector size cannot be 0" $PROGRAM_EXEC -s 0 ref out
-assert_error "sector size cannot larger than buffer size" $PROGRAM_EXEC -s 2 -b 1 ref out
-
-exit 0
diff --git a/tests/005-missing_argument_for_option.sh b/tests/005-missing_argument_for_option.sh
new file mode 100644
index 0000000..faccbdd
--- /dev/null
+++ b/tests/005-missing_argument_for_option.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+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 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
new file mode 100644
index 0000000..476a30e
--- /dev/null
+++ b/tests/006-incorrect_buffer_size.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+source ./assert.sh
+
+PROGRAM_EXEC="$1"
+
+assert "Usage" "incorrect sector size" 1 $PROGRAM_EXEC backup -s abc123 in ref out
+assert "Usage" "sector size cannot be 0" 1 $PROGRAM_EXEC backup -s 0 in ref out
+assert "Usage" "sector size cannot larger than buffer size" 1 $PROGRAM_EXEC backup -s 2 -b 1 in ref out
+
+assert "Usage" "incorrect sector size" 1 $PROGRAM_EXEC restore -s abc123 in ref out
+assert "Usage" "sector size cannot be 0" 1 $PROGRAM_EXEC restore -s 0 in ref out
+assert "Usage" "sector size cannot larger than buffer size" 1 $PROGRAM_EXEC restore -s 2 -b 1 in ref out
+
+exit 0
diff --git a/tests/006-incorrect_sector_size.sh b/tests/006-incorrect_sector_size.sh
deleted file mode 100644
index 484c09e..0000000
--- a/tests/006-incorrect_sector_size.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-source ./assert.sh
-
-PROGRAM_EXEC="$1"
-
-assert_error "incorrect buffer size" $PROGRAM_EXEC -b abc123 ref out
-assert_error "buffer size cannot be 0" $PROGRAM_EXEC -b 0 ref out
-assert_error "buffer size is not multiple of sector size" $PROGRAM_EXEC -b 3 -s 2 ref out
-
-exit 0
diff --git a/tests/007-incorrect_sector_size.sh b/tests/007-incorrect_sector_size.sh
new file mode 100644
index 0000000..3a170c4
--- /dev/null
+++ b/tests/007-incorrect_sector_size.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+source ./assert.sh
+
+PROGRAM_EXEC="$1"
+
+assert "Usage" "incorrect buffer size" 1 $PROGRAM_EXEC backup -b abc123 in ref out
+assert "Usage" "buffer size cannot be 0" 1 $PROGRAM_EXEC backup -b 0 in ref out
+assert "Usage" "buffer size is not multiple of sector size" 1 $PROGRAM_EXEC backup -b 3 -s 2 in ref out
+
+assert "Usage" "incorrect buffer size" 1 $PROGRAM_EXEC restore -b abc123 in ref out
+assert "Usage" "buffer size cannot be 0" 1 $PROGRAM_EXEC restore -b 0 in ref out
+assert "Usage" "buffer size is not multiple of sector size" 1 $PROGRAM_EXEC restore -b 3 -s 2 in ref out
+
+exit 0
diff --git a/tests/008-help_option.sh b/tests/008-help_option.sh
new file mode 100644
index 0000000..d874780
--- /dev/null
+++ b/tests/008-help_option.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+source ./assert.sh
+
+PROGRAM_EXEC="$1"
+
+assert "Usage" "" 0 $PROGRAM_EXEC help
+assert "Usage" "" 0 $PROGRAM_EXEC backup -h
+assert "Usage" "" 0 $PROGRAM_EXEC restore -h
+
+exit 0
diff --git a/tests/100-cannot_open_files.sh b/tests/100-cannot_open_files.sh
index 4b49acd..07eb574 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 ref out
touch ref out
-assert_error "cannot open input file" $PROGRAM_EXEC input ref out
+assert "" "cannot open input file" 1 $PROGRAM_EXEC backup input ref out
rm -f input ref out
touch input out
-assert_error "cannot open reference file" $PROGRAM_EXEC input ref out
+assert "" "cannot open reference file" 1 $PROGRAM_EXEC backup input ref out
rm -f input ref out
rmdir outdir 2>/dev/null
touch input ref
mkdir outdir
chmod -w outdir
-assert_error "cannot open output file" $PROGRAM_EXEC input ref outdir/out
+assert "" "cannot open output file" 1 $PROGRAM_EXEC backup input ref outdir/out
rm -f input ref out
rmdir outdir
diff --git a/tests/200-input_and_reference_size_differs.sh b/tests/200-input_and_reference_size_differs.sh
index bf6c49f..50e7ff6 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 ref
dd if=/dev/zero of=input bs=500 count=1 1>/dev/null 2>&1
dd if=/dev/zero of=ref bs=501 count=1 1>/dev/null 2>&1
-assert_error "input file and reference file differ in size" $PROGRAM_EXEC input ref out
+assert "" "input file and reference file differ in size" 1 $PROGRAM_EXEC backup input ref out
rm -f input ref 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 055df09..0cb8207 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
@@ -8,8 +8,8 @@ rm -f input ref
dd if=/dev/zero of=input bs=513 count=1 1>/dev/null 2>&1
dd if=/dev/zero of=ref bs=513 count=1 1>/dev/null 2>&1
-assert_error "size of input file and reference file is not multiple of [0-9]" \
- $PROGRAM_EXEC -s 512 input ref out
+assert "" "size of input file and reference file is not multiple of [0-9]" \
+ 1 $PROGRAM_EXEC backup -s 512 input ref out
rm -f input ref out
diff --git a/tests/300-incorrect_reference_file.sh b/tests/300-incorrect_reference_file.sh
index b3de8c0..5e03c72 100644
--- a/tests/300-incorrect_reference_file.sh
+++ b/tests/300-incorrect_reference_file.sh
@@ -6,11 +6,11 @@ PROGRAM_EXEC="$1"
rm -f ref out
touch ref out
-assert_error "reference file is empty" $PROGRAM_EXEC ref out
+assert "" "input file is empty" 1 $PROGRAM_EXEC restore ref out
dd if=/dev/zero of=ref bs=513 count=1 1>/dev/null 2>&1
-assert_error "reference file has size that cannot contain valid diff data" \
- $PROGRAM_EXEC -s 512 ref out
+assert "" "input file has size that cannot contain valid diff data" \
+ 1 $PROGRAM_EXEC restore -s 512 ref out
rm -f ref out
dd if=/dev/zero of=out bs=512 count=2 1>/dev/null 2>&1
@@ -20,8 +20,8 @@ dd if=/dev/zero of=ref bs=$(( 512 + 8 )) count=2 1>/dev/null 2>&1
printf '\x02' | dd of=ref bs=1 count=1 seek=0 conv=notrunc 1>/dev/null 2>&1
# The second offset will be 1
printf '\x01' | dd of=ref bs=1 count=1 seek=520 conv=notrunc 1>/dev/null 2>&1
-assert_error "a sector offset points behind the previous offset" \
- $PROGRAM_EXEC -s 512 ref out
+assert "" "a sector offset points behind the previous offset" \
+ 1 $PROGRAM_EXEC restore -s 512 ref out
rm -f ref out
dd if=/dev/zero of=out bs=512 count=1 1>/dev/null 2>&1
@@ -29,8 +29,8 @@ dd if=/dev/zero of=out bs=512 count=1 1>/dev/null 2>&1
dd if=/dev/zero of=ref bs=$(( 512 + 8 )) count=2 1>/dev/null 2>&1
# The first offset will be 1
printf '\x01' | dd of=ref bs=1 count=1 seek=0 conv=notrunc 1>/dev/null 2>&1
-assert_error "a sector offset points past the end of the output file" \
- $PROGRAM_EXEC -s 512 ref out
+assert "" "a sector offset points past the end of the output file" \
+ 1 $PROGRAM_EXEC restore -s 512 ref out
rm -f ref out
diff --git a/tests/400-successful_backup_restore.sh b/tests/400-successful_backup_restore.sh
index ce5949a..517a458 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_success $PROGRAM_EXEC -s 512 input ref out
+assert "" "" 0 $PROGRAM_EXEC backup -s 512 input ref 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 ref input; then
exit 1
fi
-assert_success $PROGRAM_EXEC -s 512 out input
+assert "" "" 0 $PROGRAM_EXEC restore -s 512 out input
if ! files_are_the_same input backedup_input; then
echo "assert: Cannot restore the backup"
diff --git a/tests/assert.sh b/tests/assert.sh
index 73b5dd4..cae9924 100644
--- a/tests/assert.sh
+++ b/tests/assert.sh
@@ -1,34 +1,55 @@
-function assert_error()
+test_error=0
+
+function print_assert_out_error()
{
- expected_stderr="$1"
+ echo "assert: $1 does not contain expected string"
+ echo " actual: $2"
+ echo " expected: $3"
+}
- shift 1
- actual_stderr="$("$@" 2>&1 1>/dev/null)"
- retval=$?
- is_stderr_expected="$(echo "$actual_stderr" | grep -i "$expected_stderr")"
+function assert_retval()
+{
+ if [ $1 -ne $2 ]; then
+ echo "assert: Return value is $1, expected $2"
+ test_error=1
+ fi
+}
- if [ $retval -eq 0 ]; then
- echo "assert_error: Return value is $retval, expected != 0"
- exit 1
- elif [ -z "$is_stderr_expected" ]; then
- echo "assert_error: stderr does not contain expected string"
- echo " actual: $actual_stderr"
- echo " expected: $expected_stderr"
- exit 1
+function assert_out()
+{
+ if [ -z "$2" -a -z "$3" ]; then
+ return
+ elif [ -z "$2" -a -n "$3" ]; then
+ print_assert_out_error $1 "$2" "$3"
+ elif [ -n "$2" -a -z "$3" ]; then
+ print_assert_out_error $1 "$2" "$3"
+ else
+ is_stderr_expected="$(echo "$2" | grep -i "$3")"
+ if [ -z "$is_stderr_expected" ]; then
+ print_assert_out_error $1 "$2" "$3"
+ fi
fi
}
-function assert_success()
+function assert()
{
- actual_stderr="$("$@" 2>&1 1>/dev/null)"
- retval=$?
+ expected_stdout="$1"
+ expected_stderr="$2"
+ expected_retval="$3"
+ shift 3
- if [ $retval -ne 0 ]; then
- echo "assert_error: Return value is $retval, expected 0"
- exit 1
- elif [ -n "$actual_stderr" ]; then
- echo "assert_error: stderr is not empty"
- echo " actual: $actual_stderr"
+ "$@" 1>actual_stdout 2>actual_stderr
+ actual_retval=$?
+ actual_stdout="$(cat actual_stdout)"
+ actual_stderr="$(cat actual_stderr)"
+ rm actual_stdout actual_stderr
+
+ test_error=0
+ assert_retval $actual_retval $expected_retval
+ assert_out stdout "$actual_stdout" "$expected_stdout"
+ assert_out stderr "$actual_stderr" "$expected_stderr"
+
+ if [ $test_error -ne 0 ]; then
exit 1
fi
}