diff --git a/src/impl/realm_coordinator.cpp b/src/impl/realm_coordinator.cpp index 51922f24..2fd0dd0e 100644 --- a/src/impl/realm_coordinator.cpp +++ b/src/impl/realm_coordinator.cpp @@ -69,7 +69,7 @@ std::shared_ptr RealmCoordinator::get_realm(Realm::Config config) m_notifier = std::make_unique(*this); } catch (std::system_error const& ex) { - throw RealmFileException(RealmFileException::Kind::AccessError, config.path, ex.code().message()); + throw RealmFileException(RealmFileException::Kind::AccessError, config.path, ex.code().message(), ""); } } } diff --git a/src/shared_realm.cpp b/src/shared_realm.cpp index dc0ba898..48c835d1 100644 --- a/src/shared_realm.cpp +++ b/src/shared_realm.cpp @@ -68,51 +68,76 @@ Realm::Realm(Config config) } } +REALM_NOINLINE static void translate_file_exception(StringData path, bool read_only=false) +{ + try { + throw; + } + catch (util::File::PermissionDenied const& ex) { + throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(), + "Unable to open a realm at path '" + ex.get_path() + + "'. Please use a path where your app has " + (read_only ? "read" : "read-write") + " permissions.", + ex.what()); + } + catch (util::File::Exists const& ex) { + throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(), + "File at path '" + ex.get_path() + "' already exists.", + ex.what()); + } + catch (util::File::NotFound const& ex) { + throw RealmFileException(RealmFileException::Kind::NotFound, ex.get_path(), + "Directory at path '" + ex.get_path() + "' does not exist.", + ex.what()); + } + catch (util::File::AccessError const& ex) { + // Errors for `open()` include the path, but other errors don't. We + // don't want two copies of the path in the error, so strip it out if it + // appears, and then include it in our prefix. + std::string underlying = ex.what(); + auto pos = underlying.find(ex.get_path()); + if (pos != std::string::npos && pos > 0) { + // One extra char at each end for the quotes + underlying.replace(pos - 1, ex.get_path().size() + 2, ""); + } + throw RealmFileException(RealmFileException::Kind::AccessError, ex.get_path(), + "Unable to open a realm at path '" + ex.get_path() + "': " + underlying, + ex.what()); + } + catch (IncompatibleLockFile const& ex) { + throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, path, + "Realm file is currently open in another process " + "which cannot share access with this process. All processes sharing a single file must be the same architecture.", + ex.what()); + } + catch (FileFormatUpgradeRequired const& ex) { + throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, path, + "The Realm file format must be allowed to be upgraded " + "in order to proceed.", + ex.what()); + } +} + void Realm::open_with_config(const Config& config, std::unique_ptr& history, std::unique_ptr& shared_group, std::unique_ptr& read_only_group) { + if (config.encryption_key.data() && config.encryption_key.size() != 64) { + throw InvalidEncryptionKeyException(); + } try { if (config.read_only) { read_only_group = std::make_unique(config.path, config.encryption_key.data(), Group::mode_ReadOnly); } else { - if (config.encryption_key.data() && config.encryption_key.size() != 64) { - throw InvalidEncryptionKeyException(); - } history = realm::make_client_history(config.path, config.encryption_key.data()); SharedGroup::DurabilityLevel durability = config.in_memory ? SharedGroup::durability_MemOnly : SharedGroup::durability_Full; shared_group = std::make_unique(*history, durability, config.encryption_key.data(), !config.disable_format_upgrade); } } - catch (util::File::PermissionDenied const& ex) { - throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(), - "Unable to open a realm at path '" + ex.get_path() + - "'. Please use a path where your app has " + (config.read_only ? "read" : "read-write") + " permissions."); - } - catch (util::File::Exists const& ex) { - throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(), - "File at path '" + ex.get_path() + "' already exists."); - } - catch (util::File::NotFound const& ex) { - throw RealmFileException(RealmFileException::Kind::NotFound, ex.get_path(), - "File at path '" + ex.get_path() + "' does not exist."); - } - catch (util::File::AccessError const& ex) { - throw RealmFileException(RealmFileException::Kind::AccessError, ex.get_path(), - "Unable to open a realm at path '" + ex.get_path() + "'"); - } - catch (IncompatibleLockFile const& ex) { - throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, config.path, - "Realm file is currently open in another process " - "which cannot share access with this process. All processes sharing a single file must be the same architecture."); - } - catch (FileFormatUpgradeRequired const& ex) { - throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, config.path, - "The Realm file format must be allowed to be upgraded " - "in order to proceed."); + catch (...) { + translate_file_exception(config.path, config.read_only); } } @@ -138,7 +163,7 @@ void Realm::init(std::shared_ptr coordinator) if (target_schema) { if (m_config.read_only) { if (m_config.schema_version == ObjectStore::NotVersioned) { - throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema"); + throw UninitializedRealmException("Can't open an un-initialized Realm without a Schema"); } target_schema->validate(); ObjectStore::verify_schema(*m_config.schema, *target_schema, true); @@ -370,6 +395,18 @@ bool Realm::compact() return m_shared_group->compact(); } +void Realm::write_copy(StringData path, BinaryData key) +{ + REALM_ASSERT(!key.data() || key.size() == 64); + verify_thread(); + try { + read_group()->write(path, key.data()); + } + catch (...) { + translate_file_exception(path); + } +} + void Realm::notify() { verify_thread(); diff --git a/src/shared_realm.hpp b/src/shared_realm.hpp index 90ff1665..11f6c326 100644 --- a/src/shared_realm.hpp +++ b/src/shared_realm.hpp @@ -19,19 +19,20 @@ #ifndef REALM_REALM_HPP #define REALM_REALM_HPP -#include "schema.hpp" - #include #include #include #include namespace realm { + class BinaryData; class BindingContext; class Group; class Realm; class Replication; + class Schema; class SharedGroup; + class StringData; typedef std::shared_ptr SharedRealm; typedef std::weak_ptr WeakRealm; @@ -121,6 +122,7 @@ namespace realm { void invalidate(); bool compact(); + void write_copy(StringData path, BinaryData encryption_key); std::thread::id thread_id() const { return m_thread_id; } void verify_thread() const; @@ -199,14 +201,16 @@ namespace realm { /** Thrown if the file needs to be upgraded to a new format, but upgrades have been explicitly disabled. */ FormatUpgradeRequired, }; - RealmFileException(Kind kind, std::string path, std::string message) : - std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)) {} + RealmFileException(Kind kind, std::string path, std::string message, std::string underlying) : + std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)), m_underlying(std::move(underlying)) {} Kind kind() const { return m_kind; } const std::string& path() const { return m_path; } + const std::string& underlying() const { return m_underlying; } private: Kind m_kind; std::string m_path; + std::string m_underlying; }; class MismatchedConfigException : public std::runtime_error { @@ -224,9 +228,9 @@ namespace realm { IncorrectThreadException() : std::runtime_error("Realm accessed from incorrect thread.") {} }; - class UnitializedRealmException : public std::runtime_error { + class UninitializedRealmException : public std::runtime_error { public: - UnitializedRealmException(std::string message) : std::runtime_error(message) {} + UninitializedRealmException(std::string message) : std::runtime_error(message) {} }; class InvalidEncryptionKeyException : public std::runtime_error { diff --git a/tests/util/test_file.cpp b/tests/util/test_file.cpp index 708c0381..0c6efa99 100644 --- a/tests/util/test_file.cpp +++ b/tests/util/test_file.cpp @@ -3,6 +3,7 @@ #include "impl/realm_coordinator.hpp" #include +#include #include #include