added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / util / encrypted_file_mapping.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP
20 #define REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP
21
22 #include <realm/util/file.hpp>
23 #include <realm/util/thread.hpp>
24 #include <realm/util/features.h>
25
26 #if REALM_ENABLE_ENCRYPTION
27
28 typedef size_t (*Header_to_size)(const char* addr);
29
30 #include <vector>
31
32 namespace realm {
33 namespace util {
34
35 struct SharedFileInfo;
36 class EncryptedFileMapping;
37
38 class EncryptedFileMapping {
39 public:
40     // Adds the newly-created object to file.mappings iff it's successfully constructed
41     EncryptedFileMapping(SharedFileInfo& file, size_t file_offset, void* addr, size_t size, File::AccessMode access);
42     ~EncryptedFileMapping();
43
44     // Default implementations of copy/assign can trigger multiple destructions
45     EncryptedFileMapping(const EncryptedFileMapping&) = delete;
46     EncryptedFileMapping& operator=(const EncryptedFileMapping&) = delete;
47
48     // Write all dirty pages to disk and mark them read-only
49     // Does not call fsync
50     void flush() noexcept;
51
52     // Sync this file to disk
53     void sync() noexcept;
54
55     // Make sure that memory in the specified range is synchronized with any
56     // changes made globally visible through call to write_barrier
57     void read_barrier(const void* addr, size_t size, UniqueLock& lock, Header_to_size header_to_size);
58
59     // Ensures that any changes made to memory in the specified range
60     // becomes visible to any later calls to read_barrier()
61     void write_barrier(const void* addr, size_t size) noexcept;
62
63     // Set this mapping to a new address and size
64     // Flushes any remaining dirty pages from the old mapping
65     void set(void* new_addr, size_t new_size, size_t new_file_offset);
66
67     bool contains_page(size_t page_in_file) const;
68     size_t get_local_index_of_address(const void* addr, size_t offset = 0) const;
69
70 private:
71     SharedFileInfo& m_file;
72
73     size_t m_page_shift;
74     size_t m_blocks_per_page;
75
76     void* m_addr = nullptr;
77
78     size_t m_first_page;
79
80     // MUST be of type char because of coherence issues when writing inside mutex and reading outside 
81     // it. FIXME: We're investigating if this is good enough, or if we need further mechanisms
82     std::vector<char> m_up_to_date_pages;
83     std::vector<bool> m_dirty_pages;
84
85     File::AccessMode m_access;
86
87 #ifdef REALM_DEBUG
88     std::unique_ptr<char[]> m_validate_buffer;
89 #endif
90
91     char* page_addr(size_t local_page_ndx) const noexcept;
92
93     void mark_outdated(size_t local_page_ndx) noexcept;
94     bool copy_up_to_date_page(size_t local_page_ndx) noexcept;
95     void refresh_page(size_t local_page_ndx);
96     void write_page(size_t local_page_ndx) noexcept;
97
98     void validate_page(size_t local_page_ndx) noexcept;
99     void validate() noexcept;
100 };
101
102 inline size_t EncryptedFileMapping::get_local_index_of_address(const void* addr, size_t offset) const
103 {
104     REALM_ASSERT_EX(addr >= m_addr, addr, m_addr);
105
106     size_t local_ndx = ((reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(m_addr) + offset) >> m_page_shift);
107     REALM_ASSERT_EX(local_ndx < m_up_to_date_pages.size(), local_ndx, m_up_to_date_pages.size());
108     return local_ndx;
109 }
110
111 inline bool EncryptedFileMapping::contains_page(size_t page_in_file) const
112 {
113     // first check for (page_in_file >= m_first_page) so that the following
114     // subtraction using unsigned types never wraps under 0
115     return page_in_file >= m_first_page && page_in_file - m_first_page < m_up_to_date_pages.size();
116 }
117
118 inline void EncryptedFileMapping::read_barrier(const void* addr, size_t size, UniqueLock& lock,
119                                                Header_to_size header_to_size)
120 {
121     size_t first_accessed_local_page = get_local_index_of_address(addr);
122
123     // make sure the first page is available
124     // Checking before taking the lock is important to performance.
125     if (!m_up_to_date_pages[first_accessed_local_page]) {
126         if (!lock.holds_lock())
127             lock.lock();
128         // after taking the lock, we must repeat the check so that we never
129         // call refresh_page() on a page which is already up to date.
130         if (!m_up_to_date_pages[first_accessed_local_page])
131             refresh_page(first_accessed_local_page);
132     }
133
134     if (header_to_size) {
135
136         // We know it's an array, and array headers are 8-byte aligned, so it is
137         // included in the first page which was handled above.
138         size = header_to_size(static_cast<const char*>(addr));
139     }
140
141     size_t last_idx = get_local_index_of_address(addr, size == 0 ? 0 : size - 1);
142     size_t up_to_date_pages_size = m_up_to_date_pages.size();
143
144     // We already checked first_accessed_local_page above, so we start the loop
145     // at first_accessed_local_page + 1 to check the following page.
146     for (size_t idx = first_accessed_local_page + 1; idx <= last_idx && idx < up_to_date_pages_size; ++idx) {
147         if (!m_up_to_date_pages[idx]) {
148             if (!lock.holds_lock())
149                 lock.lock();
150             // after taking the lock, we must repeat the check so that we never
151             // call refresh_page() on a page which is already up to date.
152             if (!m_up_to_date_pages[idx])
153                 refresh_page(idx);
154         }
155     }
156 }
157 }
158 }
159
160 #endif // REALM_ENABLE_ENCRYPTION
161
162 namespace realm {
163 namespace util {
164
165 /// Thrown by EncryptedFileMapping if a file opened is non-empty and does not
166 /// contain valid encrypted data
167 struct DecryptionFailed : util::File::AccessError {
168     DecryptionFailed()
169         : util::File::AccessError("Decryption failed", std::string())
170     {
171     }
172 };
173 }
174 }
175
176 #endif // REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP