file.hpp 43 KB

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