Skip to content

Commit d1b267d

Browse files
committed
Merge branch 'master' of github.com:realm/realm-core into next-major
2 parents b3039cf + 216f71f commit d1b267d

30 files changed

+519
-255
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@
2121

2222
----------------------------------------------
2323

24-
# NEXT RELEASE
24+
# 14.5.0 Release notes
2525

2626
### Enhancements
2727
* Introduce sync 'progress_estimate' parameter (value from 0.0 to 1.0) for existing sync 'ProgressNotifierCallback' api to report sync progress on current batch of upload/download until completion ([#7450](https://github.com/realm/realm-core/issues/7450))
2828

2929
### Fixed
30-
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
3130
* Fix an assertion failure "m_lock_info && m_lock_info->m_file.get_path() == m_filename" that appears to be related to opening a Realm while the file is in the process of being closed on another thread ([Swift #8507](https://github.com/realm/realm-swift/issues/8507)).
3231
* Fixed diverging history due to a bug in the replication code when setting default null values (embedded objects included) ([#7536](https://github.com/realm/realm-core/issues/7536)).
32+
* Version 19.39.33523 of MSVC would crash when compiling for arm64 in release mode ([PR #7533](https://github.com/realm/realm-core/pull/7533)).
33+
* Null pointer exception may be triggered when logging out and async commits callbacks not executed ([#7434](https://github.com/realm/realm-core/issues/7434), since v13.26.0)
34+
* Fixed building for iPhone simulators targeting deployment target 11 ([#7554](https://github.com/realm/realm-core/pull/7554)).
3335

3436
### Breaking changes
3537
* Updated default base URL to be `https://services.cloud.mongodb.com` to support the new domains (was `https://realm.mongodb.com`). ([PR #7534](https://github.com/realm/realm-core/pull/7534))
@@ -42,7 +44,10 @@
4244
### Internals
4345
* Update libuv used in object store tests from v1.35.0 to v1.48.0 ([PR #7508](https://github.com/realm/realm-core/pull/7508)).
4446
* Made `set_default_logger` nullable in the bindgen spec.yml (PR [#7515](https://github.com/realm/realm-core/pull/7515)).
47+
* Recreating the sync metadata Realm when the encryption key changes is now done in a multi-process safe manner ([PR #7526](https://github.com/realm/realm-core/pull/7526)).
4548
* Added `App::default_base_url()` static accessor for SDKs to retrieve the default base URL from Core. ([PR #7534](https://github.com/realm/realm-core/pull/7534))
49+
* Realm2JSON tool will now correctly upgrade file to current fileformat.
50+
* (bindgen) Remove dependency on the `clang-format` package and rely on a binary provided by the system instead.
4651

4752
----------------------------------------------
4853

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import PackageDescription
44
import Foundation
55

6-
let versionStr = "14.4.1"
6+
let versionStr = "14.5.0"
77
let versionPieces = versionStr.split(separator: "-")
88
let versionCompontents = versionPieces[0].split(separator: ".")
99
let versionExtra = versionPieces.count > 1 ? versionPieces[1] : ""

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This repository holds the source code for the core database component used by al
2020

2121
Realm Core is not in itself an "end-user" product with a publicly stable and supported API.
2222

23-
Refer to the [Realm documentation](https://www.mongodb.com/docs/realm) for information about Realm and using the SDKs.
23+
Refer to the [Atlas Device SDK documentation](https://www.mongodb.com/docs/atlas/device-sdks/) for information about Realm and using the SDKs.
2424

2525
## Building Realm
2626

bindgen/src/formatter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,4 @@ export function format(formatter: Formatter, cwd: string, filePaths: string[]):
6767
}
6868
}
6969

70-
export const clangFormat = createCommandFormatter("clang", ["npx", "clang-format", "-i"]);
70+
export const clangFormat = createCommandFormatter("clang", ["clang-format", "-i"]);

dependencies.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PACKAGE_NAME: realm-core
2-
VERSION: 14.4.1
2+
VERSION: 14.5.0
33
OPENSSL_VERSION: 3.2.0
44
ZLIB_VERSION: 1.2.13
55
# https://github.com/10gen/baas/commits

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
"chalk": "^4.1.2",
2323
"change-case": "^4.1.2",
2424
"chevrotain": "^10.4.0",
25-
"clang-format": "^1.8.0",
2625
"commander": "^11.1.0",
2726
"debug": "^4.3.4",
2827
"typescript-json-schema": "^0.55.0",

src/realm/alloc_slab.cpp

Lines changed: 78 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -813,17 +813,39 @@ ref_type SlabAlloc::attach_file(const std::string& path, Config& cfg, util::Writ
813813
// the call below to set_encryption_key.
814814
m_file.set_encryption_key(cfg.encryption_key);
815815

816+
note_reader_start(this);
817+
util::ScopeExit reader_end_guard([this]() noexcept {
818+
note_reader_end(this);
819+
});
816820
size_t size = 0;
817821
// The size of a database file must not exceed what can be encoded in
818822
// size_t.
819823
if (REALM_UNLIKELY(int_cast_with_overflow_detect(m_file.get_size(), size)))
820824
throw InvalidDatabase("Realm file too large", path);
821-
if (cfg.encryption_key && size == 0 && physical_file_size != 0) {
825+
if (cfg.clear_file_on_error && cfg.session_initiator) {
826+
if (size == 0 && physical_file_size != 0) {
827+
cfg.clear_file = true;
828+
}
829+
else if (size > 0) {
830+
try {
831+
read_and_validate_header(m_file, path, size, cfg.session_initiator, m_write_observer);
832+
}
833+
catch (const InvalidDatabase&) {
834+
cfg.clear_file = true;
835+
}
836+
}
837+
}
838+
if (cfg.clear_file) {
839+
m_file.resize(0);
840+
size = 0;
841+
physical_file_size = 0;
842+
}
843+
else if (cfg.encryption_key && !cfg.clear_file && size == 0 && physical_file_size != 0) {
822844
// The opened file holds data, but is so small it cannot have
823845
// been created with encryption
824846
throw InvalidDatabase("Attempt to open unencrypted file with encryption key", path);
825847
}
826-
if (size == 0 || cfg.clear_file) {
848+
if (size == 0) {
827849
if (REALM_UNLIKELY(cfg.read_only))
828850
throw InvalidDatabase("Read-only access to empty Realm file", path);
829851

@@ -833,7 +855,7 @@ ref_type SlabAlloc::attach_file(const std::string& path, Config& cfg, util::Writ
833855
// mappings_for_file in the encryption layer. So the prealloc() is required before
834856
// interacting with the encryption layer in File::write().
835857
// Pre-alloc initial space
836-
m_file.prealloc(initial_size); // Throws
858+
m_file.prealloc(initial_size); // Throws
837859
// seek() back to the start of the file in preparation for writing the header
838860
// This sequence of File operations is protected from races by
839861
// DB::m_controlmutex, so we know we are the only ones operating on the file
@@ -847,65 +869,9 @@ ref_type SlabAlloc::attach_file(const std::string& path, Config& cfg, util::Writ
847869

848870
size = initial_size;
849871
}
850-
ref_type top_ref;
851-
note_reader_start(this);
852-
util::ScopeExit reader_end_guard([this]() noexcept {
853-
note_reader_end(this);
854-
});
855-
856-
try {
857-
// we'll read header and (potentially) footer
858-
File::Map<char> map_header(m_file, File::access_ReadOnly, sizeof(Header), 0, m_write_observer);
859-
realm::util::encryption_read_barrier(map_header, 0, sizeof(Header));
860-
auto header = reinterpret_cast<const Header*>(map_header.get_addr());
861872

862-
File::Map<char> map_footer;
863-
const StreamingFooter* footer = nullptr;
864-
if (is_file_on_streaming_form(*header) && size >= sizeof(StreamingFooter) + sizeof(Header)) {
865-
size_t footer_ref = size - sizeof(StreamingFooter);
866-
size_t footer_page_base = footer_ref & ~(page_size() - 1);
867-
size_t footer_offset = footer_ref - footer_page_base;
868-
map_footer = File::Map<char>(m_file, footer_page_base, File::access_ReadOnly,
869-
sizeof(StreamingFooter) + footer_offset, 0, m_write_observer);
870-
realm::util::encryption_read_barrier(map_footer, footer_offset, sizeof(StreamingFooter));
871-
footer = reinterpret_cast<const StreamingFooter*>(map_footer.get_addr() + footer_offset);
872-
}
873-
874-
top_ref = validate_header(header, footer, size, path, cfg.encryption_key != nullptr); // Throws
875-
m_attach_mode = cfg.is_shared ? attach_SharedFile : attach_UnsharedFile;
876-
m_data = map_header.get_addr(); // <-- needed below
877-
878-
if (cfg.session_initiator && is_file_on_streaming_form(*header)) {
879-
// Don't compare file format version fields as they are allowed to differ.
880-
// Also don't compare reserved fields.
881-
REALM_ASSERT_EX(header->m_flags == 0, header->m_flags, get_file_path_for_assertions());
882-
REALM_ASSERT_EX(header->m_mnemonic[0] == uint8_t('T'), header->m_mnemonic[0],
883-
get_file_path_for_assertions());
884-
REALM_ASSERT_EX(header->m_mnemonic[1] == uint8_t('-'), header->m_mnemonic[1],
885-
get_file_path_for_assertions());
886-
REALM_ASSERT_EX(header->m_mnemonic[2] == uint8_t('D'), header->m_mnemonic[2],
887-
get_file_path_for_assertions());
888-
REALM_ASSERT_EX(header->m_mnemonic[3] == uint8_t('B'), header->m_mnemonic[3],
889-
get_file_path_for_assertions());
890-
REALM_ASSERT_EX(header->m_top_ref[0] == 0xFFFFFFFFFFFFFFFFULL, header->m_top_ref[0],
891-
get_file_path_for_assertions());
892-
REALM_ASSERT_EX(header->m_top_ref[1] == 0, header->m_top_ref[1], get_file_path_for_assertions());
893-
REALM_ASSERT_EX(footer->m_magic_cookie == footer_magic_cookie, footer->m_magic_cookie,
894-
get_file_path_for_assertions());
895-
}
896-
}
897-
catch (const InvalidDatabase&) {
898-
throw;
899-
}
900-
catch (const DecryptionFailed& e) {
901-
throw InvalidDatabase(util::format("Realm file decryption failed (%1)", e.what()), path);
902-
}
903-
catch (const std::exception& e) {
904-
throw InvalidDatabase(e.what(), path);
905-
}
906-
catch (...) {
907-
throw InvalidDatabase("unknown error", path);
908-
}
873+
ref_type top_ref = read_and_validate_header(m_file, path, size, cfg.session_initiator, m_write_observer);
874+
m_attach_mode = cfg.is_shared ? attach_SharedFile : attach_UnsharedFile;
909875
// m_data not valid at this point!
910876
m_baseline = 0;
911877
// make sure that any call to begin_read cause any slab to be placed in free
@@ -1040,6 +1006,57 @@ void SlabAlloc::attach_empty()
10401006
m_ref_translation_ptr = new RefTranslation[1];
10411007
}
10421008

1009+
ref_type SlabAlloc::read_and_validate_header(util::File& file, const std::string& path, size_t size,
1010+
bool session_initiator, util::WriteObserver* write_observer)
1011+
{
1012+
try {
1013+
// we'll read header and (potentially) footer
1014+
File::Map<char> map_header(file, File::access_ReadOnly, sizeof(Header), 0, write_observer);
1015+
realm::util::encryption_read_barrier(map_header, 0, sizeof(Header));
1016+
auto header = reinterpret_cast<const Header*>(map_header.get_addr());
1017+
1018+
File::Map<char> map_footer;
1019+
const StreamingFooter* footer = nullptr;
1020+
if (is_file_on_streaming_form(*header) && size >= sizeof(StreamingFooter) + sizeof(Header)) {
1021+
size_t footer_ref = size - sizeof(StreamingFooter);
1022+
size_t footer_page_base = footer_ref & ~(page_size() - 1);
1023+
size_t footer_offset = footer_ref - footer_page_base;
1024+
map_footer = File::Map<char>(file, footer_page_base, File::access_ReadOnly,
1025+
sizeof(StreamingFooter) + footer_offset, 0, write_observer);
1026+
realm::util::encryption_read_barrier(map_footer, footer_offset, sizeof(StreamingFooter));
1027+
footer = reinterpret_cast<const StreamingFooter*>(map_footer.get_addr() + footer_offset);
1028+
}
1029+
1030+
auto top_ref = validate_header(header, footer, size, path, file.get_encryption_key() != nullptr); // Throws
1031+
1032+
if (session_initiator && is_file_on_streaming_form(*header)) {
1033+
// Don't compare file format version fields as they are allowed to differ.
1034+
// Also don't compare reserved fields.
1035+
REALM_ASSERT_EX(header->m_flags == 0, header->m_flags, path);
1036+
REALM_ASSERT_EX(header->m_mnemonic[0] == uint8_t('T'), header->m_mnemonic[0], path);
1037+
REALM_ASSERT_EX(header->m_mnemonic[1] == uint8_t('-'), header->m_mnemonic[1], path);
1038+
REALM_ASSERT_EX(header->m_mnemonic[2] == uint8_t('D'), header->m_mnemonic[2], path);
1039+
REALM_ASSERT_EX(header->m_mnemonic[3] == uint8_t('B'), header->m_mnemonic[3], path);
1040+
REALM_ASSERT_EX(header->m_top_ref[0] == 0xFFFFFFFFFFFFFFFFULL, header->m_top_ref[0], path);
1041+
REALM_ASSERT_EX(header->m_top_ref[1] == 0, header->m_top_ref[1], path);
1042+
REALM_ASSERT_EX(footer->m_magic_cookie == footer_magic_cookie, footer->m_magic_cookie, path);
1043+
}
1044+
return top_ref;
1045+
}
1046+
catch (const InvalidDatabase&) {
1047+
throw;
1048+
}
1049+
catch (const DecryptionFailed& e) {
1050+
throw InvalidDatabase(util::format("Realm file decryption failed (%1)", e.what()), path);
1051+
}
1052+
catch (const std::exception& e) {
1053+
throw InvalidDatabase(e.what(), path);
1054+
}
1055+
catch (...) {
1056+
throw InvalidDatabase("unknown error", path);
1057+
}
1058+
}
1059+
10431060
void SlabAlloc::throw_header_exception(std::string msg, const Header& header, const std::string& path)
10441061
{
10451062
char buf[256];

src/realm/alloc_slab.hpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,20 @@ class SlabAlloc : public Allocator {
108108
/// Always initialize the file as if it was a newly
109109
/// created file and ignore any pre-existing contents. Requires that
110110
/// Config::session_initiator be true as well.
111+
///
112+
/// \var Config::clear_file_on_error
113+
/// If the file being opened is not a valid Realm file (possibly due to a
114+
/// decryption failure), reinitialize it as if clear_file was set.
111115
struct Config {
116+
const char* encryption_key = nullptr;
112117
bool is_shared = false;
113118
bool read_only = false;
114119
bool no_create = false;
115120
bool skip_validate = false;
116121
bool session_initiator = false;
117122
bool clear_file = false;
123+
bool clear_file_on_error = false;
118124
bool disable_sync = false;
119-
const char* encryption_key = nullptr;
120125
};
121126

122127
struct Retry {};
@@ -363,6 +368,11 @@ class SlabAlloc : public Allocator {
363368
void note_reader_start(const void* reader_id);
364369
void note_reader_end(const void* reader_id) noexcept;
365370

371+
/// Read the header (and possibly footer) from the file, returning the top ref if it's valid and throwing
372+
/// InvalidDatabase otherwise.
373+
static ref_type read_and_validate_header(util::File& file, const std::string& path, size_t size,
374+
bool session_initiator, util::WriteObserver* write_observer);
375+
366376
void verify() const override;
367377
#ifdef REALM_DEBUG
368378
void enable_debug(bool enable)
@@ -703,10 +713,10 @@ class SlabAlloc : public Allocator {
703713
/// corrupted, or if the specified encryption key is incorrect. This
704714
/// function will not detect all forms of corruption, though.
705715
/// Returns the top_ref for the latest commit.
706-
ref_type validate_header(const char* data, size_t len, const std::string& path);
707-
ref_type validate_header(const Header* header, const StreamingFooter* footer, size_t size,
708-
const std::string& path, bool is_encrypted = false);
709-
void throw_header_exception(std::string msg, const Header& header, const std::string& path);
716+
static ref_type validate_header(const char* data, size_t len, const std::string& path);
717+
static ref_type validate_header(const Header* header, const StreamingFooter* footer, size_t size,
718+
const std::string& path, bool is_encrypted = false);
719+
static void throw_header_exception(std::string msg, const Header& header, const std::string& path);
710720

711721
static bool is_file_on_streaming_form(const Header& header);
712722
/// Read the top_ref from the given buffer and set m_file_on_streaming_form

src/realm/db.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ std::string DBOptions::sys_tmp_dir = getenv("TMPDIR") ? getenv("TMPDIR") : "";
898898
// initializing process crashes and leaves the shared memory in an
899899
// undefined state.
900900

901-
void DB::open(const std::string& path, bool no_create_file, const DBOptions& options)
901+
void DB::open(const std::string& path, const DBOptions& options)
902902
{
903903
// Exception safety: Since do_open() is called from constructors, if it
904904
// throws, it must leave the file closed.
@@ -1144,10 +1144,11 @@ void DB::open(const std::string& path, bool no_create_file, const DBOptions& opt
11441144
cfg.read_only = false;
11451145
cfg.skip_validate = !begin_new_session;
11461146
cfg.disable_sync = options.durability == Durability::MemOnly || options.durability == Durability::Unsafe;
1147+
cfg.clear_file_on_error = options.clear_on_invalid_file;
11471148

11481149
// only the session initiator is allowed to create the database, all other
11491150
// must assume that it already exists.
1150-
cfg.no_create = (begin_new_session ? no_create_file : true);
1151+
cfg.no_create = (begin_new_session ? options.no_create : true);
11511152

11521153
// if we're opening a MemOnly file that isn't already opened by
11531154
// someone else then it's a file which should have been deleted on
@@ -1499,8 +1500,7 @@ void DB::open(Replication& repl, const std::string& file, const DBOptions& optio
14991500

15001501
set_replication(&repl);
15011502

1502-
bool no_create = false;
1503-
open(file, no_create, options); // Throws
1503+
open(file, options); // Throws
15041504
}
15051505

15061506
class DBLogger : public Logger {
@@ -1532,7 +1532,7 @@ void DB::set_logger(const std::shared_ptr<util::Logger>& logger) noexcept
15321532
m_logger = std::make_shared<DBLogger>(logger, m_log_id);
15331533
}
15341534

1535-
void DB::open(Replication& repl, const DBOptions options)
1535+
void DB::open(Replication& repl, const DBOptions& options)
15361536
{
15371537
REALM_ASSERT(!is_attached());
15381538
repl.initialize(*this); // Throws
@@ -2808,10 +2808,10 @@ inline DB::DB(Private, const DBOptions& options)
28082808
}
28092809
}
28102810

2811-
DBRef DB::create(const std::string& file, bool no_create, const DBOptions& options) NO_THREAD_SAFETY_ANALYSIS
2811+
DBRef DB::create(const std::string& file, const DBOptions& options) NO_THREAD_SAFETY_ANALYSIS
28122812
{
28132813
DBRef retval = std::make_shared<DB>(Private(), options);
2814-
retval->open(file, no_create, options);
2814+
retval->open(file, options);
28152815
return retval;
28162816
}
28172817

0 commit comments

Comments
 (0)