file.hpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359
  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. #ifndef REALM_UTIL_FILE_HPP
  19. #define REALM_UTIL_FILE_HPP
  20. #include <cstddef>
  21. #include <cstdint>
  22. #include <ctime>
  23. #include <functional>
  24. #include <memory>
  25. #include <stdexcept>
  26. #include <streambuf>
  27. #include <string>
  28. #ifndef _WIN32
  29. #include <dirent.h> // POSIX.1-2001
  30. #endif
  31. #include <realm/utilities.hpp>
  32. #include <realm/util/assert.hpp>
  33. #include <realm/util/backtrace.hpp>
  34. #include <realm/util/features.h>
  35. #include <realm/util/function_ref.hpp>
  36. #include <realm/util/safe_int_ops.hpp>
  37. #if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L // compiling with MSVC and C++ 17
  38. #include <filesystem>
  39. #define REALM_HAVE_STD_FILESYSTEM 1
  40. #if REALM_UWP
  41. // workaround for linker issue described in https://github.com/microsoft/STL/issues/322
  42. // remove once the Windows SDK or STL fixes this.
  43. #pragma comment(lib, "onecoreuap.lib")
  44. #endif
  45. #else
  46. #define REALM_HAVE_STD_FILESYSTEM 0
  47. #endif
  48. #if REALM_APPLE_DEVICE && !REALM_TVOS
  49. #define REALM_FILELOCK_EMULATION
  50. #endif
  51. namespace realm {
  52. namespace util {
  53. class EncryptedFileMapping;
  54. /// Create the specified directory in the file system.
  55. ///
  56. /// \throw File::AccessError If the directory could not be created. If
  57. /// the reason corresponds to one of the exception types that are
  58. /// derived from File::AccessError, the derived exception type is
  59. /// thrown (as long as the underlying system provides the information
  60. /// to unambiguously distinguish that particular reason).
  61. void make_dir(const std::string& path);
  62. /// Same as make_dir() except that this one returns false, rather than throwing
  63. /// an exception, if the specified directory already existed. If the directory
  64. // did not already exist and was newly created, this returns true.
  65. bool try_make_dir(const std::string& path);
  66. /// Remove the specified empty directory path from the file system. It is an
  67. /// error if the specified path is not a directory, or if it is a nonempty
  68. /// directory. In so far as the specified path is a directory, std::remove(const
  69. /// char*) is equivalent to this function.
  70. ///
  71. /// \throw File::AccessError If the directory could not be removed. If the
  72. /// reason corresponds to one of the exception types that are derived from
  73. /// File::AccessError, the derived exception type is thrown (as long as the
  74. /// underlying system provides the information to unambiguously distinguish that
  75. /// particular reason).
  76. void remove_dir(const std::string& path);
  77. /// Same as remove_dir() except that this one returns false, rather
  78. /// than throwing an exception, if the specified directory did not
  79. /// exist. If the directory did exist, and was deleted, this function
  80. /// returns true.
  81. bool try_remove_dir(const std::string& path);
  82. /// Remove the specified directory after removing all its contents. Files
  83. /// (nondirectory entries) will be removed as if by a call to File::remove(),
  84. /// and empty directories as if by a call to remove_dir().
  85. ///
  86. /// \throw File::AccessError If removal of the directory, or any of its contents
  87. /// fail.
  88. ///
  89. /// remove_dir_recursive() assumes that no other process or thread is making
  90. /// simultaneous changes in the directory.
  91. void remove_dir_recursive(const std::string& path);
  92. /// Same as remove_dir_recursive() except that this one returns false, rather
  93. /// than throwing an exception, if the specified directory did not
  94. /// exist. If the directory did exist, and was deleted, this function
  95. /// returns true.
  96. ///
  97. /// try_remove_dir_recursive() assumes that no other process or thread is making
  98. /// simultaneous changes in the directory.
  99. bool try_remove_dir_recursive(const std::string& path);
  100. /// Create a new unique directory for temporary files. The absolute
  101. /// path to the new directory is returned without a trailing slash.
  102. std::string make_temp_dir();
  103. size_t page_size();
  104. /// This class provides a RAII abstraction over the concept of a file
  105. /// descriptor (or file handle).
  106. ///
  107. /// Locks are automatically and immediately released when the File
  108. /// instance is closed.
  109. ///
  110. /// You can use CloseGuard and UnlockGuard to acheive exception-safe
  111. /// closing or unlocking prior to the File instance being detroyed.
  112. ///
  113. /// A single File instance must never be accessed concurrently by
  114. /// multiple threads.
  115. ///
  116. /// You can write to a file via an std::ostream as follows:
  117. ///
  118. /// \code{.cpp}
  119. ///
  120. /// File::Streambuf my_streambuf(&my_file);
  121. /// std::ostream out(&my_strerambuf);
  122. /// out << 7945.9;
  123. ///
  124. /// \endcode
  125. class File {
  126. public:
  127. enum Mode {
  128. mode_Read, ///< access_ReadOnly, create_Never (fopen: rb)
  129. mode_Update, ///< access_ReadWrite, create_Never (fopen: rb+)
  130. mode_Write, ///< access_ReadWrite, create_Auto, flag_Trunc (fopen: wb+)
  131. mode_Append ///< access_ReadWrite, create_Auto, flag_Append (fopen: ab+)
  132. };
  133. /// Equivalent to calling open(const std::string&, Mode) on a
  134. /// default constructed instance.
  135. explicit File(const std::string& path, Mode = mode_Read);
  136. /// Create an instance that is not initially attached to an open
  137. /// file.
  138. File() = default;
  139. ~File() noexcept;
  140. File(File&&) noexcept;
  141. File& operator=(File&&) noexcept;
  142. // Disable copying by l-value. Copying an open file will create a scenario
  143. // where the same file descriptor will be opened once but closed twice.
  144. File(const File&) = delete;
  145. File& operator=(const File&) = delete;
  146. /// Calling this function on an instance that is already attached
  147. /// to an open file has undefined behavior.
  148. ///
  149. /// \throw AccessError If the file could not be opened. If the
  150. /// reason corresponds to one of the exception types that are
  151. /// derived from AccessError, the derived exception type is thrown
  152. /// (as long as the underlying system provides the information to
  153. /// unambiguously distinguish that particular reason).
  154. void open(const std::string& path, Mode = mode_Read);
  155. /// This function is idempotent, that is, it is valid to call it
  156. /// regardless of whether this instance currently is attached to
  157. /// an open file.
  158. void close() noexcept;
  159. /// Check whether this File instance is currently attached to an
  160. /// open file.
  161. bool is_attached() const noexcept;
  162. enum AccessMode {
  163. access_ReadOnly,
  164. access_ReadWrite,
  165. };
  166. enum CreateMode {
  167. create_Auto, ///< Create the file if it does not already exist.
  168. create_Never, ///< Fail if the file does not already exist.
  169. create_Must ///< Fail if the file already exists.
  170. };
  171. enum {
  172. flag_Trunc = 1, ///< Truncate the file if it already exists.
  173. flag_Append = 2 ///< Move to end of file before each write.
  174. };
  175. /// See open(const std::string&, Mode).
  176. ///
  177. /// Specifying access_ReadOnly together with a create mode that is
  178. /// not create_Never, or together with a non-zero \a flags
  179. /// argument, results in undefined behavior. Specifying flag_Trunc
  180. /// together with create_Must results in undefined behavior.
  181. void open(const std::string& path, AccessMode, CreateMode, int flags);
  182. /// Same as open(path, access_ReadWrite, create_Auto, 0), except
  183. /// that this one returns an indication of whether a new file was
  184. /// created, or an existing file was opened.
  185. void open(const std::string& path, bool& was_created);
  186. /// Read data into the specified buffer and return the number of
  187. /// bytes read. If the returned number of bytes is less than \a
  188. /// size, then the end of the file has been reached.
  189. ///
  190. /// Calling this function on an instance, that is not currently
  191. /// attached to an open file, has undefined behavior.
  192. size_t read(char* data, size_t size);
  193. static size_t read_static(FileDesc fd, char* data, size_t size);
  194. /// Write the specified data to this file.
  195. ///
  196. /// Calling this function on an instance, that is not currently
  197. /// attached to an open file, has undefined behavior.
  198. ///
  199. /// Calling this function on an instance, that was opened in
  200. /// read-only mode, has undefined behavior.
  201. void write(const char* data, size_t size);
  202. static void write_static(FileDesc fd, const char* data, size_t size);
  203. // Tells current file pointer of fd
  204. static uint64_t get_file_pos(FileDesc fd);
  205. /// Calls write(s.data(), s.size()).
  206. void write(const std::string& s)
  207. {
  208. write(s.data(), s.size());
  209. }
  210. /// Calls read(data, N).
  211. template <size_t N>
  212. size_t read(char (&data)[N])
  213. {
  214. return read(data, N);
  215. }
  216. /// Calls write(data(), N).
  217. template <size_t N>
  218. void write(const char (&data)[N])
  219. {
  220. write(data, N);
  221. }
  222. /// Plays the same role as off_t in POSIX
  223. typedef int_fast64_t SizeType;
  224. /// Calling this function on an instance that is not attached to
  225. /// an open file has undefined behavior.
  226. SizeType get_size() const;
  227. static SizeType get_size_static(FileDesc fd);
  228. static SizeType get_size_static(const std::string& path);
  229. /// If this causes the file to grow, then the new section will
  230. /// have undefined contents. Setting the size with this function
  231. /// does not necessarily allocate space on the target device. If
  232. /// you want to ensure allocation, call alloc(). Calling this
  233. /// function will generally affect the read/write offset
  234. /// associated with this File instance.
  235. ///
  236. /// Calling this function on an instance that is not attached to
  237. /// an open file has undefined behavior. Calling this function on
  238. /// a file that is opened in read-only mode, is an error.
  239. void resize(SizeType);
  240. /// Same effect as prealloc_if_supported(original_size, new_size);
  241. ///
  242. /// The downside is that this function is not guaranteed to have
  243. /// atomic behaviour on all systems, that is, two processes, or
  244. /// two threads should never call this function concurrently for
  245. /// the same underlying file even though they access the file
  246. /// through distinct File instances.
  247. ///
  248. /// \sa prealloc_if_supported()
  249. void prealloc(size_t new_size);
  250. /// When supported by the system, allocate space on the target
  251. /// device for the specified region of the file. If the region
  252. /// extends beyond the current end of the file, the file size is
  253. /// increased as necessary.
  254. ///
  255. /// On systems that do not support this operation, this function
  256. /// has no effect. You may call is_prealloc_supported() to
  257. /// determine if it is supported on your system.
  258. ///
  259. /// Calling this function on an instance, that is not attached to
  260. /// an open file, has undefined behavior. Calling this function on
  261. /// a file, that is opened in read-only mode, is an error.
  262. ///
  263. /// This function is guaranteed to have atomic behaviour, that is,
  264. /// there is never any risk of the file size being reduced even
  265. /// with concurrently executing invocations.
  266. ///
  267. /// \sa prealloc()
  268. /// \sa is_prealloc_supported()
  269. bool prealloc_if_supported(SizeType offset, size_t size);
  270. /// See prealloc_if_supported().
  271. static bool is_prealloc_supported();
  272. /// Reposition the read/write offset of this File
  273. /// instance. Distinct File instances have separate independent
  274. /// offsets, as long as the cucrrent process is not forked.
  275. void seek(SizeType);
  276. static void seek_static(FileDesc, SizeType);
  277. /// Flush in-kernel buffers to disk. This blocks the caller until the
  278. /// synchronization operation is complete. On POSIX systems this function
  279. /// calls `fsync()`. On Apple platforms if calls `fcntl()` with command
  280. /// `F_FULLFSYNC`.
  281. void sync();
  282. /// Place an exclusive lock on this file. This blocks the caller
  283. /// until all other locks have been released.
  284. ///
  285. /// Locks acquired on distinct File instances have fully recursive
  286. /// behavior, even if they are acquired in the same process (or
  287. /// thread) and are attached to the same underlying file.
  288. ///
  289. /// Calling this function on an instance that is not attached to
  290. /// an open file, or on an instance that is already locked has
  291. /// undefined behavior.
  292. void lock_exclusive();
  293. /// Place an shared lock on this file. This blocks the caller
  294. /// until all other exclusive locks have been released.
  295. ///
  296. /// Locks acquired on distinct File instances have fully recursive
  297. /// behavior, even if they are acquired in the same process (or
  298. /// thread) and are attached to the same underlying file.
  299. ///
  300. /// Calling this function on an instance that is not attached to
  301. /// an open file, or on an instance that is already locked has
  302. /// undefined behavior.
  303. void lock_shared();
  304. /// Non-blocking version of lock_exclusive(). Returns true iff it
  305. /// succeeds.
  306. bool try_lock_exclusive();
  307. /// Non-blocking version of lock_shared(). Returns true iff it
  308. /// succeeds.
  309. bool try_lock_shared();
  310. /// Release a previously acquired lock on this file. This function
  311. /// is idempotent.
  312. void unlock() noexcept;
  313. /// Set the encryption key used for this file. Must be called before any
  314. /// mappings are created or any data is read from or written to the file.
  315. ///
  316. /// \param key A 64-byte encryption key, or null to disable encryption.
  317. void set_encryption_key(const char* key);
  318. /// Get the encryption key set by set_encryption_key(),
  319. /// null_ptr if no key set.
  320. const char* get_encryption_key() const;
  321. /// Set the path used for emulating file locks. If not set explicitly,
  322. /// the emulation will use the path of the file itself suffixed by ".fifo"
  323. void set_fifo_path(const std::string& fifo_dir_path, const std::string& fifo_file_name);
  324. enum {
  325. /// If possible, disable opportunistic flushing of dirted
  326. /// pages of a memory mapped file to physical medium. On some
  327. /// systems this cannot be disabled. On other systems it is
  328. /// the default behavior. An explicit call to sync_map() will
  329. /// flush the buffers regardless of whether this flag is
  330. /// specified or not.
  331. map_NoSync = 1
  332. };
  333. /// Map this file into memory. The file is mapped as shared
  334. /// memory. This allows two processes to interact under exatly the
  335. /// same rules as applies to the interaction via regular memory of
  336. /// multiple threads inside a single process.
  337. ///
  338. /// This File instance does not need to remain in existence after
  339. /// the mapping is established.
  340. ///
  341. /// Multiple concurrent mappings may be created from the same File
  342. /// instance.
  343. ///
  344. /// Specifying access_ReadWrite for a file that is opened in
  345. /// read-only mode, is an error.
  346. ///
  347. /// Calling this function on an instance that is not attached to
  348. /// an open file, or one that is attached to an empty file has
  349. /// undefined behavior.
  350. ///
  351. /// Calling this function with a size that is greater than the
  352. /// size of the file has undefined behavior.
  353. void* map(AccessMode, size_t size, int map_flags = 0, size_t offset = 0) const;
  354. void* map_fixed(AccessMode, void* address, size_t size, int map_flags = 0, size_t offset = 0) const;
  355. void* map_reserve(AccessMode, size_t size, size_t offset) const;
  356. /// The same as unmap(old_addr, old_size) followed by map(a,
  357. /// new_size, map_flags), but more efficient on some systems.
  358. ///
  359. /// The old address range must have been acquired by a call to
  360. /// map() or remap() on this File instance, the specified access
  361. /// mode and flags must be the same as the ones specified
  362. /// previously, and this File instance must not have been reopend
  363. /// in the meantime. Failing to adhere to these rules will result
  364. /// in undefined behavior.
  365. ///
  366. /// If this function throws, the old address range will remain
  367. /// mapped.
  368. void* remap(void* old_addr, size_t old_size, AccessMode a, size_t new_size, int map_flags = 0,
  369. size_t file_offset = 0) const;
  370. #if REALM_ENABLE_ENCRYPTION
  371. void* map(AccessMode, size_t size, EncryptedFileMapping*& mapping, int map_flags = 0, size_t offset = 0) const;
  372. void* map_fixed(AccessMode, void* address, size_t size, EncryptedFileMapping* mapping, int map_flags = 0,
  373. size_t offset = 0) const;
  374. void* map_reserve(AccessMode, size_t size, size_t offset, EncryptedFileMapping*& mapping) const;
  375. #endif
  376. /// Unmap the specified address range which must have been
  377. /// previously returned by map().
  378. static void unmap(void* addr, size_t size) noexcept;
  379. /// Flush in-kernel buffers to disk. This blocks the caller until
  380. /// the synchronization operation is complete. The specified
  381. /// address range must be (a subset of) one that was previously returned by
  382. /// map().
  383. static void sync_map(FileDesc fd, void* addr, size_t size);
  384. /// Check whether the specified file or directory exists. Note
  385. /// that a file or directory that resides in a directory that the
  386. /// calling process has no access to, will necessarily be reported
  387. /// as not existing.
  388. static bool exists(const std::string& path);
  389. /// Get the time of last modification made to the file
  390. static time_t last_write_time(const std::string& path);
  391. /// Get freespace (in bytes) of filesystem containing path
  392. static SizeType get_free_space(const std::string& path);
  393. /// Check whether the specified path exists and refers to a directory. If
  394. /// the referenced file system object resides in an inaccessible directory,
  395. /// this function returns false.
  396. static bool is_dir(const std::string& path);
  397. /// Remove the specified file path from the file system. It is an error if
  398. /// the specified path is a directory. If the specified file is a symbolic
  399. /// link, the link is removed, leaving the liked file intact. In so far as
  400. /// the specified path is not a directory, std::remove(const char*) is
  401. /// equivalent to this function.
  402. ///
  403. /// The specified file must not be open by the calling process. If
  404. /// it is, this function has undefined behaviour. Note that an
  405. /// open memory map of the file counts as "the file being open".
  406. ///
  407. /// \throw AccessError If the specified directory entry could not
  408. /// be removed. If the reason corresponds to one of the exception
  409. /// types that are derived from AccessError, the derived exception
  410. /// type is thrown (as long as the underlying system provides the
  411. /// information to unambiguously distinguish that particular
  412. /// reason).
  413. static void remove(const std::string& path);
  414. /// Same as remove() except that this one returns false, rather
  415. /// than throwing an exception, if the specified file does not
  416. /// exist. If the file did exist, and was deleted, this function
  417. /// returns true.
  418. static bool try_remove(const std::string& path);
  419. /// Change the path of a directory entry. This can be used to
  420. /// rename a file, and/or to move it from one directory to
  421. /// another. This function is equivalent to std::rename(const
  422. /// char*, const char*).
  423. ///
  424. /// \throw AccessError If the path of the directory entry could
  425. /// not be changed. If the reason corresponds to one of the
  426. /// exception types that are derived from AccessError, the derived
  427. /// exception type is thrown (as long as the underlying system
  428. /// provides the information to unambiguously distinguish that
  429. /// particular reason).
  430. static void move(const std::string& old_path, const std::string& new_path);
  431. /// Copy the file at the specified origin path to the specified target path.
  432. static void copy(const std::string& origin_path, const std::string& target_path);
  433. /// Compare the two files at the specified paths for equality. Returns true
  434. /// if, and only if they are equal.
  435. static bool compare(const std::string& path_1, const std::string& path_2);
  436. /// Check whether two open file descriptors refer to the same
  437. /// underlying file, that is, if writing via one of them, will
  438. /// affect what is read from the other. In UNIX this boils down to
  439. /// comparing inode numbers.
  440. ///
  441. /// Both instances have to be attached to open files. If they are
  442. /// not, this function has undefined behavior.
  443. bool is_same_file(const File&) const;
  444. static bool is_same_file_static(FileDesc f1, FileDesc f2);
  445. // FIXME: Get rid of this method
  446. bool is_removed() const;
  447. /// Resolve the specified path against the specified base directory.
  448. ///
  449. /// If \a path is absolute, or if \a base_dir is empty, \p path is returned
  450. /// unmodified, otherwise \a path is resolved against \a base_dir.
  451. ///
  452. /// Examples (assuming POSIX):
  453. ///
  454. /// resolve("file", "dir") -> "dir/file"
  455. /// resolve("../baz", "/foo/bar") -> "/foo/baz"
  456. /// resolve("foo", ".") -> "./foo"
  457. /// resolve(".", "/foo/") -> "/foo"
  458. /// resolve("..", "foo") -> "."
  459. /// resolve("../..", "foo") -> ".."
  460. /// resolve("..", "..") -> "../.."
  461. /// resolve("", "") -> "."
  462. /// resolve("", "/") -> "/."
  463. /// resolve("..", "/") -> "/."
  464. /// resolve("..", "foo//bar") -> "foo"
  465. ///
  466. /// This function does not access the file system.
  467. ///
  468. /// \param path The path to be resolved. An empty string produces the same
  469. /// result as as if "." was passed. The result has a trailing directory
  470. /// separator (`/`) if, and only if this path has a trailing directory
  471. /// separator.
  472. ///
  473. /// \param base_dir The base directory path, which may be relative or
  474. /// absolute. A final directory separator (`/`) is optional. The empty
  475. /// string is interpreted as a relative path.
  476. static std::string resolve(const std::string& path, const std::string& base_dir);
  477. using ForEachHandler = util::FunctionRef<bool(const std::string& file, const std::string& dir)>;
  478. /// Scan the specified directory recursivle, and report each file
  479. /// (nondirectory entry) via the specified handler.
  480. ///
  481. /// The first argument passed to the handler is the name of a file (not the
  482. /// whole path), and the second argument is the directory in which that file
  483. /// resides. The directory will be specified as a path, and relative to \a
  484. /// dir_path. The directory will be the empty string for files residing
  485. /// directly in \a dir_path.
  486. ///
  487. /// If the handler returns false, scanning will be aborted immediately, and
  488. /// for_each() will return false. Otherwise for_each() will return true.
  489. ///
  490. /// Scanning is done as if by a recursive set of DirScanner objects.
  491. static bool for_each(const std::string& dir_path, ForEachHandler handler);
  492. struct UniqueID {
  493. #ifdef _WIN32 // Windows version
  494. // FIXME: This is not implemented for Windows
  495. #else
  496. UniqueID()
  497. : device(0)
  498. , inode(0)
  499. {
  500. }
  501. UniqueID(dev_t d, ino_t i)
  502. : device(d)
  503. , inode(i)
  504. {
  505. }
  506. // NDK r10e has a bug in sys/stat.h dev_t ino_t are 4 bytes,
  507. // but stat.st_dev and st_ino are 8 bytes. So we just use uint64 instead.
  508. dev_t device;
  509. uint_fast64_t inode;
  510. #endif
  511. };
  512. // Return the unique id for the current opened file descriptor.
  513. // Same UniqueID means they are the same file.
  514. UniqueID get_unique_id() const;
  515. // Return the file descriptor for the file
  516. FileDesc get_descriptor() const;
  517. // Return the path of the open file, or an empty string if
  518. // this file has never been opened.
  519. std::string get_path() const;
  520. // Return false if the file doesn't exist. Otherwise uid will be set.
  521. static bool get_unique_id(const std::string& path, UniqueID& uid);
  522. class ExclusiveLock;
  523. class SharedLock;
  524. template <class>
  525. class Map;
  526. class CloseGuard;
  527. class UnlockGuard;
  528. class UnmapGuard;
  529. class Streambuf;
  530. // Exceptions
  531. class AccessError;
  532. class PermissionDenied;
  533. class NotFound;
  534. class Exists;
  535. private:
  536. #ifdef _WIN32
  537. void* m_fd = nullptr;
  538. bool m_have_lock = false; // Only valid when m_fd is not null
  539. #else
  540. int m_fd = -1;
  541. #ifdef REALM_FILELOCK_EMULATION
  542. int m_pipe_fd = -1; // -1 if no pipe has been allocated for emulation
  543. bool m_has_exclusive_lock = false;
  544. std::string m_fifo_dir_path;
  545. std::string m_fifo_path;
  546. #endif
  547. #endif
  548. std::unique_ptr<const char[]> m_encryption_key = nullptr;
  549. std::string m_path;
  550. bool lock(bool exclusive, bool non_blocking);
  551. void open_internal(const std::string& path, AccessMode, CreateMode, int flags, bool* success);
  552. #ifdef REALM_FILELOCK_EMULATION
  553. bool has_shared_lock() const noexcept
  554. {
  555. return m_pipe_fd != -1;
  556. }
  557. #endif
  558. struct MapBase {
  559. void* m_addr = nullptr;
  560. mutable size_t m_size = 0;
  561. size_t m_offset = 0;
  562. FileDesc m_fd;
  563. MapBase() noexcept;
  564. ~MapBase() noexcept;
  565. // Disable copying. Copying an opened MapBase will create a scenario
  566. // where the same memory will be mapped once but unmapped twice.
  567. MapBase(const MapBase&) = delete;
  568. MapBase& operator=(const MapBase&) = delete;
  569. // Use
  570. void map(const File&, AccessMode, size_t size, int map_flags, size_t offset = 0);
  571. void remap(const File&, AccessMode, size_t size, int map_flags);
  572. void unmap() noexcept;
  573. void sync();
  574. #if REALM_ENABLE_ENCRYPTION
  575. mutable util::EncryptedFileMapping* m_encrypted_mapping = nullptr;
  576. inline util::EncryptedFileMapping* get_encrypted_mapping() const
  577. {
  578. return m_encrypted_mapping;
  579. }
  580. #else
  581. inline util::EncryptedFileMapping* get_encrypted_mapping() const
  582. {
  583. return nullptr;
  584. }
  585. #endif
  586. };
  587. };
  588. class File::ExclusiveLock {
  589. public:
  590. ExclusiveLock(File& f)
  591. : m_file(f)
  592. {
  593. f.lock_exclusive();
  594. }
  595. ~ExclusiveLock() noexcept
  596. {
  597. m_file.unlock();
  598. }
  599. // Disable copying. It is not how this class should be used.
  600. ExclusiveLock(const ExclusiveLock&) = delete;
  601. ExclusiveLock& operator=(const ExclusiveLock&) = delete;
  602. private:
  603. File& m_file;
  604. };
  605. class File::SharedLock {
  606. public:
  607. SharedLock(File& f)
  608. : m_file(f)
  609. {
  610. f.lock_shared();
  611. }
  612. ~SharedLock() noexcept
  613. {
  614. m_file.unlock();
  615. }
  616. // Disable copying. It is not how this class should be used.
  617. SharedLock(const SharedLock&) = delete;
  618. SharedLock& operator=(const SharedLock&) = delete;
  619. private:
  620. File& m_file;
  621. };
  622. /// This class provides a RAII abstraction over the concept of a
  623. /// memory mapped file.
  624. ///
  625. /// Once created, the Map instance makes no reference to the File
  626. /// instance that it was based upon, and that File instance may be
  627. /// destroyed before the Map instance is destroyed.
  628. ///
  629. /// Multiple concurrent mappings may be created from the same File
  630. /// instance.
  631. ///
  632. /// You can use UnmapGuard to acheive exception-safe unmapping prior
  633. /// to the Map instance being detroyed.
  634. ///
  635. /// A single Map instance must never be accessed concurrently by
  636. /// multiple threads.
  637. template <class T>
  638. class File::Map : private MapBase {
  639. public:
  640. /// Equivalent to calling map() on a default constructed instance.
  641. explicit Map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0);
  642. explicit Map(const File&, size_t offset, AccessMode = access_ReadOnly, size_t size = sizeof(T),
  643. int map_flags = 0);
  644. /// Create an instance that is not initially attached to a memory
  645. /// mapped file.
  646. Map() noexcept;
  647. ~Map() noexcept;
  648. // Disable copying. Copying an opened Map will create a scenario
  649. // where the same memory will be mapped once but unmapped twice.
  650. Map(const Map&) = delete;
  651. Map& operator=(const Map&) = delete;
  652. /// Move the mapping from another Map object to this Map object
  653. File::Map<T>& operator=(File::Map<T>&& other) noexcept
  654. {
  655. REALM_ASSERT(this != &other);
  656. if (m_addr)
  657. unmap();
  658. m_addr = other.get_addr();
  659. m_size = other.m_size;
  660. m_offset = other.m_offset;
  661. m_fd = other.m_fd;
  662. other.m_offset = 0;
  663. other.m_addr = nullptr;
  664. other.m_size = 0;
  665. #if REALM_ENABLE_ENCRYPTION
  666. m_encrypted_mapping = other.m_encrypted_mapping;
  667. other.m_encrypted_mapping = nullptr;
  668. #endif
  669. return *this;
  670. }
  671. Map(Map&& other) noexcept
  672. {
  673. *this = std::move(other);
  674. }
  675. /// See File::map().
  676. ///
  677. /// Calling this function on a Map instance that is already
  678. /// attached to a memory mapped file has undefined behavior. The
  679. /// returned pointer is the same as what will subsequently be
  680. /// returned by get_addr().
  681. T* map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0, size_t offset = 0);
  682. /// See File::unmap(). This function is idempotent, that is, it is
  683. /// valid to call it regardless of whether this instance is
  684. /// currently attached to a memory mapped file.
  685. void unmap() noexcept;
  686. /// See File::remap().
  687. ///
  688. /// Calling this function on a Map instance that is not currently
  689. /// attached to a memory mapped file has undefined behavior. The
  690. /// returned pointer is the same as what will subsequently be
  691. /// returned by get_addr().
  692. T* remap(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0);
  693. /// See File::sync_map().
  694. ///
  695. /// Calling this function on an instance that is not currently
  696. /// attached to a memory mapped file, has undefined behavior.
  697. void sync();
  698. /// Check whether this Map instance is currently attached to a
  699. /// memory mapped file.
  700. bool is_attached() const noexcept;
  701. /// Returns a pointer to the beginning of the memory mapped file,
  702. /// or null if this instance is not currently attached.
  703. T* get_addr() const noexcept;
  704. /// Returns the size of the mapped region, or zero if this
  705. /// instance does not currently refer to a memory mapped
  706. /// file. When this instance refers to a memory mapped file, the
  707. /// returned value will always be identical to the size passed to
  708. /// the constructor or to map().
  709. size_t get_size() const noexcept;
  710. /// Release the currently attached memory mapped file from this
  711. /// Map instance. The address range may then be unmapped later by
  712. /// a call to File::unmap().
  713. T* release() noexcept;
  714. #if REALM_ENABLE_ENCRYPTION
  715. /// Get the encrypted file mapping corresponding to this mapping
  716. inline EncryptedFileMapping* get_encrypted_mapping() const
  717. {
  718. return m_encrypted_mapping;
  719. }
  720. #else
  721. inline EncryptedFileMapping* get_encrypted_mapping() const
  722. {
  723. return nullptr;
  724. }
  725. #endif
  726. friend class UnmapGuard;
  727. };
  728. class File::CloseGuard {
  729. public:
  730. CloseGuard(File& f) noexcept
  731. : m_file(&f)
  732. {
  733. }
  734. ~CloseGuard() noexcept
  735. {
  736. if (m_file)
  737. m_file->close();
  738. }
  739. void release() noexcept
  740. {
  741. m_file = nullptr;
  742. }
  743. // Disallow the default implementation of copy/assign, this is not how this
  744. // class is intended to be used. For example we could get unexpected
  745. // behaviour if one CloseGuard is copied and released but the other is not.
  746. CloseGuard(const CloseGuard&) = delete;
  747. CloseGuard& operator=(const CloseGuard&) = delete;
  748. private:
  749. File* m_file;
  750. };
  751. class File::UnlockGuard {
  752. public:
  753. UnlockGuard(File& f) noexcept
  754. : m_file(&f)
  755. {
  756. }
  757. ~UnlockGuard() noexcept
  758. {
  759. if (m_file)
  760. m_file->unlock();
  761. }
  762. void release() noexcept
  763. {
  764. m_file = nullptr;
  765. }
  766. // Disallow the default implementation of copy/assign, this is not how this
  767. // class is intended to be used. For example we could get unexpected
  768. // behaviour if one UnlockGuard is copied and released but the other is not.
  769. UnlockGuard(const UnlockGuard&) = delete;
  770. UnlockGuard& operator=(const UnlockGuard&) = delete;
  771. private:
  772. File* m_file;
  773. };
  774. class File::UnmapGuard {
  775. public:
  776. template <class T>
  777. UnmapGuard(Map<T>& m) noexcept
  778. : m_map(&m)
  779. {
  780. }
  781. ~UnmapGuard() noexcept
  782. {
  783. if (m_map)
  784. m_map->unmap();
  785. }
  786. void release() noexcept
  787. {
  788. m_map = nullptr;
  789. }
  790. // Disallow the default implementation of copy/assign, this is not how this
  791. // class is intended to be used. For example we could get unexpected
  792. // behaviour if one UnmapGuard is copied and released but the other is not.
  793. UnmapGuard(const UnmapGuard&) = delete;
  794. UnmapGuard& operator=(const UnmapGuard&) = delete;
  795. private:
  796. MapBase* m_map;
  797. };
  798. /// Only output is supported at this point.
  799. class File::Streambuf : public std::streambuf {
  800. public:
  801. explicit Streambuf(File*, size_t = 4096);
  802. ~Streambuf() noexcept;
  803. // Disable copying
  804. Streambuf(const Streambuf&) = delete;
  805. Streambuf& operator=(const Streambuf&) = delete;
  806. private:
  807. File& m_file;
  808. std::unique_ptr<char[]> const m_buffer;
  809. int_type overflow(int_type) override;
  810. int sync() override;
  811. pos_type seekpos(pos_type, std::ios_base::openmode) override;
  812. void flush();
  813. };
  814. /// Used for any I/O related exception. Note the derived exception
  815. /// types that are used for various specific types of errors.
  816. class File::AccessError : public ExceptionWithBacktrace<std::runtime_error> {
  817. public:
  818. AccessError(const std::string& msg, const std::string& path);
  819. /// Return the associated file system path, or the empty string if there is
  820. /// no associated file system path, or if the file system path is unknown.
  821. const std::string& get_path() const;
  822. void set_path(std::string path)
  823. {
  824. m_path = std::move(path);
  825. }
  826. const char* message() const noexcept
  827. {
  828. m_buffer = std::runtime_error::what();
  829. if (m_path.size() > 0)
  830. m_buffer += (std::string(" Path: ") + m_path);
  831. return m_buffer.c_str();
  832. }
  833. private:
  834. std::string m_path;
  835. mutable std::string m_buffer;
  836. };
  837. /// Thrown if the user does not have permission to open or create
  838. /// the specified file in the specified access mode.
  839. class File::PermissionDenied : public AccessError {
  840. public:
  841. PermissionDenied(const std::string& msg, const std::string& path);
  842. };
  843. /// Thrown if the directory part of the specified path was not
  844. /// found, or create_Never was specified and the file did no
  845. /// exist.
  846. class File::NotFound : public AccessError {
  847. public:
  848. NotFound(const std::string& msg, const std::string& path);
  849. };
  850. /// Thrown if create_Always was specified and the file did already
  851. /// exist.
  852. class File::Exists : public AccessError {
  853. public:
  854. Exists(const std::string& msg, const std::string& path);
  855. };
  856. class DirScanner {
  857. public:
  858. DirScanner(const std::string& path, bool allow_missing = false);
  859. ~DirScanner() noexcept;
  860. bool next(std::string& name);
  861. private:
  862. #ifndef _WIN32
  863. DIR* m_dirp;
  864. #elif REALM_HAVE_STD_FILESYSTEM
  865. std::filesystem::directory_iterator m_iterator;
  866. #endif
  867. };
  868. // Implementation:
  869. inline File::File(const std::string& path, Mode m)
  870. {
  871. open(path, m);
  872. }
  873. inline File::~File() noexcept
  874. {
  875. close();
  876. }
  877. inline void File::set_fifo_path(const std::string& fifo_dir_path, const std::string& fifo_file_name)
  878. {
  879. #ifdef REALM_FILELOCK_EMULATION
  880. m_fifo_dir_path = fifo_dir_path;
  881. m_fifo_path = fifo_dir_path + "/" + fifo_file_name;
  882. #else
  883. static_cast<void>(fifo_dir_path);
  884. static_cast<void>(fifo_file_name);
  885. #endif
  886. }
  887. inline File::File(File&& f) noexcept
  888. {
  889. #ifdef _WIN32
  890. m_fd = f.m_fd;
  891. m_have_lock = f.m_have_lock;
  892. f.m_fd = nullptr;
  893. #else
  894. m_fd = f.m_fd;
  895. #ifdef REALM_FILELOCK_EMULATION
  896. m_pipe_fd = f.m_pipe_fd;
  897. m_has_exclusive_lock = f.m_has_exclusive_lock;
  898. f.m_has_exclusive_lock = false;
  899. f.m_pipe_fd = -1;
  900. #endif
  901. f.m_fd = -1;
  902. #endif
  903. m_encryption_key = std::move(f.m_encryption_key);
  904. }
  905. inline File& File::operator=(File&& f) noexcept
  906. {
  907. close();
  908. #ifdef _WIN32
  909. m_fd = f.m_fd;
  910. m_have_lock = f.m_have_lock;
  911. f.m_fd = nullptr;
  912. #else
  913. m_fd = f.m_fd;
  914. f.m_fd = -1;
  915. #ifdef REALM_FILELOCK_EMULATION
  916. m_pipe_fd = f.m_pipe_fd;
  917. f.m_pipe_fd = -1;
  918. m_has_exclusive_lock = f.m_has_exclusive_lock;
  919. f.m_has_exclusive_lock = false;
  920. #endif
  921. #endif
  922. m_encryption_key = std::move(f.m_encryption_key);
  923. return *this;
  924. }
  925. inline void File::open(const std::string& path, Mode m)
  926. {
  927. AccessMode a = access_ReadWrite;
  928. CreateMode c = create_Auto;
  929. int flags = 0;
  930. switch (m) {
  931. case mode_Read:
  932. a = access_ReadOnly;
  933. c = create_Never;
  934. break;
  935. case mode_Update:
  936. c = create_Never;
  937. break;
  938. case mode_Write:
  939. flags = flag_Trunc;
  940. break;
  941. case mode_Append:
  942. flags = flag_Append;
  943. break;
  944. }
  945. open(path, a, c, flags);
  946. }
  947. inline void File::open(const std::string& path, AccessMode am, CreateMode cm, int flags)
  948. {
  949. open_internal(path, am, cm, flags, nullptr);
  950. }
  951. inline void File::open(const std::string& path, bool& was_created)
  952. {
  953. while (1) {
  954. bool success;
  955. open_internal(path, access_ReadWrite, create_Must, 0, &success);
  956. if (success) {
  957. was_created = true;
  958. return;
  959. }
  960. open_internal(path, access_ReadWrite, create_Never, 0, &success);
  961. if (success) {
  962. was_created = false;
  963. return;
  964. }
  965. }
  966. }
  967. inline bool File::is_attached() const noexcept
  968. {
  969. #ifdef _WIN32
  970. return (m_fd != nullptr);
  971. #else
  972. return 0 <= m_fd;
  973. #endif
  974. }
  975. inline void File::lock_exclusive()
  976. {
  977. lock(true, false);
  978. }
  979. inline void File::lock_shared()
  980. {
  981. lock(false, false);
  982. }
  983. inline bool File::try_lock_exclusive()
  984. {
  985. return lock(true, true);
  986. }
  987. inline bool File::try_lock_shared()
  988. {
  989. return lock(false, true);
  990. }
  991. inline File::MapBase::MapBase() noexcept
  992. {
  993. m_addr = nullptr;
  994. m_size = 0;
  995. }
  996. inline File::MapBase::~MapBase() noexcept
  997. {
  998. unmap();
  999. }
  1000. template <class T>
  1001. inline File::Map<T>::Map(const File& f, AccessMode a, size_t size, int map_flags)
  1002. {
  1003. map(f, a, size, map_flags);
  1004. }
  1005. template <class T>
  1006. inline File::Map<T>::Map(const File& f, size_t offset, AccessMode a, size_t size, int map_flags)
  1007. {
  1008. map(f, a, size, map_flags, offset);
  1009. }
  1010. template <class T>
  1011. inline File::Map<T>::Map() noexcept
  1012. {
  1013. }
  1014. template <class T>
  1015. inline File::Map<T>::~Map() noexcept
  1016. {
  1017. }
  1018. template <class T>
  1019. inline T* File::Map<T>::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset)
  1020. {
  1021. MapBase::map(f, a, size, map_flags, offset);
  1022. return static_cast<T*>(m_addr);
  1023. }
  1024. template <class T>
  1025. inline void File::Map<T>::unmap() noexcept
  1026. {
  1027. MapBase::unmap();
  1028. }
  1029. template <class T>
  1030. inline T* File::Map<T>::remap(const File& f, AccessMode a, size_t size, int map_flags)
  1031. {
  1032. // MapBase::remap(f, a, size, map_flags);
  1033. // missing sync() here?
  1034. unmap();
  1035. map(f, a, size, map_flags);
  1036. return static_cast<T*>(m_addr);
  1037. }
  1038. template <class T>
  1039. inline void File::Map<T>::sync()
  1040. {
  1041. MapBase::sync();
  1042. }
  1043. template <class T>
  1044. inline bool File::Map<T>::is_attached() const noexcept
  1045. {
  1046. return (m_addr != nullptr);
  1047. }
  1048. template <class T>
  1049. inline T* File::Map<T>::get_addr() const noexcept
  1050. {
  1051. return static_cast<T*>(m_addr);
  1052. }
  1053. template <class T>
  1054. inline size_t File::Map<T>::get_size() const noexcept
  1055. {
  1056. return m_addr ? m_size : 0;
  1057. }
  1058. template <class T>
  1059. inline T* File::Map<T>::release() noexcept
  1060. {
  1061. T* addr = static_cast<T*>(m_addr);
  1062. m_addr = nullptr;
  1063. m_fd = 0;
  1064. return addr;
  1065. }
  1066. inline File::Streambuf::Streambuf(File* f, size_t buffer_size)
  1067. : m_file(*f)
  1068. , m_buffer(new char[buffer_size])
  1069. {
  1070. char* b = m_buffer.get();
  1071. setp(b, b + buffer_size);
  1072. }
  1073. inline File::Streambuf::~Streambuf() noexcept
  1074. {
  1075. try {
  1076. if (m_file.is_attached())
  1077. flush();
  1078. }
  1079. catch (...) {
  1080. // Errors deliberately ignored
  1081. }
  1082. }
  1083. inline File::Streambuf::int_type File::Streambuf::overflow(int_type c)
  1084. {
  1085. flush();
  1086. if (c == traits_type::eof())
  1087. return traits_type::not_eof(c);
  1088. *pptr() = traits_type::to_char_type(c);
  1089. pbump(1);
  1090. return c;
  1091. }
  1092. inline int File::Streambuf::sync()
  1093. {
  1094. flush();
  1095. return 0;
  1096. }
  1097. inline File::Streambuf::pos_type File::Streambuf::seekpos(pos_type pos, std::ios_base::openmode)
  1098. {
  1099. flush();
  1100. SizeType pos2 = 0;
  1101. if (int_cast_with_overflow_detect(std::streamsize(pos), pos2))
  1102. throw util::overflow_error("Seek position overflow");
  1103. m_file.seek(pos2);
  1104. return pos;
  1105. }
  1106. inline void File::Streambuf::flush()
  1107. {
  1108. size_t n = pptr() - pbase();
  1109. if (n > 0) {
  1110. m_file.write(pbase(), n);
  1111. setp(m_buffer.get(), epptr());
  1112. }
  1113. }
  1114. inline File::AccessError::AccessError(const std::string& msg, const std::string& path)
  1115. : ExceptionWithBacktrace<std::runtime_error>(msg)
  1116. , m_path(path)
  1117. {
  1118. }
  1119. inline const std::string& File::AccessError::get_path() const
  1120. {
  1121. return m_path;
  1122. }
  1123. inline File::PermissionDenied::PermissionDenied(const std::string& msg, const std::string& path)
  1124. : AccessError(msg, path)
  1125. {
  1126. }
  1127. inline File::NotFound::NotFound(const std::string& msg, const std::string& path)
  1128. : AccessError(msg, path)
  1129. {
  1130. }
  1131. inline File::Exists::Exists(const std::string& msg, const std::string& path)
  1132. : AccessError(msg, path)
  1133. {
  1134. }
  1135. inline bool operator==(const File::UniqueID& lhs, const File::UniqueID& rhs)
  1136. {
  1137. #ifdef _WIN32 // Windows version
  1138. throw util::runtime_error("Not yet supported");
  1139. #else // POSIX version
  1140. return lhs.device == rhs.device && lhs.inode == rhs.inode;
  1141. #endif
  1142. }
  1143. inline bool operator!=(const File::UniqueID& lhs, const File::UniqueID& rhs)
  1144. {
  1145. return !(lhs == rhs);
  1146. }
  1147. inline bool operator<(const File::UniqueID& lhs, const File::UniqueID& rhs)
  1148. {
  1149. #ifdef _WIN32 // Windows version
  1150. throw util::runtime_error("Not yet supported");
  1151. #else // POSIX version
  1152. if (lhs.device < rhs.device)
  1153. return true;
  1154. if (lhs.device > rhs.device)
  1155. return false;
  1156. if (lhs.inode < rhs.inode)
  1157. return true;
  1158. return false;
  1159. #endif
  1160. }
  1161. inline bool operator>(const File::UniqueID& lhs, const File::UniqueID& rhs)
  1162. {
  1163. return rhs < lhs;
  1164. }
  1165. inline bool operator<=(const File::UniqueID& lhs, const File::UniqueID& rhs)
  1166. {
  1167. return !(lhs > rhs);
  1168. }
  1169. inline bool operator>=(const File::UniqueID& lhs, const File::UniqueID& rhs)
  1170. {
  1171. return !(lhs < rhs);
  1172. }
  1173. } // namespace util
  1174. } // namespace realm
  1175. #endif // REALM_UTIL_FILE_HPP