diff options
| author | Jan Sucan <jan@jansucan.com> | 2024-03-31 12:25:35 +0200 |
|---|---|---|
| committer | Ján Sučan <jan@jansucan.com> | 2024-03-31 12:28:24 +0200 |
| commit | 9f0e7e0cb8a3599d7b2be13f34963f7baf9158e7 (patch) | |
| tree | f7fa458aeb803b110d34541a1ea0316360f7ce6e | |
| parent | 2779ab6ad42359338f61df0e2ea29abcaf48d806 (diff) | |
Rewrite the backup and restore code in object-oriented way
| -rw-r--r-- | src/backup.cpp | 200 | ||||
| -rw-r--r-- | src/backup.h | 11 | ||||
| -rw-r--r-- | src/file.cpp | 84 | ||||
| -rw-r--r-- | src/file.h | 39 | ||||
| -rw-r--r-- | src/main.cpp | 12 | ||||
| -rw-r--r-- | src/print.cpp | 47 | ||||
| -rw-r--r-- | src/print.h | 32 | ||||
| -rw-r--r-- | src/restore.cpp | 224 | ||||
| -rw-r--r-- | src/restore.h | 11 |
9 files changed, 239 insertions, 421 deletions
diff --git a/src/backup.cpp b/src/backup.cpp index b0b0054..4ffbd87 100644 --- a/src/backup.cpp +++ b/src/backup.cpp @@ -26,133 +26,142 @@ #include "backup.h" -#include "file.h" -#include "print.h" - -#include <errno.h> +#include <fstream> #include <inttypes.h> #include <stdlib.h> #include <string.h> typedef struct { - FILE *in_file; - FILE *ref_file; - FILE *out_file; + std::ifstream in_file; + std::ifstream ref_file; + std::ofstream out_file; - char *in_buffer; - char *ref_buffer; - char *out_buffer; + std::unique_ptr<char[]> in_buffer; + std::unique_ptr<char[]> ref_buffer; + std::unique_ptr<char[]> out_buffer; size_t out_buffer_size; } resources_backup_t; -static int check_files(const OptionsBackup &opts, - const resources_backup_t *const res); -static int write_out_buffer(const char *const buffer, size_t size, - FILE *const file); +static void check_files(const OptionsBackup &opts); +static void write_out_buffer(const char *const buffer, size_t size, + std::ofstream &file); -static int -resources_allocate_for_backup(const OptionsBackup &opts, - resources_backup_t *const res) +static resources_backup_t +resources_allocate_for_backup(const OptionsBackup &opts) { - if ((res->in_file = fopen(opts.getInFilePath().c_str(), "r")) == NULL) { - print_error("cannot open input file: %s", strerror(errno)); - return 1; + resources_backup_t res; + + res.in_file.open(opts.getInFilePath(), + std::ifstream::in | std::ifstream::binary); + if (!res.in_file) { + throw BackupError("cannot open input file"); } - if ((res->ref_file = fopen(opts.getRefFilePath().c_str(), "r")) == NULL) { - print_error("cannot open reference file: %s", strerror(errno)); - return 1; + res.ref_file.open(opts.getRefFilePath(), + std::ifstream::in | std::ifstream::binary); + if (!res.ref_file) { + throw BackupError("cannot open reference file"); } /* When backing up, the output file is truncated to hold the * new data */ - if ((res->out_file = fopen(opts.getOutFilePath().c_str(), "w+")) == NULL) { - print_error("cannot open output file: %s", strerror(errno)); - return 1; + res.out_file.open(opts.getOutFilePath(), std::ifstream::out | + std::ifstream::trunc | + std::ifstream::binary); + if (!res.out_file) { + throw BackupError("cannot open output file"); } /* The output buffer contains also the offsets */ - res->out_buffer_size = + res.out_buffer_size = ((opts.getBufferSize() / opts.getSectorSize()) * sizeof(uint64_t)) + opts.getBufferSize(); // TODO: separate function - if ((res->in_buffer = (char *)malloc(opts.getBufferSize())) == NULL) { - print_error("cannot allocate buffer for input file data"); - return 1; - } else if ((res->ref_buffer = (char *)malloc(opts.getBufferSize())) == - 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; + try { + res.in_buffer = std::make_unique<char[]>(opts.getBufferSize()); + } catch (const std::bad_alloc &e) { + throw BackupError("cannot allocate buffer for input file data"); + } + + try { + res.ref_buffer = std::make_unique<char[]>(opts.getBufferSize()); + } catch (const std::bad_alloc &e) { + throw BackupError("cannot allocate buffer for reference file data"); } - return 0; + try { + res.out_buffer = std::make_unique<char[]>(res.out_buffer_size); + } catch (const std::bad_alloc &e) { + throw BackupError("cannot allocate buffer for output file data"); + } + + return res; } -static int -check_files(const OptionsBackup &opts, const resources_backup_t *const res) +static void +check_files(const OptionsBackup &opts) { - bool in_size_ok = false; - const size_t in_size = file_size(res->in_file, &in_size_ok); - - if (!in_size_ok) { - print_error("cannot get size of input file: %s", strerror(errno)); - return 1; + size_t in_size{0}; + try { + in_size = std::filesystem::file_size(opts.getInFilePath()); + } catch (const std::exception &e) { + throw BackupError("cannot get size of input file: " + + std::string(e.what())); } - bool ref_size_ok = false; - const size_t ref_size = file_size(res->ref_file, &ref_size_ok); - - if (!ref_size_ok) { - print_error("cannot get size of reference file: %s", strerror(errno)); - return 1; + size_t ref_size{0}; + try { + ref_size = std::filesystem::file_size(opts.getRefFilePath()); + } catch (const std::exception &e) { + throw BackupError("cannot get size of reference file: " + + std::string(e.what())); } /* Check sizes of the input file and the reference file */ if (in_size != ref_size) { - print_error("input file and reference file differ in size"); - return 1; + throw BackupError("input file and reference file differ in size"); } else if ((in_size % opts.getSectorSize()) != 0) { - print_error( - "size of input file and reference file is not multiple of %" PRIu32, - opts.getSectorSize()); - return 1; + throw BackupError( + "size of input file and reference file is not multiple of " + + std::to_string(opts.getSectorSize())); } - - return 0; } -static int -write_out_buffer(const char *const buffer, size_t size, FILE *const file) +static void +write_out_buffer(const char *const buffer, size_t size, std::ofstream &file) { - const size_t bytes_written = fwrite(buffer, 1U, size, file); + file.write(buffer, size); - if (bytes_written != size) { - print_error("cannot write to output file: %s", strerror(errno)); - return 1; + if (!file) { + throw BackupError("cannot write to output file"); } - - return 0; } -int -backup(const OptionsBackup &opts) +static size_t +read_sectors(std::ifstream &file, char *const buffer, uint32_t buffer_size, + uint32_t sector_size) { - resources_backup_t res; - - if (resources_allocate_for_backup(opts, &res) != 0) { - return 1; + file.readsome(buffer, buffer_size); + const size_t bytes_read = file.gcount(); + + if (!file.good() && !file.eof()) { + throw BackupError("cannot read from file"); + } else if ((bytes_read % sector_size) != 0) { + throw BackupError( + "data read from input file is not multiple of sector size"); + } else { + return (bytes_read / sector_size); } +} - if (check_files(opts, &res) != 0) { - return 1; - } +void +backup(const OptionsBackup &opts) +{ + resources_backup_t res{resources_allocate_for_backup(opts)}; + check_files(opts); size_t out_buffer_index = 0; uint64_t input_file_offset = 0; @@ -161,44 +170,42 @@ backup(const OptionsBackup &opts) /* Read the sectors from the input and reference files into the buffers */ const size_t in_sectors_read = - file_read_sectors(res.in_file, res.in_buffer, opts.getBufferSize(), - opts.getSectorSize()); + read_sectors(res.in_file, res.in_buffer.get(), opts.getBufferSize(), + opts.getSectorSize()); const size_t ref_sectors_read = - file_read_sectors(res.ref_file, res.ref_buffer, - opts.getBufferSize(), opts.getSectorSize()); + read_sectors(res.ref_file, res.ref_buffer.get(), + opts.getBufferSize(), opts.getSectorSize()); if ((in_sectors_read == 0) || (ref_sectors_read == 0)) { break; } else if (in_sectors_read != ref_sectors_read) { - print_error( + throw BackupError( "cannot read equal amount of sectors from the input files"); - return 1; } /* Process the sectors in the buffers */ for (size_t sector = 0; sector < in_sectors_read; ++sector) { const size_t buffer_offset = sector * opts.getSectorSize(); - if (memcmp(res.in_buffer + buffer_offset, - res.ref_buffer + buffer_offset, + if (memcmp(res.in_buffer.get() + buffer_offset, + res.ref_buffer.get() + buffer_offset, opts.getSectorSize()) != 0) { /* Backup the changed sector */ 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) { - return 1; - } + write_out_buffer(res.out_buffer.get(), out_buffer_index, + res.out_file); out_buffer_index = 0; } /* Write the next backup record */ const uint64_t o = htole64(input_file_offset); - memcpy(res.out_buffer + out_buffer_index, (void *)&o, + memcpy(res.out_buffer.get() + out_buffer_index, (void *)&o, sizeof(o)); out_buffer_index += sizeof(o); - memcpy(res.out_buffer + out_buffer_index, - res.in_buffer + buffer_offset, opts.getSectorSize()); + memcpy(res.out_buffer.get() + out_buffer_index, + res.in_buffer.get() + buffer_offset, + opts.getSectorSize()); out_buffer_index += opts.getSectorSize(); } @@ -208,11 +215,6 @@ backup(const OptionsBackup &opts) /* Write out the output buffer */ if (out_buffer_index > 0) { - if (write_out_buffer(res.out_buffer, out_buffer_index, res.out_file) != - 0) { - return 1; - } + write_out_buffer(res.out_buffer.get(), out_buffer_index, res.out_file); } - - return 0; } diff --git a/src/backup.h b/src/backup.h index 9629cb7..289ee62 100644 --- a/src/backup.h +++ b/src/backup.h @@ -29,6 +29,15 @@ #include "options.h" -int backup(const OptionsBackup &opts); +class BackupError : public std::runtime_error +{ + public: + explicit BackupError(const std::string &message) + : std::runtime_error(message) + { + } +}; + +void backup(const OptionsBackup &opts); #endif /* BACKUP_H */ diff --git a/src/file.cpp b/src/file.cpp deleted file mode 100644 index d605ceb..0000000 --- a/src/file.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* 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. - */ - -#include "file.h" - -#include "print.h" - -#include <errno.h> -#include <string.h> - -size_t -file_size(FILE *const file, bool *const return_is_ok) -{ - fpos_t p; - - if ((fgetpos(file, &p) != 0) || (fseek(file, 0L, SEEK_END) != 0)) { - *return_is_ok = false; - return 0; - } - - const long size = ftell(file); - - if (fsetpos(file, &p) != 0) { - *return_is_ok = false; - return 0; - } - - *return_is_ok = true; - return (size_t)size; -} - -size_t -file_tell(FILE *const file, bool *const return_is_ok) -{ - const long pos = ftell(file); - - if (pos < 0) { - *return_is_ok = false; - return 0; - } else { - *return_is_ok = true; - return (size_t)pos; - } -} - -size_t -file_read_sectors(FILE *const file, char *const buffer, uint32_t buffer_size, - uint32_t sector_size) -{ - const size_t bytes_read = fread(buffer, 1U, buffer_size, file); - - if (ferror(file)) { - print_error("cannot read from file: %s", strerror(errno)); - return 0; - } else if ((bytes_read % sector_size) != 0) { - print_error("data read from input file is not multiple of sector size"); - return 0; - } else { - return (bytes_read / sector_size); - } -} diff --git a/src/file.h b/src/file.h deleted file mode 100644 index 3e4a6ee..0000000 --- a/src/file.h +++ /dev/null @@ -1,39 +0,0 @@ -/* 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 FILE_H -#define FILE_H - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> - -size_t file_size(FILE *const file, bool *const return_is_ok); -size_t file_tell(FILE *const file, bool *const return_is_ok); -size_t file_read_sectors(FILE *const file, char *const buffer, - uint32_t buffer_size, uint32_t sector_size); - -#endif /* FILE_H */ diff --git a/src/main.cpp b/src/main.cpp index 6b00324..01f3a69 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,9 +37,9 @@ main(int argc, char **argv) if (OptionParser::isHelp(argc, argv)) { OptionParser::printUsage(); } else if (OptionParser::isBackup(argc, argv)) { - return backup(OptionParser::parseBackup(argc, argv)); + backup(OptionParser::parseBackup(argc, argv)); } else if (OptionParser::isRestore(argc, argv)) { - return restore(OptionParser::parseRestore(argc, argv)); + restore(OptionParser::parseRestore(argc, argv)); } else { OptionParser::printUsage(); exit(1); @@ -48,5 +48,13 @@ main(int argc, char **argv) OptionParser::printUsage(); std::cerr << "ERROR: " << e.what() << std::endl; exit(1); + } catch (const BackupError &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + exit(1); + } catch (const RestoreError &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + exit(1); } + + exit(0); } diff --git a/src/print.cpp b/src/print.cpp deleted file mode 100644 index 1dcb75a..0000000 --- a/src/print.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* 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. - */ - -#include "print.h" - -#include <stdarg.h> -#include <stdio.h> - -#define PRINT_ERROR_PREFIX "ERROR: " -#define PRINT_ERROR_SUFFIX "\n" - -void -print_error(const char *const format, ...) -{ - fprintf(stderr, PRINT_ERROR_PREFIX); - - va_list args; - - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - - fprintf(stderr, PRINT_ERROR_SUFFIX); -} diff --git a/src/print.h b/src/print.h deleted file mode 100644 index 8e0cfb5..0000000 --- a/src/print.h +++ /dev/null @@ -1,32 +0,0 @@ -/* 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 PRINT_H -#define PRINT_H - -void print_error(const char *const format, ...); - -#endif /* PRINT_H */ diff --git a/src/restore.cpp b/src/restore.cpp index 02532aa..9091db0 100644 --- a/src/restore.cpp +++ b/src/restore.cpp @@ -26,9 +26,6 @@ #include "restore.h" -#include "file.h" -#include "print.h" - #include <errno.h> #include <stdbool.h> #include <stdint.h> @@ -36,75 +33,83 @@ #include <stdlib.h> #include <string.h> +#include <filesystem> +#include <fstream> +#include <iostream> + typedef struct { - FILE *in_file; - FILE *out_file; + std::ifstream in_file; + std::fstream out_file; - char *in_buffer; - char *out_buffer; + std::unique_ptr<char[]> in_buffer; + std::unique_ptr<char[]> out_buffer; size_t in_sector_size; size_t in_buffer_size; } resources_restore_t; -static int -resources_allocate_for_restore(const OptionsRestore &opts, - resources_restore_t *const res) +static resources_restore_t +resources_allocate_for_restore(const OptionsRestore &opts) { - if ((res->in_file = fopen(opts.getInFilePath().c_str(), "r")) == NULL) { - print_error("cannot open input file: %s", strerror(errno)); - return 1; + resources_restore_t res; + + res.in_file.open(opts.getInFilePath(), std::ios::in | std::ios::binary); + if (!res.in_file) { + throw RestoreError("cannot open input file"); } /* When restoring, the file must be opened for writing and not * truncated */ - if ((res->out_file = fopen(opts.getOutFilePath().c_str(), "r+")) == NULL) { - print_error("cannot open output file: %s", strerror(errno)); - return 1; + res.out_file.open(opts.getOutFilePath(), + std::ios::in | std::ios::out | std::ios::binary); + if (!res.out_file) { + throw RestoreError("cannot open output file"); } /* Allocate the buffer for data from the input file */ /* The input buffer contains also the offsets */ - res->in_sector_size = sizeof(uint64_t) + opts.getSectorSize(); + res.in_sector_size = sizeof(uint64_t) + opts.getSectorSize(); const size_t in_buffer_sector_count = - opts.getBufferSize() / res->in_sector_size; - res->in_buffer_size = in_buffer_sector_count * res->in_sector_size; + opts.getBufferSize() / 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; + try { + res.in_buffer = std::make_unique<char[]>(res.in_buffer_size); + } catch (const std::bad_alloc &e) { + throw RestoreError("cannot allocate buffer for input file data"); } - return 0; + return res; } -static bool -is_input_file_valid(const resources_restore_t *const res, uint32_t sector_size) +static void +check_input_file(resources_restore_t &res, const OptionsRestore &opts) { - bool in_size_ok = false; - const size_t in_size = file_size(res->in_file, &in_size_ok); - - if (!in_size_ok) { - print_error("cannot get size of input file: %s", strerror(errno)); - return false; - } else if (in_size == 0) { - print_error("input file is empty"); - return false; - } else if ((in_size % (sizeof(uint64_t) + sector_size)) != 0) { + size_t in_size{0}; + try { + in_size = std::filesystem::file_size(opts.getInFilePath()); + } catch (const std::exception &e) { + throw RestoreError("cannot get size of input file: " + + std::string(e.what())); + } + + if (in_size == 0) { + throw RestoreError("input file is empty"); + } else if ((in_size % (sizeof(uint64_t) + opts.getSectorSize())) != 0) { /* The input file must hold equally sized sectors and the * offset of each of them */ - print_error("input file has size that cannot contain valid diff data"); - return false; + throw RestoreError( + "input file has size that cannot contain valid diff data"); } - bool out_size_ok = false; - const size_t out_size = file_size(res->out_file, &out_size_ok); - - if (!out_size_ok) { - print_error("cannot get size of output file: %s", strerror(errno)); - return 1; + size_t out_size{0}; + try { + out_size = std::filesystem::file_size(opts.getOutFilePath()); + } catch (const std::exception &e) { + throw RestoreError("cannot get size of output file: " + + std::string(e.what())); } uint64_t prev_out_offset = 0; @@ -114,116 +119,103 @@ is_input_file_valid(const resources_restore_t *const res, uint32_t sector_size) for (;;) { uint64_t out_offset; /* Read the next offset */ - const size_t in_read = - fread(&out_offset, sizeof(out_offset), 1U, res->in_file); - out_offset = le64toh(out_offset); + res.in_file.read(reinterpret_cast<char *>(&out_offset), + sizeof(out_offset)); - if (feof(res->in_file)) { + if (res.in_file.eof() && res.in_file.fail() && !res.in_file.bad()) { break; - } 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"); - return false; - } else if ((out_offset + sector_size) > out_size) { - print_error( + } else if (!res.in_file.good() && !res.in_file.eof()) { + throw RestoreError("cannot read from file"); + } + out_offset = le64toh(out_offset); + + if (!is_first_reading && (out_offset <= prev_out_offset)) { + throw RestoreError( + "a sector offset points behind the previous offset"); + } else if ((out_offset + opts.getSectorSize()) > out_size) { + throw RestoreError( "a sector offset points past the end of the output file"); - return false; - } else if (fseek(res->in_file, sector_size, SEEK_CUR) != 0) { - print_error("cannot seek in input file: %s", strerror(errno)); - return false; + } else if (!res.in_file.seekg(opts.getSectorSize(), + std::ios_base::cur)) { + throw RestoreError("cannot seek in input file"); } is_first_reading = false; prev_out_offset = out_offset; } - bool pos_ok = false; - const size_t pos = file_tell(res->in_file, &pos_ok); - - if (!pos_ok) { - print_error("cannot get position in the input file"); - return false; - } else if (pos != in_size) { - /* The input file must be read completely */ - print_error("input file is not valid"); - return false; - } else if (fseek(res->in_file, 0L, SEEK_SET) != 0) { - /* The file must be prepared for the restoring */ - print_error("cannot seek in input file: %s", strerror(errno)); - return false; + /* The input file must be read completely */ + char c; + res.in_file.read(&c, 1); + if (res.in_file.gcount() != 0) { + throw RestoreError("input file is not valid"); } + res.in_file.clear(); - return true; + /* The file must be prepared for the restoring */ + if (!res.in_file.seekg(0, std::ios_base::beg)) { + throw RestoreError("cannot seek in input file"); + } } -int -restore(const OptionsRestore &opts) +static size_t +read_sectors(std::ifstream &file, char *const buffer, uint32_t buffer_size, + uint32_t sector_size) { - resources_restore_t res; - - if (resources_allocate_for_restore(opts, &res) != 0) { - return 1; - } - - /* Check validity of the input file */ - if (!is_input_file_valid(&res, opts.getSectorSize())) { - return 1; + file.readsome(buffer, buffer_size); + const size_t bytes_read = file.gcount(); + + if (!file.good() && !file.eof()) { + throw RestoreError("cannot read from file"); + } else if ((bytes_read % sector_size) != 0) { + throw RestoreError( + "data read from input file is not multiple of sector size"); + } else { + return (bytes_read / sector_size); } +} - bool in_size_ok = false; - const size_t in_size = file_size(res.in_file, &in_size_ok); +void +restore(const OptionsRestore &opts) +{ + resources_restore_t res{resources_allocate_for_restore(opts)}; - if (!in_size_ok) { - print_error("cannot get size of input file: %s", strerror(errno)); - return 1; - } + check_input_file(res, opts); /* Restore data from the differential image */ for (;;) { - /* Read data of the offset and the next sector */ - const size_t in_sectors_read = file_read_sectors( - res.in_file, res.in_buffer, res.in_buffer_size, res.in_sector_size); + const size_t in_sectors_read = + read_sectors(res.in_file, res.in_buffer.get(), res.in_buffer_size, + res.in_sector_size); if (in_sectors_read == 0) { break; } - char *in_buffer = res.in_buffer; + char *in_buffer = res.in_buffer.get(); 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)); - return 1; + if (!res.out_file.seekp(out_offset, std::ios_base::beg)) { + throw RestoreError("cannot seek in output file"); } - const size_t out_written = - fwrite(in_buffer, opts.getSectorSize(), 1U, res.out_file); - in_buffer += opts.getSectorSize(); - - if (out_written != 1U) { - print_error("cannot write to output file: %s", strerror(errno)); - return 1; + if (!res.out_file.write(in_buffer, opts.getSectorSize())) { + throw RestoreError("cannot write to output file"); } + + in_buffer += opts.getSectorSize(); } } /* The input file must be read completely */ - bool pos_ok = false; - const size_t pos = file_tell(res.in_file, &pos_ok); - - if (!pos_ok) { - print_error("cannot get position in the input file"); - return 1; - } else if (pos != in_size) { - print_error("input file is not valid"); - return 1; + char c; + res.in_file.read(&c, 1); + if (res.in_file.gcount() != 0) { + throw RestoreError("input file is not valid"); } - - return 0; + res.in_file.clear(); } diff --git a/src/restore.h b/src/restore.h index 457ef48..0ea9549 100644 --- a/src/restore.h +++ b/src/restore.h @@ -29,6 +29,15 @@ #include "options.h" -int restore(const OptionsRestore &opts); +class RestoreError : public std::runtime_error +{ + public: + explicit RestoreError(const std::string &message) + : std::runtime_error(message) + { + } +}; + +void restore(const OptionsRestore &opts); #endif /* RESTORE_H */ |
