1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163

base / files / memory_mapped_file.h [blame]

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_FILES_MEMORY_MAPPED_FILE_H_
#define BASE_FILES_MEMORY_MAPPED_FILE_H_

#include <stddef.h>
#include <stdint.h>

#include <utility>

#include "base/base_export.h"
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_handle.h"
#endif

namespace base {

class FilePath;

class BASE_EXPORT MemoryMappedFile {
 public:
  enum Access {
    // Mapping a file into memory effectively allows for file I/O on any thread.
    // The accessing thread could be paused while data from the file is paged
    // into memory. Worse, a corrupted filesystem could cause a SEGV within the
    // program instead of just an I/O error.
    READ_ONLY,

    // This provides read/write access to a file and must be used with care of
    // the additional subtleties involved in doing so. Though the OS will do
    // the writing of data on its own time, too many dirty pages can cause
    // the OS to pause the thread while it writes them out. The pause can
    // be as much as 1s on some systems.
    READ_WRITE,

    // This provides read/write access to the mapped file contents as above, but
    // applies a copy-on-write policy such that no writes are carried through to
    // the underlying file.
    READ_WRITE_COPY,

    // This provides read/write access but with the ability to write beyond
    // the end of the existing file up to a maximum size specified as the
    // "region". Depending on the OS, the file may or may not be immediately
    // extended to the maximum size though it won't be loaded in RAM until
    // needed. Note, however, that the maximum size will still be reserved
    // in the process address space.
    READ_WRITE_EXTEND,

#if BUILDFLAG(IS_WIN)
    // This provides read access, but as executable code used for prefetching
    // DLLs into RAM to avoid inefficient hard fault patterns such as during
    // process startup. The accessing thread could be paused while data from
    // the file is read into memory (if needed).
    READ_CODE_IMAGE,
#endif
  };

  // The default constructor sets all members to invalid/null values.
  MemoryMappedFile();
  MemoryMappedFile(const MemoryMappedFile&) = delete;
  MemoryMappedFile& operator=(const MemoryMappedFile&) = delete;
  ~MemoryMappedFile();

  // Used to hold information about a region [offset + size] of a file.
  struct BASE_EXPORT Region {
    static const Region kWholeFile;

    friend bool operator==(const Region&, const Region&) = default;

    // Start of the region (measured in bytes from the beginning of the file).
    int64_t offset;

    // Length of the region in bytes.
    size_t size;
  };

  // Opens an existing file and maps it into memory. |access| can be read-only
  // or read/write but not read/write+extend. If this object already points
  // to a valid memory mapped file then this method will fail and return
  // false. If it cannot open the file, the file does not exist, or the
  // memory mapping fails, it will return false.
  [[nodiscard]] bool Initialize(const FilePath& file_name, Access access);
  [[nodiscard]] bool Initialize(const FilePath& file_name) {
    return Initialize(file_name, READ_ONLY);
  }

  // As above, but works with an already-opened file. |access| can be read-only
  // or read/write but not read/write+extend. MemoryMappedFile takes ownership
  // of |file| and closes it when done. |file| must have been opened with
  // permissions suitable for |access|. If the memory mapping fails, it will
  // return false.
  [[nodiscard]] bool Initialize(File file, Access access);
  [[nodiscard]] bool Initialize(File file) {
    return Initialize(std::move(file), READ_ONLY);
  }

  // As above, but works with a region of an already-opened file. |access|
  // must not be READ_CODE_IMAGE. If READ_WRITE_EXTEND is specified then
  // |region| provides the maximum size of the file. If the memory mapping
  // fails, it return false.
  [[nodiscard]] bool Initialize(File file, const Region& region, Access access);
  [[nodiscard]] bool Initialize(File file, const Region& region) {
    return Initialize(std::move(file), region, READ_ONLY);
  }

  const uint8_t* data() const { return bytes_.data(); }
  uint8_t* data() { return bytes_.data(); }
  size_t length() const { return bytes_.size(); }

  span<const uint8_t> bytes() const { return bytes_; }
  span<uint8_t> mutable_bytes() { return bytes_; }

  // Is file_ a valid file handle that points to an open, memory mapped file?
  bool IsValid() const;

 private:
  // Given the arbitrarily aligned memory region [start, size], returns the
  // boundaries of the region aligned to the granularity specified by the OS,
  // (a page on Linux, ~32k on Windows) as follows:
  // - |aligned_start| is page aligned and <= |start|.
  // - |aligned_size| is a multiple of the VM granularity and >= |size|.
  // - |offset| is the displacement of |start| w.r.t |aligned_start|.
  static void CalculateVMAlignedBoundaries(int64_t start,
                                           size_t size,
                                           int64_t* aligned_start,
                                           size_t* aligned_size,
                                           int32_t* offset);

#if BUILDFLAG(IS_WIN)
  // Maps the executable file to memory, point `bytes_` to the memory range.
  // Return true on success.
  bool MapImageToMemory(Access access);
#endif

  // Map the file to memory, point `bytes_` to that memory address. Return true
  // on success, false on any kind of failure. This is a helper for
  // Initialize().
  bool MapFileRegionToMemory(const Region& region, Access access);

  // Closes all open handles.
  void CloseHandles();

  File file_;

  // RAW_PTR_EXCLUSION: Never allocated by PartitionAlloc (always mmap'ed), so
  // there is no benefit to using a raw_span, only cost.
  RAW_PTR_EXCLUSION span<uint8_t> bytes_;

#if BUILDFLAG(IS_WIN)
  win::ScopedHandle file_mapping_;
#endif
};

}  // namespace base

#endif  // BASE_FILES_MEMORY_MAPPED_FILE_H_