added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / util / file.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_UTIL_FILE_HPP
20 #define REALM_UTIL_FILE_HPP
21
22 #include <cstddef>
23 #include <cstdint>
24 #include <memory>
25 #include <functional>
26 #include <stdexcept>
27 #include <string>
28 #include <streambuf>
29
30 #ifndef _WIN32
31 #include <dirent.h> // POSIX.1-2001
32 #endif
33
34 #if defined(_MSC_VER) && _MSC_VER >= 1900 // compiling with at least Visual Studio 2015
35 #include <experimental/filesystem>
36 namespace std {
37     namespace filesystem = std::experimental::filesystem::v1;
38 }
39 #define REALM_HAVE_STD_FILESYSTEM 1
40 #else
41 #define REALM_HAVE_STD_FILESYSTEM 0
42 #endif
43
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>
48
49
50 namespace realm {
51 namespace util {
52
53 class EncryptedFileMapping;
54
55 /// Create the specified directory in the file system.
56 ///
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);
63
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);
68
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.
73 ///
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);
80
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
84 /// returns true.
85 bool try_remove_dir(const std::string& path);
86
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().
90 ///
91 /// \throw File::AccessError If removal of the directory, or any of its contents
92 /// fail.
93 ///
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);
97
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
101 /// returns true.
102 ///
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);
106
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();
110
111 size_t page_size();
112
113
114 /// This class provides a RAII abstraction over the concept of a file
115 /// descriptor (or file handle).
116 ///
117 /// Locks are automatically and immediately released when the File
118 /// instance is closed.
119 ///
120 /// You can use CloseGuard and UnlockGuard to acheive exception-safe
121 /// closing or unlocking prior to the File instance being detroyed.
122 ///
123 /// A single File instance must never be accessed concurrently by
124 /// multiple threads.
125 ///
126 /// You can write to a file via an std::ostream as follows:
127 ///
128 /// \code{.cpp}
129 ///
130 ///   File::Streambuf my_streambuf(&my_file);
131 ///   std::ostream out(&my_strerambuf);
132 ///   out << 7945.9;
133 ///
134 /// \endcode
135 class File {
136 public:
137     enum Mode {
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+)
142     };
143
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);
147
148     /// Create an instance that is not initially attached to an open
149     /// file.
150     File() noexcept;
151
152     ~File() noexcept;
153
154     File(File&&) noexcept;
155     File& operator=(File&&) noexcept;
156
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;
161
162     /// Calling this function on an instance that is already attached
163     /// to an open file has undefined behavior.
164     ///
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);
171
172     /// This function is idempotent, that is, it is valid to call it
173     /// regardless of whether this instance currently is attached to
174     /// an open file.
175     void close() noexcept;
176
177     /// Check whether this File instance is currently attached to an
178     /// open file.
179     bool is_attached() const noexcept;
180
181     enum AccessMode {
182         access_ReadOnly,
183         access_ReadWrite,
184     };
185
186     enum CreateMode {
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.
190     };
191
192     enum {
193         flag_Trunc = 1, ///< Truncate the file if it already exists.
194         flag_Append = 2 ///< Move to end of file before each write.
195     };
196
197     /// See open(const std::string&, Mode).
198     ///
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);
204
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);
209
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.
213     ///
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);
218
219     /// Write the specified data to this file.
220     ///
221     /// Calling this function on an instance, that is not currently
222     /// attached to an open file, has undefined behavior.
223     ///
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);
228
229     // Tells current file pointer of fd
230     static uint64_t get_file_pos(FileDesc fd);
231
232     /// Calls write(s.data(), s.size()).
233     void write(const std::string& s)
234     {
235         write(s.data(), s.size());
236     }
237
238     /// Calls read(data, N).
239     template <size_t N>
240     size_t read(char (&data)[N])
241     {
242         return read(data, N);
243     }
244
245     /// Calls write(data(), N).
246     template <size_t N>
247     void write(const char (&data)[N])
248     {
249         write(data, N);
250     }
251
252     /// Plays the same role as off_t in POSIX
253     typedef int_fast64_t SizeType;
254
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);
259
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.
266     ///
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);
271
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.
277     ///
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.
283     ///
284     /// \sa prealloc_if_supported()
285     void prealloc(SizeType offset, size_t size);
286
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.
291     ///
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.
295     ///
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.
299     ///
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.
303     ///
304     /// \sa prealloc()
305     /// \sa is_prealloc_supported()
306     void prealloc_if_supported(SizeType offset, size_t size);
307
308     /// See prealloc_if_supported().
309     static bool is_prealloc_supported();
310
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.
314     void seek(SizeType);
315     static void seek_static(FileDesc, SizeType);
316
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
320     /// `F_FULLFSYNC`.
321     void sync();
322
323     /// Place an exclusive lock on this file. This blocks the caller
324     /// until all other locks have been released.
325     ///
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.
329     ///
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();
334
335     /// Place an shared lock on this file. This blocks the caller
336     /// until all other exclusive locks have been released.
337     ///
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.
341     ///
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.
345     void lock_shared();
346
347     /// Non-blocking version of lock_exclusive(). Returns true iff it
348     /// succeeds.
349     bool try_lock_exclusive();
350
351     /// Non-blocking version of lock_shared(). Returns true iff it
352     /// succeeds.
353     bool try_lock_shared();
354
355     /// Release a previously acquired lock on this file. This function
356     /// is idempotent.
357     void unlock() noexcept;
358
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.
361     ///
362     /// \param key A 64-byte encryption key, or null to disable encryption.
363     void set_encryption_key(const char* key);
364
365     /// Get the encryption key set by set_encryption_key(),
366     /// null_ptr if no key set.
367     const char* get_encryption_key();
368     enum {
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.
375         map_NoSync = 1
376     };
377
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.
382     ///
383     /// This File instance does not need to remain in existence after
384     /// the mapping is established.
385     ///
386     /// Multiple concurrent mappings may be created from the same File
387     /// instance.
388     ///
389     /// Specifying access_ReadWrite for a file that is opened in
390     /// read-only mode, is an error.
391     ///
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.
395     ///
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;
399
400     /// The same as unmap(old_addr, old_size) followed by map(a,
401     /// new_size, map_flags), but more efficient on some systems.
402     ///
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.
409     ///
410     /// If this function throws, the old address range will remain
411     /// mapped.
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;
414
415 #if REALM_ENABLE_ENCRYPTION
416     void* map(AccessMode, size_t size, EncryptedFileMapping*& mapping, int map_flags = 0, size_t offset = 0) const;
417 #endif
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;
421
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
425     /// map().
426     static void sync_map(FileDesc fd, void* addr, size_t size);
427
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
431     /// as not existing.
432     static bool exists(const std::string& path);
433
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);
438
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.
444     ///
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".
448     ///
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
454     /// reason).
455     static void remove(const std::string& path);
456
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
460     /// returns true.
461     static bool try_remove(const std::string& path);
462
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*).
467     ///
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);
475
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);
478
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);
482
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.
487     ///
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);
492
493     // FIXME: Get rid of this method
494     bool is_removed() const;
495
496     /// Resolve the specified path against the specified base directory.
497     ///
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.
500     ///
501     /// Examples (assuming POSIX):
502     ///
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"
514     ///
515     /// This function does not access the file system.
516     ///
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
520     /// separator.
521     ///
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);
526
527     using ForEachHandler = std::function<bool(const std::string& file, const std::string& dir)>;
528
529     /// Scan the specified directory recursivle, and report each file
530     /// (nondirectory entry) via the specified handler.
531     ///
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.
537     ///
538     /// If the handler returns false, scanning will be aborted immediately, and
539     /// for_each() will return false. Otherwise for_each() will return true.
540     ///
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);
543
544     struct UniqueID {
545 #ifdef _WIN32 // Windows version
546 // FIXME: This is not implemented for Windows
547 #else
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;
551         uint_fast64_t inode;
552 #endif
553     };
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);
559
560     class ExclusiveLock;
561     class SharedLock;
562
563     template <class>
564     class Map;
565
566     class CloseGuard;
567     class UnlockGuard;
568     class UnmapGuard;
569
570     class Streambuf;
571
572     // Exceptions
573     class AccessError;
574     class PermissionDenied;
575     class NotFound;
576     class Exists;
577
578 private:
579 #ifdef _WIN32
580     void* m_fd;
581     bool m_have_lock; // Only valid when m_fd is not null
582 #else
583     int m_fd;
584 #endif
585     std::unique_ptr<const char[]> m_encryption_key = nullptr;
586
587     bool lock(bool exclusive, bool non_blocking);
588     void open_internal(const std::string& path, AccessMode, CreateMode, int flags, bool* success);
589
590     struct MapBase {
591         void* m_addr = nullptr;
592         size_t m_size = 0;
593         FileDesc m_fd;
594
595         MapBase() noexcept;
596         ~MapBase() noexcept;
597
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;
602
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;
606         void sync();
607 #if REALM_ENABLE_ENCRYPTION
608         util::EncryptedFileMapping* m_encrypted_mapping = nullptr;
609         inline util::EncryptedFileMapping* get_encrypted_mapping() const
610         {
611             return m_encrypted_mapping;
612         }
613 #else
614         inline util::EncryptedFileMapping* get_encrypted_mapping() const
615         {
616             return nullptr;
617         }
618 #endif
619     };
620 };
621
622
623 class File::ExclusiveLock {
624 public:
625     ExclusiveLock(File& f)
626         : m_file(f)
627     {
628         f.lock_exclusive();
629     }
630     ~ExclusiveLock() noexcept
631     {
632         m_file.unlock();
633     }
634     // Disable copying. It is not how this class should be used.
635     ExclusiveLock(const ExclusiveLock&) = delete;
636     ExclusiveLock& operator=(const ExclusiveLock&) = delete;
637
638 private:
639     File& m_file;
640 };
641
642 class File::SharedLock {
643 public:
644     SharedLock(File& f)
645         : m_file(f)
646     {
647         f.lock_shared();
648     }
649     ~SharedLock() noexcept
650     {
651         m_file.unlock();
652     }
653     // Disable copying. It is not how this class should be used.
654     SharedLock(const SharedLock&) = delete;
655     SharedLock& operator=(const SharedLock&) = delete;
656
657 private:
658     File& m_file;
659 };
660
661
662 /// This class provides a RAII abstraction over the concept of a
663 /// memory mapped file.
664 ///
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.
668 ///
669 /// Multiple concurrent mappings may be created from the same File
670 /// instance.
671 ///
672 /// You can use UnmapGuard to acheive exception-safe unmapping prior
673 /// to the Map instance being detroyed.
674 ///
675 /// A single Map instance must never be accessed concurrently by
676 /// multiple threads.
677 template <class T>
678 class File::Map : private MapBase {
679 public:
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);
682
683     explicit Map(const File&, size_t offset, AccessMode = access_ReadOnly, size_t size = sizeof(T),
684                  int map_flags = 0);
685
686     /// Create an instance that is not initially attached to a memory
687     /// mapped file.
688     Map() noexcept;
689
690     ~Map() noexcept;
691
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;
696
697     /// Move the mapping from another Map object to this Map object
698     File::Map<T>& operator=(File::Map<T>&& other)
699     {
700         if (m_addr)
701             unmap();
702         m_addr = other.get_addr();
703         m_size = other.m_size;
704         other.m_addr = 0;
705         other.m_size = 0;
706 #if REALM_ENABLE_ENCRYPTION
707         m_encrypted_mapping = other.m_encrypted_mapping;
708         other.m_encrypted_mapping = nullptr;
709 #endif
710         return *this;
711     }
712
713     /// See File::map().
714     ///
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);
720
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;
725
726     /// See File::remap().
727     ///
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);
733
734     /// See File::sync_map().
735     ///
736     /// Calling this function on an instance that is not currently
737     /// attached to a memory mapped file, has undefined behavior.
738     void sync();
739
740     /// Check whether this Map instance is currently attached to a
741     /// memory mapped file.
742     bool is_attached() const noexcept;
743
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;
747
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;
754
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;
759
760 #if REALM_ENABLE_ENCRYPTION
761     /// Get the encrypted file mapping corresponding to this mapping
762     inline EncryptedFileMapping* get_encrypted_mapping() const
763     {
764         return m_encrypted_mapping;
765     }
766 #else
767     inline EncryptedFileMapping* get_encrypted_mapping() const
768     {
769         return nullptr;
770     }
771 #endif
772
773     friend class UnmapGuard;
774 };
775
776
777 class File::CloseGuard {
778 public:
779     CloseGuard(File& f) noexcept
780         : m_file(&f)
781     {
782     }
783     ~CloseGuard() noexcept
784     {
785         if (m_file)
786             m_file->close();
787     }
788     void release() noexcept
789     {
790         m_file = nullptr;
791     }
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;
797
798 private:
799     File* m_file;
800 };
801
802
803 class File::UnlockGuard {
804 public:
805     UnlockGuard(File& f) noexcept
806         : m_file(&f)
807     {
808     }
809     ~UnlockGuard() noexcept
810     {
811         if (m_file)
812             m_file->unlock();
813     }
814     void release() noexcept
815     {
816         m_file = nullptr;
817     }
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;
823
824 private:
825     File* m_file;
826 };
827
828
829 class File::UnmapGuard {
830 public:
831     template <class T>
832     UnmapGuard(Map<T>& m) noexcept
833         : m_map(&m)
834     {
835     }
836     ~UnmapGuard() noexcept
837     {
838         if (m_map)
839             m_map->unmap();
840     }
841     void release() noexcept
842     {
843         m_map = nullptr;
844     }
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;
850
851 private:
852     MapBase* m_map;
853 };
854
855
856 /// Only output is supported at this point.
857 class File::Streambuf : public std::streambuf {
858 public:
859     explicit Streambuf(File*);
860     ~Streambuf() noexcept;
861
862     // Disable copying
863     Streambuf(const Streambuf&) = delete;
864     Streambuf& operator=(const Streambuf&) = delete;
865
866 private:
867     static const size_t buffer_size = 4096;
868
869     File& m_file;
870     std::unique_ptr<char[]> const m_buffer;
871
872     int_type overflow(int_type) override;
873     int sync() override;
874     pos_type seekpos(pos_type, std::ios_base::openmode) override;
875     void flush();
876 };
877
878
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 {
882 public:
883     AccessError(const std::string& msg, const std::string& path);
884
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;
888
889 private:
890     std::string m_path;
891 };
892
893
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 {
897 public:
898     PermissionDenied(const std::string& msg, const std::string& path);
899 };
900
901
902 /// Thrown if the directory part of the specified path was not
903 /// found, or create_Never was specified and the file did no
904 /// exist.
905 class File::NotFound : public AccessError {
906 public:
907     NotFound(const std::string& msg, const std::string& path);
908 };
909
910
911 /// Thrown if create_Always was specified and the file did already
912 /// exist.
913 class File::Exists : public AccessError {
914 public:
915     Exists(const std::string& msg, const std::string& path);
916 };
917
918
919 class DirScanner {
920 public:
921     DirScanner(const std::string& path, bool allow_missing = false);
922     ~DirScanner() noexcept;
923     bool next(std::string& name);
924
925 private:
926 #ifndef _WIN32
927     DIR* m_dirp;
928 #elif REALM_HAVE_STD_FILESYSTEM
929     std::filesystem::directory_iterator m_iterator;
930 #endif
931 };
932
933
934 // Implementation:
935
936 inline File::File(const std::string& path, Mode m)
937 {
938 #ifdef _WIN32
939     m_fd = nullptr;
940 #else
941     m_fd = -1;
942 #endif
943
944     open(path, m);
945 }
946
947 inline File::File() noexcept
948 {
949 #ifdef _WIN32
950     m_fd = nullptr;
951 #else
952     m_fd = -1;
953 #endif
954 }
955
956 inline File::~File() noexcept
957 {
958     close();
959 }
960
961 inline File::File(File&& f) noexcept
962 {
963 #ifdef _WIN32
964     m_fd = f.m_fd;
965     m_have_lock = f.m_have_lock;
966     f.m_fd = nullptr;
967 #else
968     m_fd = f.m_fd;
969     f.m_fd = -1;
970 #endif
971     m_encryption_key = std::move(f.m_encryption_key);
972 }
973
974 inline File& File::operator=(File&& f) noexcept
975 {
976     close();
977 #ifdef _WIN32
978     m_fd = f.m_fd;
979     m_have_lock = f.m_have_lock;
980     f.m_fd = nullptr;
981 #else
982     m_fd = f.m_fd;
983     f.m_fd = -1;
984 #endif
985     m_encryption_key = std::move(f.m_encryption_key);
986     return *this;
987 }
988
989 inline void File::open(const std::string& path, Mode m)
990 {
991     AccessMode a = access_ReadWrite;
992     CreateMode c = create_Auto;
993     int flags = 0;
994     switch (m) {
995         case mode_Read:
996             a = access_ReadOnly;
997             c = create_Never;
998             break;
999         case mode_Update:
1000             c = create_Never;
1001             break;
1002         case mode_Write:
1003             flags = flag_Trunc;
1004             break;
1005         case mode_Append:
1006             flags = flag_Append;
1007             break;
1008     }
1009     open(path, a, c, flags);
1010 }
1011
1012 inline void File::open(const std::string& path, AccessMode am, CreateMode cm, int flags)
1013 {
1014     open_internal(path, am, cm, flags, nullptr);
1015 }
1016
1017
1018 inline void File::open(const std::string& path, bool& was_created)
1019 {
1020     while (1) {
1021         bool success;
1022         open_internal(path, access_ReadWrite, create_Must, 0, &success);
1023         if (success) {
1024             was_created = true;
1025             return;
1026         }
1027         open_internal(path, access_ReadWrite, create_Never, 0, &success);
1028         if (success) {
1029             was_created = false;
1030             return;
1031         }
1032     }
1033 }
1034
1035 inline bool File::is_attached() const noexcept
1036 {
1037 #ifdef _WIN32
1038     return (m_fd != nullptr);
1039 #else
1040     return 0 <= m_fd;
1041 #endif
1042 }
1043
1044 inline void File::lock_exclusive()
1045 {
1046     lock(true, false);
1047 }
1048
1049 inline void File::lock_shared()
1050 {
1051     lock(false, false);
1052 }
1053
1054 inline bool File::try_lock_exclusive()
1055 {
1056     return lock(true, true);
1057 }
1058
1059 inline bool File::try_lock_shared()
1060 {
1061     return lock(false, true);
1062 }
1063
1064 inline File::MapBase::MapBase() noexcept
1065 {
1066     m_addr = nullptr;
1067 }
1068
1069 inline File::MapBase::~MapBase() noexcept
1070 {
1071     unmap();
1072 }
1073
1074 inline void File::MapBase::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset)
1075 {
1076     REALM_ASSERT(!m_addr);
1077 #if REALM_ENABLE_ENCRYPTION
1078     m_addr = f.map(a, size, m_encrypted_mapping, map_flags, offset);
1079 #else
1080     m_addr = f.map(a, size, map_flags, offset);
1081 #endif
1082     m_size = size;
1083     m_fd = f.m_fd;
1084 }
1085
1086 inline void File::MapBase::unmap() noexcept
1087 {
1088     if (!m_addr)
1089         return;
1090     File::unmap(m_addr, m_size);
1091     m_addr = nullptr;
1092 #if REALM_ENABLE_ENCRYPTION
1093     m_encrypted_mapping = nullptr;
1094 #endif
1095     m_fd = 0;
1096 }
1097
1098 inline void File::MapBase::remap(const File& f, AccessMode a, size_t size, int map_flags)
1099 {
1100     REALM_ASSERT(m_addr);
1101
1102     m_addr = f.remap(m_addr, m_size, a, size, map_flags);
1103     m_size = size;
1104     m_fd = f.m_fd;
1105 }
1106
1107 inline void File::MapBase::sync()
1108 {
1109     REALM_ASSERT(m_addr);
1110
1111     File::sync_map(m_fd, m_addr, m_size);
1112 }
1113
1114 template <class T>
1115 inline File::Map<T>::Map(const File& f, AccessMode a, size_t size, int map_flags)
1116 {
1117     map(f, a, size, map_flags);
1118 }
1119
1120 template <class T>
1121 inline File::Map<T>::Map(const File& f, size_t offset, AccessMode a, size_t size, int map_flags)
1122 {
1123     map(f, a, size, map_flags, offset);
1124 }
1125
1126 template <class T>
1127 inline File::Map<T>::Map() noexcept
1128 {
1129 }
1130
1131 template <class T>
1132 inline File::Map<T>::~Map() noexcept
1133 {
1134 }
1135
1136 template <class T>
1137 inline T* File::Map<T>::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset)
1138 {
1139     MapBase::map(f, a, size, map_flags, offset);
1140     return static_cast<T*>(m_addr);
1141 }
1142
1143 template <class T>
1144 inline void File::Map<T>::unmap() noexcept
1145 {
1146     MapBase::unmap();
1147 }
1148
1149 template <class T>
1150 inline T* File::Map<T>::remap(const File& f, AccessMode a, size_t size, int map_flags)
1151 {
1152     MapBase::remap(f, a, size, map_flags);
1153     return static_cast<T*>(m_addr);
1154 }
1155
1156 template <class T>
1157 inline void File::Map<T>::sync()
1158 {
1159     MapBase::sync();
1160 }
1161
1162 template <class T>
1163 inline bool File::Map<T>::is_attached() const noexcept
1164 {
1165     return (m_addr != nullptr);
1166 }
1167
1168 template <class T>
1169 inline T* File::Map<T>::get_addr() const noexcept
1170 {
1171     return static_cast<T*>(m_addr);
1172 }
1173
1174 template <class T>
1175 inline size_t File::Map<T>::get_size() const noexcept
1176 {
1177     return m_addr ? m_size : 0;
1178 }
1179
1180 template <class T>
1181 inline T* File::Map<T>::release() noexcept
1182 {
1183     T* addr = static_cast<T*>(m_addr);
1184     m_addr = nullptr;
1185     m_fd = 0;
1186     return addr;
1187 }
1188
1189
1190 inline File::Streambuf::Streambuf(File* f)
1191     : m_file(*f)
1192     , m_buffer(new char[buffer_size])
1193 {
1194     char* b = m_buffer.get();
1195     setp(b, b + buffer_size);
1196 }
1197
1198 inline File::Streambuf::~Streambuf() noexcept
1199 {
1200     try {
1201         if (m_file.is_attached())
1202             flush();
1203     }
1204     catch (...) {
1205         // Errors deliberately ignored
1206     }
1207 }
1208
1209 inline File::Streambuf::int_type File::Streambuf::overflow(int_type c)
1210 {
1211     flush();
1212     if (c == traits_type::eof())
1213         return traits_type::not_eof(c);
1214     *pptr() = traits_type::to_char_type(c);
1215     pbump(1);
1216     return c;
1217 }
1218
1219 inline int File::Streambuf::sync()
1220 {
1221     flush();
1222     return 0;
1223 }
1224
1225 inline File::Streambuf::pos_type File::Streambuf::seekpos(pos_type pos, std::ios_base::openmode)
1226 {
1227     flush();
1228     SizeType pos2 = 0;
1229     if (int_cast_with_overflow_detect(std::streamsize(pos), pos2))
1230         throw std::runtime_error("Seek position overflow");
1231     m_file.seek(pos2);
1232     return pos;
1233 }
1234
1235 inline void File::Streambuf::flush()
1236 {
1237     size_t n = pptr() - pbase();
1238     if (n > 0) {
1239         m_file.write(pbase(), n);
1240         setp(m_buffer.get(), epptr());
1241     }
1242 }
1243
1244 inline File::AccessError::AccessError(const std::string& msg, const std::string& path)
1245     : std::runtime_error(msg)
1246     , m_path(path)
1247 {
1248 }
1249
1250 inline std::string File::AccessError::get_path() const
1251 {
1252     return m_path;
1253 }
1254
1255 inline File::PermissionDenied::PermissionDenied(const std::string& msg, const std::string& path)
1256     : AccessError(msg, path)
1257 {
1258 }
1259
1260 inline File::NotFound::NotFound(const std::string& msg, const std::string& path)
1261     : AccessError(msg, path)
1262 {
1263 }
1264
1265 inline File::Exists::Exists(const std::string& msg, const std::string& path)
1266     : AccessError(msg, path)
1267 {
1268 }
1269
1270 inline bool operator==(const File::UniqueID& lhs, const File::UniqueID& rhs)
1271 {
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;
1276 #endif
1277 }
1278
1279 inline bool operator!=(const File::UniqueID& lhs, const File::UniqueID& rhs)
1280 {
1281     return !(lhs == rhs);
1282 }
1283
1284 inline bool operator<(const File::UniqueID& lhs, const File::UniqueID& rhs)
1285 {
1286 #ifdef _WIN32 // Windows version
1287     throw std::runtime_error("Not yet supported");
1288 #else // POSIX version
1289     if (lhs.device < rhs.device)
1290         return true;
1291     if (lhs.device > rhs.device)
1292         return false;
1293     if (lhs.inode < rhs.inode)
1294         return true;
1295     return false;
1296 #endif
1297 }
1298
1299 inline bool operator>(const File::UniqueID& lhs, const File::UniqueID& rhs)
1300 {
1301     return rhs < lhs;
1302 }
1303
1304 inline bool operator<=(const File::UniqueID& lhs, const File::UniqueID& rhs)
1305 {
1306     return !(lhs > rhs);
1307 }
1308
1309 inline bool operator>=(const File::UniqueID& lhs, const File::UniqueID& rhs)
1310 {
1311     return !(lhs < rhs);
1312 }
1313
1314 } // namespace util
1315 } // namespace realm
1316
1317 #endif // REALM_UTIL_FILE_HPP