• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 2213

10 Apr 2024 11:21PM UTC coverage: 91.792% (-0.8%) from 92.623%
2213

push

Evergreen

web-flow
Add missing availability checks for SecCopyErrorMessageString (#7577)

This requires iOS 11.3 and we currently target iOS 11.

94842 of 175770 branches covered (53.96%)

7 of 22 new or added lines in 2 files covered. (31.82%)

1861 existing lines in 82 files now uncovered.

242866 of 264583 relevant lines covered (91.79%)

5593111.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.02
/src/realm/object-store/sync/impl/sync_file.cpp
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
#include <realm/object-store/sync/impl/sync_file.hpp>
20

21
#include <realm/object-store/sync/sync_user.hpp>
22

23
#include <realm/db.hpp>
24
#include <realm/object-store/sync/app_config.hpp>
25
#include <realm/sync/config.hpp>
26
#include <realm/util/file.hpp>
27
#include <realm/util/hex_dump.hpp>
28
#include <realm/util/sha_crypto.hpp>
29
#include <realm/util/time.hpp>
30
#include <realm/util/scope_exit.hpp>
31

32
#include <iomanip>
33
#include <sstream>
34
#include <system_error>
35
#include <fstream>
36

37
#ifdef _WIN32
38
#include <io.h>
39
#include <fcntl.h>
40

41
inline static int mkstemp(char* _template)
42
{
43
    return _open(_mktemp(_template), _O_CREAT | _O_TEMPORARY, _S_IREAD | _S_IWRITE);
44
}
45
#else
46
#include <unistd.h>
47
#endif
48

49

50
using File = realm::util::File;
51

52
namespace realm {
53

54
namespace {
55

56
uint8_t value_of_hex_digit(char hex_digit)
57
{
76✔
58
    if (hex_digit >= '0' && hex_digit <= '9') {
76✔
59
        return hex_digit - '0';
40✔
60
    }
40✔
61
    else if (hex_digit >= 'A' && hex_digit <= 'F') {
36✔
62
        return 10 + hex_digit - 'A';
36✔
63
    }
36✔
64
    else if (hex_digit >= 'a' && hex_digit <= 'f') {
×
65
        return 10 + hex_digit - 'a';
×
UNCOV
66
    }
×
UNCOV
67
    else {
×
UNCOV
68
        throw LogicError(ErrorCodes::InvalidArgument, "Cannot get the value of a character that isn't a hex digit.");
×
UNCOV
69
    }
×
70
}
76✔
71

72
bool filename_is_reserved(const std::string& filename)
73
{
4,683✔
74
    return (filename == "." || filename == "..");
4,683✔
75
}
4,683✔
76

77
bool character_is_unreserved(char character)
78
{
46,079✔
79
    bool is_capital_letter = (character >= 'A' && character <= 'Z');
46,079✔
80
    bool is_lowercase_letter = (character >= 'a' && character <= 'z');
46,079✔
81
    bool is_number = (character >= '0' && character <= '9');
46,079✔
82
    bool is_allowed_symbol = (character == '-' || character == '_' || character == '.');
46,079✔
83
    return is_capital_letter || is_lowercase_letter || is_number || is_allowed_symbol;
46,079✔
84
}
46,079✔
85

86
char decoded_char_for(const std::string& percent_encoding, size_t index)
87
{
38✔
88
    if (index + 2 >= percent_encoding.length()) {
38✔
UNCOV
89
        throw LogicError(ErrorCodes::InvalidArgument,
×
UNCOV
90
                         "Malformed string: not enough characters after '%' before end of string.");
×
UNCOV
91
    }
×
92
    REALM_ASSERT(percent_encoding[index] == '%');
38✔
93
    return (16 * value_of_hex_digit(percent_encoding[index + 1])) + value_of_hex_digit(percent_encoding[index + 2]);
38✔
94
}
38✔
95

96
} // namespace
97

98
namespace util {
99

100
std::string make_percent_encoded_string(const std::string& raw_string)
101
{
4,725✔
102
    std::string buffer;
4,725✔
103
    buffer.reserve(raw_string.size());
4,725✔
104
    for (size_t i = 0; i < raw_string.size(); i++) {
50,736✔
105
        unsigned char character = raw_string[i];
46,011✔
106
        if (character_is_unreserved(character)) {
46,011✔
107
            buffer.push_back(character);
45,501✔
108
        }
45,501✔
109
        else {
510✔
110
            buffer.resize(buffer.size() + 3);
510✔
111
            // Format string must resolve to exactly 3 characters.
254✔
112
            snprintf(&buffer.back() - 2, 4, "%%%2X", character);
510✔
113
        }
510✔
114
    }
46,011✔
115
    return buffer;
4,725✔
116
}
4,725✔
117

118
std::string make_raw_string(const std::string& percent_encoded_string)
119
{
4✔
120
    std::string buffer;
4✔
121
    size_t input_len = percent_encoded_string.length();
4✔
122
    buffer.reserve(input_len);
4✔
123
    size_t idx = 0;
4✔
124
    while (idx < input_len) {
110✔
125
        char current = percent_encoded_string[idx];
106✔
126
        if (current == '%') {
106✔
127
            // Decode. +3.
19✔
128
            buffer.push_back(decoded_char_for(percent_encoded_string, idx));
38✔
129
            idx += 3;
38✔
130
        }
38✔
131
        else {
68✔
132
            // No need to decode. +1.
34✔
133
            if (!character_is_unreserved(current)) {
68✔
UNCOV
134
                throw LogicError(ErrorCodes::InvalidArgument,
×
UNCOV
135
                                 "Input string is invalid: contains reserved characters.");
×
UNCOV
136
            }
×
137
            buffer.push_back(current);
68✔
138
            idx++;
68✔
139
        }
68✔
140
    }
106✔
141
    return buffer;
4✔
142
}
4✔
143

144
std::string file_path_by_appending_component(const std::string& path, const std::string& component,
145
                                             FilePathType path_type)
146
{
11,204✔
147
#ifdef _WIN32
148
    const char separator = '\\';
149
#else
150
    const char separator = '/';
11,204✔
151
#endif
11,204✔
152
    std::string buffer;
11,204✔
153
    buffer.reserve(2 + path.length() + component.length());
11,204✔
154
    buffer.append(path);
11,204✔
155
    std::string terminal = "";
11,204✔
156
    if (path_type == FilePathType::Directory && component[component.length() - 1] != separator) {
11,204✔
157
        terminal = separator;
10,276✔
158
    }
10,276✔
159
    char path_last = path[path.length() - 1];
11,204✔
160
    char component_first = component[0];
11,204✔
161
    if (path_last == separator && component_first == separator) {
11,204✔
UNCOV
162
        buffer.append(component.substr(1));
×
UNCOV
163
        buffer.append(terminal);
×
UNCOV
164
    }
×
165
    else if (path_last == separator || component_first == separator) {
11,204✔
166
        buffer.append(component);
6,839✔
167
        buffer.append(terminal);
6,839✔
168
    }
6,839✔
169
    else {
4,365✔
170
        buffer.append(std::string(1, separator));
4,365✔
171
        buffer.append(component);
4,365✔
172
        buffer.append(terminal);
4,365✔
173
    }
4,365✔
174
    return buffer;
11,204✔
175
}
11,204✔
176

177
std::string file_path_by_appending_extension(const std::string& path, const std::string& extension)
178
{
6✔
179
    std::string buffer;
6✔
180
    buffer.reserve(1 + path.length() + extension.length());
6✔
181
    buffer.append(path);
6✔
182
    char path_last = path[path.length() - 1];
6✔
183
    char extension_first = extension[0];
6✔
184
    if (path_last == '.' && extension_first == '.') {
6✔
185
        buffer.append(extension.substr(1));
2✔
186
    }
2✔
187
    else if (path_last == '.' || extension_first == '.') {
4✔
188
        buffer.append(extension);
4✔
189
    }
4✔
UNCOV
190
    else {
×
UNCOV
191
        buffer.append(".");
×
UNCOV
192
        buffer.append(extension);
×
UNCOV
193
    }
×
194
    return buffer;
6✔
195
}
6✔
196

197
std::string create_timestamped_template(const std::string& prefix, int wildcard_count)
198
{
74✔
199
    constexpr int WILDCARD_MAX = 20;
74✔
200
    constexpr int WILDCARD_MIN = 6;
74✔
201
    wildcard_count = std::min(WILDCARD_MAX, std::max(WILDCARD_MIN, wildcard_count));
74✔
202
    std::time_t time = std::time(nullptr);
74✔
203
    std::stringstream stream;
74✔
204
    stream << prefix << "-" << util::format_local_time(time, "%Y%m%d-%H%M%S") << "-"
74✔
205
           << std::string(wildcard_count, 'X');
74✔
206
    return stream.str();
74✔
207
}
74✔
208

209
std::string reserve_unique_file_name(const std::string& path, const std::string& template_string)
210
{
74✔
211
    REALM_ASSERT_DEBUG(template_string.find("XXXXXX") != std::string::npos);
74✔
212
    std::string path_buffer = file_path_by_appending_component(path, template_string, FilePathType::File);
74✔
213
    int fd = mkstemp(&path_buffer[0]);
74✔
214
    if (fd < 0) {
74✔
215
        int err = errno;
×
UNCOV
216
        throw RuntimeError(ErrorCodes::FileOperationFailed,
×
UNCOV
217
                           util::format("Failed to make temporary path: %1 (%2)",
×
UNCOV
218
                                        std::system_error(err, std::system_category()).what(), err));
×
UNCOV
219
    }
×
220
    // Remove the file so we can use the name for our own file.
37✔
221
#ifdef _WIN32
222
    _close(fd);
223
    _unlink(path_buffer.c_str());
224
#else
225
    close(fd);
74✔
226
    unlink(path_buffer.c_str());
74✔
227
#endif
74✔
228
    return path_buffer;
74✔
229
}
74✔
230

231
static std::string validate_and_clean_path(const std::string& path)
232
{
4,683✔
233
    REALM_ASSERT(path.length() > 0);
4,683✔
234
    std::string escaped_path = util::make_percent_encoded_string(path);
4,683✔
235
    if (filename_is_reserved(escaped_path))
4,683✔
UNCOV
236
        throw LogicError(
×
UNCOV
237
            ErrorCodes::InvalidArgument,
×
UNCOV
238
            util::format("A path can't have an identifier reserved by the filesystem: '%1'", escaped_path));
×
239
    return escaped_path;
4,683✔
240
}
4,683✔
241

242
} // namespace util
243

244
SyncFileManager::SyncFileManager(const app::AppConfig& config)
245
    : m_base_path(util::file_path_by_appending_component(config.base_file_path, c_sync_directory,
246
                                                         util::FilePathType::Directory))
247
    , m_app_path(util::file_path_by_appending_component(m_base_path, util::validate_and_clean_path(config.app_id),
248
                                                        util::FilePathType::Directory))
249
{
4,355✔
250
    util::try_make_dir(m_base_path);
4,355✔
251
    util::try_make_dir(m_app_path);
4,355✔
252
}
4,355✔
253

254
std::string SyncFileManager::get_special_directory(std::string directory_name) const
255
{
708✔
256
    auto dir_path = file_path_by_appending_component(m_app_path, directory_name, util::FilePathType::Directory);
708✔
257
    util::try_make_dir(dir_path);
708✔
258
    return dir_path;
708✔
259
}
708✔
260

261
std::string SyncFileManager::user_directory(const std::string& user_id) const
262
{
86✔
263
    std::string user_path = get_user_directory_path(user_id);
86✔
264
    util::try_make_dir(user_path);
86✔
265
    return user_path;
86✔
266
}
86✔
267

268
void SyncFileManager::remove_user_realms(const std::string& user_id) const
269
{
30✔
270
    // The following is redundant except for apps built before file tracking.
14✔
271
    std::string user_path = get_user_directory_path(user_id);
30✔
272
    util::try_remove_dir_recursive(user_path);
30✔
273
}
30✔
274

275
bool SyncFileManager::remove_realm(const std::string& absolute_path) const
276
{
48✔
277
    REALM_ASSERT(absolute_path.length() > 0);
48✔
278
    bool success = true;
48✔
279
    try {
48✔
280
        constexpr bool delete_lockfile = true;
48✔
281
        realm::DB::delete_files(absolute_path, &success, delete_lockfile);
48✔
282
    }
48✔
283
    catch (FileAccessError const&) {
24✔
284
        success = false;
3✔
285
    }
3✔
286
    return success;
48✔
287
}
48✔
288

289
bool SyncFileManager::copy_realm_file(const std::string& old_path, const std::string& new_path) const
290
{
16✔
291
    REALM_ASSERT(old_path.length() > 0);
16✔
292
    try {
16✔
293
        const bool overwrite_existing = false;
16✔
294
        return File::copy(old_path, new_path, overwrite_existing);
16✔
295
    }
16✔
296
    catch (FileAccessError const&) {
×
297
        return false;
×
298
    }
×
299
    return true;
×
UNCOV
300
}
×
301

302
bool SyncFileManager::remove_realm(const std::string& user_id, const std::vector<std::string>& legacy_user_identities,
303
                                   const std::string& raw_realm_path, const std::string& partition) const
304
{
4✔
305
    auto existing = get_existing_realm_file_path(user_id, legacy_user_identities, raw_realm_path, partition);
4✔
306
    if (existing) {
4✔
307
        return remove_realm(*existing);
2✔
308
    }
2✔
309
    return false; // if there is nothing to remove this is considered to be not successful
2✔
310
}
2✔
311

312
bool SyncFileManager::try_file_exists(const std::string& path) noexcept
313
{
226✔
314
    try {
226✔
315
        // May throw; for example when the path is too long
113✔
316
        return util::File::exists(path);
226✔
317
    }
226✔
318
    catch (const std::exception&) {
8✔
319
        return false;
8✔
320
    }
8✔
321
}
226✔
322

323
static bool try_file_remove(const std::string& path) noexcept
324
{
40✔
325
    try {
40✔
326
        return util::File::try_remove(path);
40✔
327
    }
40✔
328
    catch (const std::exception&) {
4✔
329
        return false;
4✔
330
    }
4✔
331
}
40✔
332

333
util::Optional<std::string>
334
SyncFileManager::get_existing_realm_file_path(const std::string& user_id,
335
                                              const std::vector<std::string>& legacy_user_identities,
336
                                              const std::string& realm_file_name, const std::string& partition) const
337
{
50✔
338
    std::string preferred_name_without_suffix = preferred_realm_path_without_suffix(user_id, realm_file_name);
50✔
339
    if (try_file_exists(preferred_name_without_suffix)) {
50✔
340
        return preferred_name_without_suffix;
×
341
    }
×
342

25✔
343
    std::string preferred_name_with_suffix = preferred_name_without_suffix + c_realm_file_suffix;
50✔
344
    if (try_file_exists(preferred_name_with_suffix)) {
50✔
345
        return preferred_name_with_suffix;
4✔
346
    }
4✔
347

23✔
348
    // Shorten the Realm path to just `<rootDir>/<hashedAbsolutePath>.realm`
23✔
349
    std::string hashed_name = fallback_hashed_realm_file_path(preferred_name_without_suffix);
46✔
350
    std::string hashed_path = hashed_name + c_realm_file_suffix;
46✔
351
    if (try_file_exists(hashed_path)) {
46✔
352
        // detected that the hashed fallback has been used previously
1✔
353
        // it was created for a reason so keep using it
1✔
354
        return hashed_path;
2✔
355
    }
2✔
356

22✔
357
    // The legacy fallback paths are not applicable to flexible sync
22✔
358
    if (partition.empty()) {
44✔
359
        return util::none;
10✔
360
    }
10✔
361

17✔
362
    // We used to hash the string value of the partition. For compatibility, check that SHA256
17✔
363
    // hash file name exists, and if it does, continue to use it.
17✔
364
    if (!partition.empty()) {
34✔
365
        std::string hashed_partition_path = legacy_hashed_partition_path(user_id, partition);
34✔
366
        if (try_file_exists(hashed_partition_path)) {
34✔
367
            return hashed_partition_path;
×
368
        }
×
369
    }
34✔
370

17✔
371
    for (auto& legacy_identity : legacy_user_identities) {
34✔
372
        // retain support for legacy paths
12✔
373
        std::string old_path = legacy_realm_file_path(legacy_identity, realm_file_name);
24✔
374
        if (try_file_exists(old_path)) {
24✔
375
            return old_path;
2✔
376
        }
2✔
377
        // retain support for legacy local identity paths
11✔
378
        std::string old_local_identity_path = legacy_local_identity_path(legacy_identity, partition);
22✔
379
        if (try_file_exists(old_local_identity_path)) {
22✔
380
            return old_local_identity_path;
4✔
381
        }
4✔
382
    }
22✔
383

17✔
384
    return util::none;
31✔
385
}
34✔
386

387
std::string SyncFileManager::realm_file_path(const std::string& user_id,
388
                                             const std::vector<std::string>& legacy_user_identities,
389
                                             const std::string& realm_file_name, const std::string& partition) const
390
{
46✔
391
    auto existing_path = get_existing_realm_file_path(user_id, legacy_user_identities, realm_file_name, partition);
46✔
392
    if (existing_path) {
46✔
393
        return *existing_path;
10✔
394
    }
10✔
395

18✔
396
    // since this appears to be a new file, test the normal location
18✔
397
    // we use a test file with the same name and a suffix of the
18✔
398
    // same length, so we can catch "filename too long" errors on windows
18✔
399
    std::string preferred_name_without_suffix = preferred_realm_path_without_suffix(user_id, realm_file_name);
36✔
400
    std::string preferred_name_with_suffix = preferred_name_without_suffix + c_realm_file_suffix;
36✔
401
    try {
36✔
402
        std::string test_path = preferred_name_without_suffix + c_realm_file_test_suffix;
36✔
403
        auto defer = util::make_scope_exit([test_path]() noexcept {
36✔
404
            try_file_remove(test_path);
36✔
405
        });
36✔
406
        util::File f(test_path, util::File::Mode::mode_Write);
36✔
407
        // if the test file succeeds, delete it and return the preferred location
18✔
408
    }
36✔
409
    catch (const FileAccessError&) {
20✔
410
        // the preferred test failed, test the hashed path
2✔
411
        std::string hashed_name = fallback_hashed_realm_file_path(preferred_name_without_suffix);
4✔
412
        std::string hashed_path = hashed_name + c_realm_file_suffix;
4✔
413
        try {
4✔
414
            std::string test_hashed_path = hashed_name + c_realm_file_test_suffix;
4✔
415
            auto defer = util::make_scope_exit([test_hashed_path]() noexcept {
4✔
416
                try_file_remove(test_hashed_path);
4✔
417
            });
4✔
418
            util::File f(test_hashed_path, util::File::Mode::mode_Write);
4✔
419
            // at this point the create succeeded, clean up the test file and return the hashed path
2✔
420
            return hashed_path;
4✔
421
        }
4✔
UNCOV
422
        catch (const FileAccessError& e_hashed) {
×
423
            // hashed test path also failed, give up and report error to user.
UNCOV
424
            throw LogicError(ErrorCodes::InvalidArgument,
×
425
                             util::format("A valid realm path cannot be created for the "
×
426
                                          "Realm identity '%1' at neither '%2' nor '%3'. %4",
×
427
                                          realm_file_name, preferred_name_with_suffix, hashed_path, e_hashed.what()));
×
428
        }
×
429
    }
32✔
430

16✔
431
    return preferred_name_with_suffix;
32✔
432
}
32✔
433

434
std::string SyncFileManager::metadata_path() const
435
{
632✔
436
    auto dir_path = file_path_by_appending_component(get_utility_directory(), c_metadata_directory,
632✔
437
                                                     util::FilePathType::Directory);
632✔
438
    util::try_make_dir(dir_path);
632✔
439
    return util::file_path_by_appending_component(dir_path, c_metadata_realm);
632✔
440
}
632✔
441

442
bool SyncFileManager::remove_metadata_realm() const
443
{
2✔
444
    auto dir_path = file_path_by_appending_component(get_utility_directory(), c_metadata_directory,
2✔
445
                                                     util::FilePathType::Directory);
2✔
446
    try {
2✔
447
        util::try_remove_dir_recursive(dir_path);
2✔
448
        return true;
2✔
449
    }
2✔
UNCOV
450
    catch (FileAccessError const&) {
×
451
        return false;
×
452
    }
×
453
}
2✔
454

455
std::string SyncFileManager::preferred_realm_path_without_suffix(const std::string& user_id,
456
                                                                 const std::string& realm_file_name) const
457
{
86✔
458
    auto escaped_file_name = util::validate_and_clean_path(realm_file_name);
86✔
459
    std::string preferred_name = util::file_path_by_appending_component(user_directory(user_id), escaped_file_name);
86✔
460
    if (StringData(preferred_name).ends_with(c_realm_file_suffix)) {
86✔
461
        preferred_name = preferred_name.substr(0, preferred_name.size() - strlen(c_realm_file_suffix));
10✔
462
    }
10✔
463
    return preferred_name;
86✔
464
}
86✔
465

466
std::string SyncFileManager::fallback_hashed_realm_file_path(const std::string& preferred_path) const
467
{
50✔
468
    std::array<unsigned char, 32> hash;
50✔
469
    util::sha256(preferred_path.data(), preferred_path.size(), hash.data());
50✔
470
    std::string hashed_name =
50✔
471
        util::file_path_by_appending_component(m_app_path, util::hex_dump(hash.data(), hash.size(), ""));
50✔
472
    return hashed_name;
50✔
473
}
50✔
474

475
std::string SyncFileManager::legacy_hashed_partition_path(const std::string& user_id,
476
                                                          const std::string& partition) const
477
{
34✔
478
    std::array<unsigned char, 32> hash;
34✔
479
    util::sha256(partition.data(), partition.size(), hash.data());
34✔
480
    std::string legacy_hashed_file_name = util::hex_dump(hash.data(), hash.size(), "");
34✔
481
    std::string legacy_partition_path = util::file_path_by_appending_component(
34✔
482
        get_user_directory_path(user_id), legacy_hashed_file_name + c_realm_file_suffix);
34✔
483
    return legacy_partition_path;
34✔
484
}
34✔
485

486
std::string SyncFileManager::legacy_realm_file_path(const std::string& local_user_identity,
487
                                                    const std::string& realm_file_name) const
488
{
24✔
489
    auto path =
24✔
490
        util::file_path_by_appending_component(m_app_path, c_legacy_sync_directory, util::FilePathType::Directory);
24✔
491
    path = util::file_path_by_appending_component(path, util::validate_and_clean_path(local_user_identity),
24✔
492
                                                  util::FilePathType::Directory);
24✔
493
    path = util::file_path_by_appending_component(path, util::validate_and_clean_path(realm_file_name));
24✔
494
    return path;
24✔
495
}
24✔
496

497
std::string SyncFileManager::legacy_local_identity_path(const std::string& local_user_identity,
498
                                                        const std::string& realm_file_name) const
499
{
22✔
500
    auto escaped_file_name = util::validate_and_clean_path(realm_file_name);
22✔
501
    std::string user_path = get_user_directory_path(local_user_identity);
22✔
502
    std::string path_name = util::file_path_by_appending_component(user_path, escaped_file_name);
22✔
503
    std::string path = path_name + c_realm_file_suffix;
22✔
504

11✔
505
    return path;
22✔
506
}
22✔
507

508
std::string SyncFileManager::get_user_directory_path(const std::string& user_id) const
509
{
172✔
510
    return file_path_by_appending_component(m_app_path, util::validate_and_clean_path(user_id),
172✔
511
                                            util::FilePathType::Directory);
172✔
512
}
172✔
513

514
static std::string string_from_partition(std::string_view partition)
515
{
14✔
516
    bson::Bson partition_value = bson::parse(partition);
14✔
517
    switch (partition_value.type()) {
14✔
518
        case bson::Bson::Type::Int32:
2✔
519
            return util::format("i_%1", static_cast<int32_t>(partition_value));
2✔
520
        case bson::Bson::Type::Int64:
2✔
521
            return util::format("l_%1", static_cast<int64_t>(partition_value));
2✔
522
        case bson::Bson::Type::String:
4✔
523
            return util::format("s_%1", static_cast<std::string>(partition_value));
4✔
524
        case bson::Bson::Type::ObjectId:
2✔
525
            return util::format("o_%1", static_cast<ObjectId>(partition_value).to_string());
2✔
526
        case bson::Bson::Type::Uuid:
2✔
527
            return util::format("u_%1", static_cast<UUID>(partition_value).to_string());
2✔
528
        case bson::Bson::Type::Null:
2✔
529
            return "null";
2✔
UNCOV
530
        default:
✔
UNCOV
531
            throw InvalidArgument(util::format("Unsupported partition key value: '%1'. Only int, string "
×
UNCOV
532
                                               "UUID and ObjectId types are currently supported.",
×
UNCOV
533
                                               partition_value.to_string()));
×
534
    }
14✔
535
}
14✔
536

537
std::string SyncFileManager::path_for_realm(const SyncConfig& config,
538
                                            std::optional<std::string> custom_file_name) const
539
{
28✔
540
    auto user = config.user;
28✔
541
    REALM_ASSERT(user);
28✔
542
    // Attempt to make a nicer filename which will ease debugging when
14✔
543
    // locating files in the filesystem.
14✔
544
    auto file_name = [&]() -> std::string {
28✔
545
        if (custom_file_name) {
28✔
546
            return *custom_file_name;
12✔
547
        }
12✔
548
        if (config.flx_sync_requested) {
16✔
549
            REALM_ASSERT_DEBUG(config.partition_value.empty());
2✔
550
            return "flx_sync_default";
2✔
551
        }
2✔
552
        return string_from_partition(config.partition_value);
14✔
553
    }();
14✔
554
    auto path = realm_file_path(user->user_id(), user->legacy_identities(), file_name, config.partition_value);
28✔
555
    user->track_realm(path);
28✔
556
    return path;
28✔
557
}
28✔
558

559
} // namespace realm
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc