diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8f86168..fc2b831 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,7 @@ set(sources main.cc checks.cc sqlite.cc + checks/metadata_integrity.cc checks/metadata_schema_version.cc checks/orphaned_metadata.cc checks/orphaned_objects.cc diff --git a/src/checks.cc b/src/checks.cc index ff54251..05496be 100644 --- a/src/checks.cc +++ b/src/checks.cc @@ -22,6 +22,7 @@ #include #include +#include "checks/metadata_integrity.h" #include "checks/metadata_schema_version.h" #include "checks/object_integrity.h" #include "checks/orphaned_metadata.h" @@ -43,6 +44,7 @@ bool run_checks(const std::filesystem::path& path, bool should_fix) { bool all_checks_passed = true; std::vector> checks; + checks.emplace_back(std::make_shared(path)); checks.emplace_back(std::make_shared(path)); checks.emplace_back(std::make_shared(path)); checks.emplace_back(std::make_shared(path)); diff --git a/src/checks/metadata_integrity.cc b/src/checks/metadata_integrity.cc new file mode 100644 index 0000000..b0c4103 --- /dev/null +++ b/src/checks/metadata_integrity.cc @@ -0,0 +1,84 @@ +/* + * Copyright 2023 SUSE, LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "metadata_integrity.h" + +#include +#include +#include +#include +#include + +MetadataIntegrityFix::MetadataIntegrityFix( + const std::filesystem::path& path, const std::vector& _errors +) + : Fix(path), errors(_errors) {} + +void MetadataIntegrityFix::fix() {} + +std::string MetadataIntegrityFix::to_string() const { + std::string msg("Database integrity check failed:"); + for (auto& s : errors) { + msg += "\n- " + s; + } + return msg; +} + +MetadataIntegrityCheck::MetadataIntegrityCheck(const std::filesystem::path& path +) + : Check(FATAL, path) { + metadata = std::make_unique(root_path / "s3gw.db"); +} + +MetadataIntegrityCheck::~MetadataIntegrityCheck() {} + +bool MetadataIntegrityCheck::check() { + std::vector errors; + auto callback = [](void* arg, int num_columns, char** column_data, char**) { + assert(num_columns == 1); + // If this returns anything other than "ok", we'll end up with + // information added to the errors vector, so will know something + // is broken. These are more fine grained than a completely trashed + // database, for example: + // - row 21 missing from index vobjs_object_id_idx + // - non-unique entry in index versioned_object_objid_vid_unique + if (std::strcmp(column_data[0], "ok") != 0) { + static_cast*>(arg)->emplace_back(column_data[0]); + } + return 0; + }; + + // This will return up to 100 error rows by default. For more details see + // https://www.sqlite.org/pragma.html#pragma_integrity_check + int rc = sqlite3_exec( + metadata->handle, "PRAGMA integrity_check", callback, + static_cast(&errors), NULL + ); + + if (rc != SQLITE_OK) { + // This will happen if the file is _so_ trashed it doesn't even + // look like an SQLite database. Here you'll see things like: + // - file is not a database + // - database disk image is malformed + errors.emplace_back(sqlite3_errstr(rc)); + } + if (!errors.empty()) { + fixes.emplace_back(std::make_shared(root_path, errors) + ); + return false; + } + return true; +} diff --git a/src/checks/metadata_integrity.h b/src/checks/metadata_integrity.h new file mode 100644 index 0000000..40e86ac --- /dev/null +++ b/src/checks/metadata_integrity.h @@ -0,0 +1,49 @@ +/* + * Copyright 2023 SUSE, LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FSCK_SFS_SRC_CHECKS_METADATA_INTEGRITY_H__ +#define FSCK_SFS_SRC_CHECKS_METADATA_INTEGRITY_H__ + +#include +#include + +#include "checks.h" +#include "sqlite.h" + +class MetadataIntegrityFix : public Fix { + private: + std::string to_string() const; + std::vector errors; + + public: + MetadataIntegrityFix( + const std::filesystem::path& path, const std::vector& _errors + ); + operator std::string() const { return to_string(); }; + void fix(); +}; + +class MetadataIntegrityCheck : public Check { + private: + std::unique_ptr metadata; + + public: + MetadataIntegrityCheck(const std::filesystem::path& path); + virtual ~MetadataIntegrityCheck() override; + virtual bool check() override; +}; + +#endif // FSCK_SFS_SRC_CHECKS_METADATA_INTEGRITY_H__ diff --git a/src/main.cc b/src/main.cc index bbffed8..8d5885c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -93,8 +93,6 @@ int main(int argc, char* argv[]) { std::filesystem::is_regular_file(path_database), "Metadata database is not a regular file" ); - Database db(path_database); - FSCK_ASSERT(db.check_integrity(), "Database integrity check failed"); } return run_checks(path_root, options_map.count("fix") > 0) ? 0 : 1; diff --git a/src/sqlite.cc b/src/sqlite.cc index 5b0ae84..0a8dcb2 100644 --- a/src/sqlite.cc +++ b/src/sqlite.cc @@ -18,8 +18,6 @@ #include -#include -#include #include #include @@ -37,36 +35,6 @@ Database::~Database() { sqlite3_close(handle); } -bool Database::check_integrity() { - auto callback = [](void* arg, int num_columns, char** column_data, char**) { - assert(num_columns == 1); - if (std::strcmp(column_data[0], "ok") == 0) { - *(static_cast(arg)) = true; - } else { - std::cout << column_data[0] << std::endl; - } - return 0; - }; - - bool is_ok = false; - // This will return up to 100 error rows by default. For more details see - // https://www.sqlite.org/pragma.html#pragma_integrity_check - int rc = sqlite3_exec( - handle, "PRAGMA integrity_check", callback, static_cast(&is_ok), - NULL - ); - if (rc != SQLITE_OK) { - // This will happen if the file is _so_ trashed it doesn't even - // look like an SQLite database. Here you'll see things like: - // - file is not a database (26) - // - database disk image is malformed (11) - std::cout << "sqlite3_exec error: " << sqlite3_errstr(rc) << " (" << rc - << ")" << std::endl; - } - - return is_ok; -} - int Database::prepare(std::string query, sqlite3_stmt** stm) { int rc = 0; const char* unused = 0; diff --git a/src/sqlite.h b/src/sqlite.h index d2a4b19..7017131 100644 --- a/src/sqlite.h +++ b/src/sqlite.h @@ -36,8 +36,6 @@ class Database { Database(std::filesystem::path); ~Database(); - bool check_integrity(); - int prepare(std::string, sqlite3_stmt**); int count_in_table(std::string, std::string);