1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 **************************************************************************/
19 #ifndef REALM_UTIL_FILE_HPP
20 #define REALM_UTIL_FILE_HPP
31 #include <dirent.h> // POSIX.1-2001
34 #if defined(_MSC_VER) && _MSC_VER >= 1900 // compiling with at least Visual Studio 2015
35 #include <experimental/filesystem>
37 namespace filesystem = std::experimental::filesystem::v1;
39 #define REALM_HAVE_STD_FILESYSTEM 1
41 #define REALM_HAVE_STD_FILESYSTEM 0
44 #include <realm/utilities.hpp>
45 #include <realm/util/features.h>
46 #include <realm/util/assert.hpp>
47 #include <realm/util/safe_int_ops.hpp>
53 class EncryptedFileMapping;
55 /// Create the specified directory in the file system.
57 /// \throw File::AccessError If the directory could not be created. If
58 /// the reason corresponds to one of the exception types that are
59 /// derived from File::AccessError, the derived exception type is
60 /// thrown (as long as the underlying system provides the information
61 /// to unambiguously distinguish that particular reason).
62 void make_dir(const std::string& path);
64 /// Same as make_dir() except that this one returns false, rather than throwing
65 /// an exception, if the specified directory already existed. If the directory
66 // did not already exist and was newly created, this returns true.
67 bool try_make_dir(const std::string& path);
69 /// Remove the specified empty directory path from the file system. It is an
70 /// error if the specified path is not a directory, or if it is a nonempty
71 /// directory. In so far as the specified path is a directory, std::remove(const
72 /// char*) is equivalent to this function.
74 /// \throw File::AccessError If the directory could not be removed. If the
75 /// reason corresponds to one of the exception types that are derived from
76 /// File::AccessError, the derived exception type is thrown (as long as the
77 /// underlying system provides the information to unambiguously distinguish that
78 /// particular reason).
79 void remove_dir(const std::string& path);
81 /// Same as remove_dir() except that this one returns false, rather
82 /// than throwing an exception, if the specified directory did not
83 /// exist. If the directory did exist, and was deleted, this function
85 bool try_remove_dir(const std::string& path);
87 /// Remove the specified directory after removing all its contents. Files
88 /// (nondirectory entries) will be removed as if by a call to File::remove(),
89 /// and empty directories as if by a call to remove_dir().
91 /// \throw File::AccessError If removal of the directory, or any of its contents
94 /// remove_dir_recursive() assumes that no other process or thread is making
95 /// simultaneous changes in the directory.
96 void remove_dir_recursive(const std::string& path);
98 /// Same as remove_dir_recursive() except that this one returns false, rather
99 /// than throwing an exception, if the specified directory did not
100 /// exist. If the directory did exist, and was deleted, this function
103 /// try_remove_dir_recursive() assumes that no other process or thread is making
104 /// simultaneous changes in the directory.
105 bool try_remove_dir_recursive(const std::string& path);
107 /// Create a new unique directory for temporary files. The absolute
108 /// path to the new directory is returned without a trailing slash.
109 std::string make_temp_dir();
114 /// This class provides a RAII abstraction over the concept of a file
115 /// descriptor (or file handle).
117 /// Locks are automatically and immediately released when the File
118 /// instance is closed.
120 /// You can use CloseGuard and UnlockGuard to acheive exception-safe
121 /// closing or unlocking prior to the File instance being detroyed.
123 /// A single File instance must never be accessed concurrently by
124 /// multiple threads.
126 /// You can write to a file via an std::ostream as follows:
130 /// File::Streambuf my_streambuf(&my_file);
131 /// std::ostream out(&my_strerambuf);
138 mode_Read, ///< access_ReadOnly, create_Never (fopen: rb)
139 mode_Update, ///< access_ReadWrite, create_Never (fopen: rb+)
140 mode_Write, ///< access_ReadWrite, create_Auto, flag_Trunc (fopen: wb+)
141 mode_Append ///< access_ReadWrite, create_Auto, flag_Append (fopen: ab+)
144 /// Equivalent to calling open(const std::string&, Mode) on a
145 /// default constructed instance.
146 explicit File(const std::string& path, Mode = mode_Read);
148 /// Create an instance that is not initially attached to an open
154 File(File&&) noexcept;
155 File& operator=(File&&) noexcept;
157 // Disable copying by l-value. Copying an open file will create a scenario
158 // where the same file descriptor will be opened once but closed twice.
159 File(const File&) = delete;
160 File& operator=(const File&) = delete;
162 /// Calling this function on an instance that is already attached
163 /// to an open file has undefined behavior.
165 /// \throw AccessError If the file could not be opened. If the
166 /// reason corresponds to one of the exception types that are
167 /// derived from AccessError, the derived exception type is thrown
168 /// (as long as the underlying system provides the information to
169 /// unambiguously distinguish that particular reason).
170 void open(const std::string& path, Mode = mode_Read);
172 /// This function is idempotent, that is, it is valid to call it
173 /// regardless of whether this instance currently is attached to
175 void close() noexcept;
177 /// Check whether this File instance is currently attached to an
179 bool is_attached() const noexcept;
187 create_Auto, ///< Create the file if it does not already exist.
188 create_Never, ///< Fail if the file does not already exist.
189 create_Must ///< Fail if the file already exists.
193 flag_Trunc = 1, ///< Truncate the file if it already exists.
194 flag_Append = 2 ///< Move to end of file before each write.
197 /// See open(const std::string&, Mode).
199 /// Specifying access_ReadOnly together with a create mode that is
200 /// not create_Never, or together with a non-zero \a flags
201 /// argument, results in undefined behavior. Specifying flag_Trunc
202 /// together with create_Must results in undefined behavior.
203 void open(const std::string& path, AccessMode, CreateMode, int flags);
205 /// Same as open(path, access_ReadWrite, create_Auto, 0), except
206 /// that this one returns an indication of whether a new file was
207 /// created, or an existing file was opened.
208 void open(const std::string& path, bool& was_created);
210 /// Read data into the specified buffer and return the number of
211 /// bytes read. If the returned number of bytes is less than \a
212 /// size, then the end of the file has been reached.
214 /// Calling this function on an instance, that is not currently
215 /// attached to an open file, has undefined behavior.
216 size_t read(char* data, size_t size);
217 static size_t read_static(FileDesc fd, char* data, size_t size);
219 /// Write the specified data to this file.
221 /// Calling this function on an instance, that is not currently
222 /// attached to an open file, has undefined behavior.
224 /// Calling this function on an instance, that was opened in
225 /// read-only mode, has undefined behavior.
226 void write(const char* data, size_t size);
227 static void write_static(FileDesc fd, const char* data, size_t size);
229 // Tells current file pointer of fd
230 static uint64_t get_file_pos(FileDesc fd);
232 /// Calls write(s.data(), s.size()).
233 void write(const std::string& s)
235 write(s.data(), s.size());
238 /// Calls read(data, N).
240 size_t read(char (&data)[N])
242 return read(data, N);
245 /// Calls write(data(), N).
247 void write(const char (&data)[N])
252 /// Plays the same role as off_t in POSIX
253 typedef int_fast64_t SizeType;
255 /// Calling this function on an instance that is not attached to
256 /// an open file has undefined behavior.
257 SizeType get_size() const;
258 static SizeType get_size_static(FileDesc fd);
260 /// If this causes the file to grow, then the new section will
261 /// have undefined contents. Setting the size with this function
262 /// does not necessarily allocate space on the target device. If
263 /// you want to ensure allocation, call alloc(). Calling this
264 /// function will generally affect the read/write offset
265 /// associated with this File instance.
267 /// Calling this function on an instance that is not attached to
268 /// an open file has undefined behavior. Calling this function on
269 /// a file that is opened in read-only mode, is an error.
270 void resize(SizeType);
272 /// The same as prealloc_if_supported() but when the operation is
273 /// not supported by the system, this function will still increase
274 /// the file size when the specified region extends beyond the
275 /// current end of the file. This allows you to both extend and
276 /// allocate in one operation.
278 /// The downside is that this function is not guaranteed to have
279 /// atomic behaviour on all systems, that is, two processes, or
280 /// two threads should never call this function concurrently for
281 /// the same underlying file even though they access the file
282 /// through distinct File instances.
284 /// \sa prealloc_if_supported()
285 void prealloc(SizeType offset, size_t size);
287 /// When supported by the system, allocate space on the target
288 /// device for the specified region of the file. If the region
289 /// extends beyond the current end of the file, the file size is
290 /// increased as necessary.
292 /// On systems that do not support this operation, this function
293 /// has no effect. You may call is_prealloc_supported() to
294 /// determine if it is supported on your system.
296 /// Calling this function on an instance, that is not attached to
297 /// an open file, has undefined behavior. Calling this function on
298 /// a file, that is opened in read-only mode, is an error.
300 /// This function is guaranteed to have atomic behaviour, that is,
301 /// there is never any risk of the file size being reduced even
302 /// with concurrently executing invocations.
305 /// \sa is_prealloc_supported()
306 void prealloc_if_supported(SizeType offset, size_t size);
308 /// See prealloc_if_supported().
309 static bool is_prealloc_supported();
311 /// Reposition the read/write offset of this File
312 /// instance. Distinct File instances have separate independent
313 /// offsets, as long as the cucrrent process is not forked.
315 static void seek_static(FileDesc, SizeType);
317 /// Flush in-kernel buffers to disk. This blocks the caller until the
318 /// synchronization operation is complete. On POSIX systems this function
319 /// calls `fsync()`. On Apple platforms if calls `fcntl()` with command
323 /// Place an exclusive lock on this file. This blocks the caller
324 /// until all other locks have been released.
326 /// Locks acquired on distinct File instances have fully recursive
327 /// behavior, even if they are acquired in the same process (or
328 /// thread) and are attached to the same underlying file.
330 /// Calling this function on an instance that is not attached to
331 /// an open file, or on an instance that is already locked has
332 /// undefined behavior.
333 void lock_exclusive();
335 /// Place an shared lock on this file. This blocks the caller
336 /// until all other exclusive locks have been released.
338 /// Locks acquired on distinct File instances have fully recursive
339 /// behavior, even if they are acquired in the same process (or
340 /// thread) and are attached to the same underlying file.
342 /// Calling this function on an instance that is not attached to
343 /// an open file, or on an instance that is already locked has
344 /// undefined behavior.
347 /// Non-blocking version of lock_exclusive(). Returns true iff it
349 bool try_lock_exclusive();
351 /// Non-blocking version of lock_shared(). Returns true iff it
353 bool try_lock_shared();
355 /// Release a previously acquired lock on this file. This function
357 void unlock() noexcept;
359 /// Set the encryption key used for this file. Must be called before any
360 /// mappings are created or any data is read from or written to the file.
362 /// \param key A 64-byte encryption key, or null to disable encryption.
363 void set_encryption_key(const char* key);
365 /// Get the encryption key set by set_encryption_key(),
366 /// null_ptr if no key set.
367 const char* get_encryption_key();
369 /// If possible, disable opportunistic flushing of dirted
370 /// pages of a memory mapped file to physical medium. On some
371 /// systems this cannot be disabled. On other systems it is
372 /// the default behavior. An explicit call to sync_map() will
373 /// flush the buffers regardless of whether this flag is
374 /// specified or not.
378 /// Map this file into memory. The file is mapped as shared
379 /// memory. This allows two processes to interact under exatly the
380 /// same rules as applies to the interaction via regular memory of
381 /// multiple threads inside a single process.
383 /// This File instance does not need to remain in existence after
384 /// the mapping is established.
386 /// Multiple concurrent mappings may be created from the same File
389 /// Specifying access_ReadWrite for a file that is opened in
390 /// read-only mode, is an error.
392 /// Calling this function on an instance that is not attached to
393 /// an open file, or one that is attached to an empty file has
394 /// undefined behavior.
396 /// Calling this function with a size that is greater than the
397 /// size of the file has undefined behavior.
398 void* map(AccessMode, size_t size, int map_flags = 0, size_t offset = 0) const;
400 /// The same as unmap(old_addr, old_size) followed by map(a,
401 /// new_size, map_flags), but more efficient on some systems.
403 /// The old address range must have been acquired by a call to
404 /// map() or remap() on this File instance, the specified access
405 /// mode and flags must be the same as the ones specified
406 /// previously, and this File instance must not have been reopend
407 /// in the meantime. Failing to adhere to these rules will result
408 /// in undefined behavior.
410 /// If this function throws, the old address range will remain
412 void* remap(void* old_addr, size_t old_size, AccessMode a, size_t new_size, int map_flags = 0,
413 size_t file_offset = 0) const;
415 #if REALM_ENABLE_ENCRYPTION
416 void* map(AccessMode, size_t size, EncryptedFileMapping*& mapping, int map_flags = 0, size_t offset = 0) const;
418 /// Unmap the specified address range which must have been
419 /// previously returned by map().
420 static void unmap(void* addr, size_t size) noexcept;
422 /// Flush in-kernel buffers to disk. This blocks the caller until
423 /// the synchronization operation is complete. The specified
424 /// address range must be (a subset of) one that was previously returned by
426 static void sync_map(FileDesc fd, void* addr, size_t size);
428 /// Check whether the specified file or directory exists. Note
429 /// that a file or directory that resides in a directory that the
430 /// calling process has no access to, will necessarily be reported
432 static bool exists(const std::string& path);
434 /// Check whether the specified path exists and refers to a directory. If
435 /// the referenced file system object resides in an inaccessible directory,
436 /// this function returns false.
437 static bool is_dir(const std::string& path);
439 /// Remove the specified file path from the file system. It is an error if
440 /// the specified path is a directory. If the specified file is a symbolic
441 /// link, the link is removed, leaving the liked file intact. In so far as
442 /// the specified path is not a directory, std::remove(const char*) is
443 /// equivalent to this function.
445 /// The specified file must not be open by the calling process. If
446 /// it is, this function has undefined behaviour. Note that an
447 /// open memory map of the file counts as "the file being open".
449 /// \throw AccessError If the specified directory entry could not
450 /// be removed. If the reason corresponds to one of the exception
451 /// types that are derived from AccessError, the derived exception
452 /// type is thrown (as long as the underlying system provides the
453 /// information to unambiguously distinguish that particular
455 static void remove(const std::string& path);
457 /// Same as remove() except that this one returns false, rather
458 /// than throwing an exception, if the specified file does not
459 /// exist. If the file did exist, and was deleted, this function
461 static bool try_remove(const std::string& path);
463 /// Change the path of a directory entry. This can be used to
464 /// rename a file, and/or to move it from one directory to
465 /// another. This function is equivalent to std::rename(const
466 /// char*, const char*).
468 /// \throw AccessError If the path of the directory entry could
469 /// not be changed. If the reason corresponds to one of the
470 /// exception types that are derived from AccessError, the derived
471 /// exception type is thrown (as long as the underlying system
472 /// provides the information to unambiguously distinguish that
473 /// particular reason).
474 static void move(const std::string& old_path, const std::string& new_path);
476 /// Copy the file at the specified origin path to the specified target path.
477 static void copy(const std::string& origin_path, const std::string& target_path);
479 /// Compare the two files at the specified paths for equality. Returns true
480 /// if, and only if they are equal.
481 static bool compare(const std::string& path_1, const std::string& path_2);
483 /// Check whether two open file descriptors refer to the same
484 /// underlying file, that is, if writing via one of them, will
485 /// affect what is read from the other. In UNIX this boils down to
486 /// comparing inode numbers.
488 /// Both instances have to be attached to open files. If they are
489 /// not, this function has undefined behavior.
490 bool is_same_file(const File&) const;
491 static bool is_same_file_static(FileDesc f1, FileDesc f2);
493 // FIXME: Get rid of this method
494 bool is_removed() const;
496 /// Resolve the specified path against the specified base directory.
498 /// If \a path is absolute, or if \a base_dir is empty, \p path is returned
499 /// unmodified, otherwise \a path is resolved against \a base_dir.
501 /// Examples (assuming POSIX):
503 /// resolve("file", "dir") -> "dir/file"
504 /// resolve("../baz", "/foo/bar") -> "/foo/baz"
505 /// resolve("foo", ".") -> "./foo"
506 /// resolve(".", "/foo/") -> "/foo"
507 /// resolve("..", "foo") -> "."
508 /// resolve("../..", "foo") -> ".."
509 /// resolve("..", "..") -> "../.."
510 /// resolve("", "") -> "."
511 /// resolve("", "/") -> "/."
512 /// resolve("..", "/") -> "/."
513 /// resolve("..", "foo//bar") -> "foo"
515 /// This function does not access the file system.
517 /// \param path The path to be resolved. An empty string produces the same
518 /// result as as if "." was passed. The result has a trailing directory
519 /// separator (`/`) if, and only if this path has a trailing directory
522 /// \param base_dir The base directory path, which may be relative or
523 /// absolute. A final directory separator (`/`) is optional. The empty
524 /// string is interpreted as a relative path.
525 static std::string resolve(const std::string& path, const std::string& base_dir);
527 using ForEachHandler = std::function<bool(const std::string& file, const std::string& dir)>;
529 /// Scan the specified directory recursivle, and report each file
530 /// (nondirectory entry) via the specified handler.
532 /// The first argument passed to the handler is the name of a file (not the
533 /// whole path), and the second argument is the directory in which that file
534 /// resides. The directory will be specified as a path, and relative to \a
535 /// dir_path. The directory will be the empty string for files residing
536 /// directly in \a dir_path.
538 /// If the handler returns false, scanning will be aborted immediately, and
539 /// for_each() will return false. Otherwise for_each() will return true.
541 /// Scanning is done as if by a recursive set of DirScanner objects.
542 static bool for_each(const std::string& dir_path, ForEachHandler handler);
545 #ifdef _WIN32 // Windows version
546 // FIXME: This is not implemented for Windows
548 // NDK r10e has a bug in sys/stat.h dev_t ino_t are 4 bytes,
549 // but stat.st_dev and st_ino are 8 bytes. So we just use uint64 instead.
550 uint_fast64_t device;
554 // Return the unique id for the current opened file descriptor.
555 // Same UniqueID means they are the same file.
556 UniqueID get_unique_id() const;
557 // Return false if the file doesn't exist. Otherwise uid will be set.
558 static bool get_unique_id(const std::string& path, UniqueID& uid);
574 class PermissionDenied;
581 bool m_have_lock; // Only valid when m_fd is not null
585 std::unique_ptr<const char[]> m_encryption_key = nullptr;
587 bool lock(bool exclusive, bool non_blocking);
588 void open_internal(const std::string& path, AccessMode, CreateMode, int flags, bool* success);
591 void* m_addr = nullptr;
598 // Disable copying. Copying an opened MapBase will create a scenario
599 // where the same memory will be mapped once but unmapped twice.
600 MapBase(const MapBase&) = delete;
601 MapBase& operator=(const MapBase&) = delete;
603 void map(const File&, AccessMode, size_t size, int map_flags, size_t offset = 0);
604 void remap(const File&, AccessMode, size_t size, int map_flags);
605 void unmap() noexcept;
607 #if REALM_ENABLE_ENCRYPTION
608 util::EncryptedFileMapping* m_encrypted_mapping = nullptr;
609 inline util::EncryptedFileMapping* get_encrypted_mapping() const
611 return m_encrypted_mapping;
614 inline util::EncryptedFileMapping* get_encrypted_mapping() const
623 class File::ExclusiveLock {
625 ExclusiveLock(File& f)
630 ~ExclusiveLock() noexcept
634 // Disable copying. It is not how this class should be used.
635 ExclusiveLock(const ExclusiveLock&) = delete;
636 ExclusiveLock& operator=(const ExclusiveLock&) = delete;
642 class File::SharedLock {
649 ~SharedLock() noexcept
653 // Disable copying. It is not how this class should be used.
654 SharedLock(const SharedLock&) = delete;
655 SharedLock& operator=(const SharedLock&) = delete;
662 /// This class provides a RAII abstraction over the concept of a
663 /// memory mapped file.
665 /// Once created, the Map instance makes no reference to the File
666 /// instance that it was based upon, and that File instance may be
667 /// destroyed before the Map instance is destroyed.
669 /// Multiple concurrent mappings may be created from the same File
672 /// You can use UnmapGuard to acheive exception-safe unmapping prior
673 /// to the Map instance being detroyed.
675 /// A single Map instance must never be accessed concurrently by
676 /// multiple threads.
678 class File::Map : private MapBase {
680 /// Equivalent to calling map() on a default constructed instance.
681 explicit Map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0);
683 explicit Map(const File&, size_t offset, AccessMode = access_ReadOnly, size_t size = sizeof(T),
686 /// Create an instance that is not initially attached to a memory
692 // Disable copying. Copying an opened Map will create a scenario
693 // where the same memory will be mapped once but unmapped twice.
694 Map(const Map&) = delete;
695 Map& operator=(const Map&) = delete;
697 /// Move the mapping from another Map object to this Map object
698 File::Map<T>& operator=(File::Map<T>&& other)
702 m_addr = other.get_addr();
703 m_size = other.m_size;
706 #if REALM_ENABLE_ENCRYPTION
707 m_encrypted_mapping = other.m_encrypted_mapping;
708 other.m_encrypted_mapping = nullptr;
715 /// Calling this function on a Map instance that is already
716 /// attached to a memory mapped file has undefined behavior. The
717 /// returned pointer is the same as what will subsequently be
718 /// returned by get_addr().
719 T* map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0, size_t offset = 0);
721 /// See File::unmap(). This function is idempotent, that is, it is
722 /// valid to call it regardless of whether this instance is
723 /// currently attached to a memory mapped file.
724 void unmap() noexcept;
726 /// See File::remap().
728 /// Calling this function on a Map instance that is not currently
729 /// attached to a memory mapped file has undefined behavior. The
730 /// returned pointer is the same as what will subsequently be
731 /// returned by get_addr().
732 T* remap(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0);
734 /// See File::sync_map().
736 /// Calling this function on an instance that is not currently
737 /// attached to a memory mapped file, has undefined behavior.
740 /// Check whether this Map instance is currently attached to a
741 /// memory mapped file.
742 bool is_attached() const noexcept;
744 /// Returns a pointer to the beginning of the memory mapped file,
745 /// or null if this instance is not currently attached.
746 T* get_addr() const noexcept;
748 /// Returns the size of the mapped region, or zero if this
749 /// instance does not currently refer to a memory mapped
750 /// file. When this instance refers to a memory mapped file, the
751 /// returned value will always be identical to the size passed to
752 /// the constructor or to map().
753 size_t get_size() const noexcept;
755 /// Release the currently attached memory mapped file from this
756 /// Map instance. The address range may then be unmapped later by
757 /// a call to File::unmap().
758 T* release() noexcept;
760 #if REALM_ENABLE_ENCRYPTION
761 /// Get the encrypted file mapping corresponding to this mapping
762 inline EncryptedFileMapping* get_encrypted_mapping() const
764 return m_encrypted_mapping;
767 inline EncryptedFileMapping* get_encrypted_mapping() const
773 friend class UnmapGuard;
777 class File::CloseGuard {
779 CloseGuard(File& f) noexcept
783 ~CloseGuard() noexcept
788 void release() noexcept
792 // Disallow the default implementation of copy/assign, this is not how this
793 // class is intended to be used. For example we could get unexpected
794 // behaviour if one CloseGuard is copied and released but the other is not.
795 CloseGuard(const CloseGuard&) = delete;
796 CloseGuard& operator=(const CloseGuard&) = delete;
803 class File::UnlockGuard {
805 UnlockGuard(File& f) noexcept
809 ~UnlockGuard() noexcept
814 void release() noexcept
818 // Disallow the default implementation of copy/assign, this is not how this
819 // class is intended to be used. For example we could get unexpected
820 // behaviour if one UnlockGuard is copied and released but the other is not.
821 UnlockGuard(const UnlockGuard&) = delete;
822 UnlockGuard& operator=(const UnlockGuard&) = delete;
829 class File::UnmapGuard {
832 UnmapGuard(Map<T>& m) noexcept
836 ~UnmapGuard() noexcept
841 void release() noexcept
845 // Disallow the default implementation of copy/assign, this is not how this
846 // class is intended to be used. For example we could get unexpected
847 // behaviour if one UnmapGuard is copied and released but the other is not.
848 UnmapGuard(const UnmapGuard&) = delete;
849 UnmapGuard& operator=(const UnmapGuard&) = delete;
856 /// Only output is supported at this point.
857 class File::Streambuf : public std::streambuf {
859 explicit Streambuf(File*);
860 ~Streambuf() noexcept;
863 Streambuf(const Streambuf&) = delete;
864 Streambuf& operator=(const Streambuf&) = delete;
867 static const size_t buffer_size = 4096;
870 std::unique_ptr<char[]> const m_buffer;
872 int_type overflow(int_type) override;
874 pos_type seekpos(pos_type, std::ios_base::openmode) override;
879 /// Used for any I/O related exception. Note the derived exception
880 /// types that are used for various specific types of errors.
881 class File::AccessError : public std::runtime_error {
883 AccessError(const std::string& msg, const std::string& path);
885 /// Return the associated file system path, or the empty string if there is
886 /// no associated file system path, or if the file system path is unknown.
887 std::string get_path() const;
894 /// Thrown if the user does not have permission to open or create
895 /// the specified file in the specified access mode.
896 class File::PermissionDenied : public AccessError {
898 PermissionDenied(const std::string& msg, const std::string& path);
902 /// Thrown if the directory part of the specified path was not
903 /// found, or create_Never was specified and the file did no
905 class File::NotFound : public AccessError {
907 NotFound(const std::string& msg, const std::string& path);
911 /// Thrown if create_Always was specified and the file did already
913 class File::Exists : public AccessError {
915 Exists(const std::string& msg, const std::string& path);
921 DirScanner(const std::string& path, bool allow_missing = false);
922 ~DirScanner() noexcept;
923 bool next(std::string& name);
928 #elif REALM_HAVE_STD_FILESYSTEM
929 std::filesystem::directory_iterator m_iterator;
936 inline File::File(const std::string& path, Mode m)
947 inline File::File() noexcept
956 inline File::~File() noexcept
961 inline File::File(File&& f) noexcept
965 m_have_lock = f.m_have_lock;
971 m_encryption_key = std::move(f.m_encryption_key);
974 inline File& File::operator=(File&& f) noexcept
979 m_have_lock = f.m_have_lock;
985 m_encryption_key = std::move(f.m_encryption_key);
989 inline void File::open(const std::string& path, Mode m)
991 AccessMode a = access_ReadWrite;
992 CreateMode c = create_Auto;
1006 flags = flag_Append;
1009 open(path, a, c, flags);
1012 inline void File::open(const std::string& path, AccessMode am, CreateMode cm, int flags)
1014 open_internal(path, am, cm, flags, nullptr);
1018 inline void File::open(const std::string& path, bool& was_created)
1022 open_internal(path, access_ReadWrite, create_Must, 0, &success);
1027 open_internal(path, access_ReadWrite, create_Never, 0, &success);
1029 was_created = false;
1035 inline bool File::is_attached() const noexcept
1038 return (m_fd != nullptr);
1044 inline void File::lock_exclusive()
1049 inline void File::lock_shared()
1054 inline bool File::try_lock_exclusive()
1056 return lock(true, true);
1059 inline bool File::try_lock_shared()
1061 return lock(false, true);
1064 inline File::MapBase::MapBase() noexcept
1069 inline File::MapBase::~MapBase() noexcept
1074 inline void File::MapBase::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset)
1076 REALM_ASSERT(!m_addr);
1077 #if REALM_ENABLE_ENCRYPTION
1078 m_addr = f.map(a, size, m_encrypted_mapping, map_flags, offset);
1080 m_addr = f.map(a, size, map_flags, offset);
1086 inline void File::MapBase::unmap() noexcept
1090 File::unmap(m_addr, m_size);
1092 #if REALM_ENABLE_ENCRYPTION
1093 m_encrypted_mapping = nullptr;
1098 inline void File::MapBase::remap(const File& f, AccessMode a, size_t size, int map_flags)
1100 REALM_ASSERT(m_addr);
1102 m_addr = f.remap(m_addr, m_size, a, size, map_flags);
1107 inline void File::MapBase::sync()
1109 REALM_ASSERT(m_addr);
1111 File::sync_map(m_fd, m_addr, m_size);
1115 inline File::Map<T>::Map(const File& f, AccessMode a, size_t size, int map_flags)
1117 map(f, a, size, map_flags);
1121 inline File::Map<T>::Map(const File& f, size_t offset, AccessMode a, size_t size, int map_flags)
1123 map(f, a, size, map_flags, offset);
1127 inline File::Map<T>::Map() noexcept
1132 inline File::Map<T>::~Map() noexcept
1137 inline T* File::Map<T>::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset)
1139 MapBase::map(f, a, size, map_flags, offset);
1140 return static_cast<T*>(m_addr);
1144 inline void File::Map<T>::unmap() noexcept
1150 inline T* File::Map<T>::remap(const File& f, AccessMode a, size_t size, int map_flags)
1152 MapBase::remap(f, a, size, map_flags);
1153 return static_cast<T*>(m_addr);
1157 inline void File::Map<T>::sync()
1163 inline bool File::Map<T>::is_attached() const noexcept
1165 return (m_addr != nullptr);
1169 inline T* File::Map<T>::get_addr() const noexcept
1171 return static_cast<T*>(m_addr);
1175 inline size_t File::Map<T>::get_size() const noexcept
1177 return m_addr ? m_size : 0;
1181 inline T* File::Map<T>::release() noexcept
1183 T* addr = static_cast<T*>(m_addr);
1190 inline File::Streambuf::Streambuf(File* f)
1192 , m_buffer(new char[buffer_size])
1194 char* b = m_buffer.get();
1195 setp(b, b + buffer_size);
1198 inline File::Streambuf::~Streambuf() noexcept
1201 if (m_file.is_attached())
1205 // Errors deliberately ignored
1209 inline File::Streambuf::int_type File::Streambuf::overflow(int_type c)
1212 if (c == traits_type::eof())
1213 return traits_type::not_eof(c);
1214 *pptr() = traits_type::to_char_type(c);
1219 inline int File::Streambuf::sync()
1225 inline File::Streambuf::pos_type File::Streambuf::seekpos(pos_type pos, std::ios_base::openmode)
1229 if (int_cast_with_overflow_detect(std::streamsize(pos), pos2))
1230 throw std::runtime_error("Seek position overflow");
1235 inline void File::Streambuf::flush()
1237 size_t n = pptr() - pbase();
1239 m_file.write(pbase(), n);
1240 setp(m_buffer.get(), epptr());
1244 inline File::AccessError::AccessError(const std::string& msg, const std::string& path)
1245 : std::runtime_error(msg)
1250 inline std::string File::AccessError::get_path() const
1255 inline File::PermissionDenied::PermissionDenied(const std::string& msg, const std::string& path)
1256 : AccessError(msg, path)
1260 inline File::NotFound::NotFound(const std::string& msg, const std::string& path)
1261 : AccessError(msg, path)
1265 inline File::Exists::Exists(const std::string& msg, const std::string& path)
1266 : AccessError(msg, path)
1270 inline bool operator==(const File::UniqueID& lhs, const File::UniqueID& rhs)
1272 #ifdef _WIN32 // Windows version
1273 throw std::runtime_error("Not yet supported");
1274 #else // POSIX version
1275 return lhs.device == rhs.device && lhs.inode == rhs.inode;
1279 inline bool operator!=(const File::UniqueID& lhs, const File::UniqueID& rhs)
1281 return !(lhs == rhs);
1284 inline bool operator<(const File::UniqueID& lhs, const File::UniqueID& rhs)
1286 #ifdef _WIN32 // Windows version
1287 throw std::runtime_error("Not yet supported");
1288 #else // POSIX version
1289 if (lhs.device < rhs.device)
1291 if (lhs.device > rhs.device)
1293 if (lhs.inode < rhs.inode)
1299 inline bool operator>(const File::UniqueID& lhs, const File::UniqueID& rhs)
1304 inline bool operator<=(const File::UniqueID& lhs, const File::UniqueID& rhs)
1306 return !(lhs > rhs);
1309 inline bool operator>=(const File::UniqueID& lhs, const File::UniqueID& rhs)
1311 return !(lhs < rhs);
1315 } // namespace realm
1317 #endif // REALM_UTIL_FILE_HPP