@@ -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+
10431060void SlabAlloc::throw_header_exception (std::string msg, const Header& header, const std::string& path)
10441061{
10451062 char buf[256 ];
0 commit comments