1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/files/memory_mapped_file.h"
6
7#include <utility>
8
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "base/sys_info.h"
12#include "build/build_config.h"
13
14namespace base {
15
16const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0};
17
18bool MemoryMappedFile::Region::operator==(
19    const MemoryMappedFile::Region& other) const {
20  return other.offset == offset && other.size == size;
21}
22
23bool MemoryMappedFile::Region::operator!=(
24    const MemoryMappedFile::Region& other) const {
25  return other.offset != offset || other.size != size;
26}
27
28MemoryMappedFile::~MemoryMappedFile() {
29  CloseHandles();
30}
31
32#if !defined(OS_NACL)
33bool MemoryMappedFile::Initialize(const FilePath& file_name, Access access) {
34  if (IsValid())
35    return false;
36
37  uint32_t flags = 0;
38  switch (access) {
39    case READ_ONLY:
40      flags = File::FLAG_OPEN | File::FLAG_READ;
41      break;
42    case READ_WRITE:
43      flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE;
44      break;
45    case READ_WRITE_EXTEND:
46      // Can't open with "extend" because no maximum size is known.
47      NOTREACHED();
48  }
49  file_.Initialize(file_name, flags);
50
51  if (!file_.IsValid()) {
52    DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe();
53    return false;
54  }
55
56  if (!MapFileRegionToMemory(Region::kWholeFile, access)) {
57    CloseHandles();
58    return false;
59  }
60
61  return true;
62}
63
64bool MemoryMappedFile::Initialize(File file, Access access) {
65  DCHECK_NE(READ_WRITE_EXTEND, access);
66  return Initialize(std::move(file), Region::kWholeFile, access);
67}
68
69bool MemoryMappedFile::Initialize(File file,
70                                  const Region& region,
71                                  Access access) {
72  switch (access) {
73    case READ_WRITE_EXTEND:
74      // Ensure that the extended size is within limits of File.
75      if (region.size > std::numeric_limits<int64_t>::max() - region.offset) {
76        DLOG(ERROR) << "Region bounds exceed maximum for base::File.";
77        return false;
78      }
79      // Fall through.
80    case READ_ONLY:
81    case READ_WRITE:
82      // Ensure that the region values are valid.
83      if (region.offset < 0 || region.size < 0) {
84        DLOG(ERROR) << "Region bounds are not valid.";
85        return false;
86      }
87      break;
88  }
89
90  if (IsValid())
91    return false;
92
93  if (region != Region::kWholeFile) {
94    DCHECK_GE(region.offset, 0);
95    DCHECK_GT(region.size, 0);
96  }
97
98  file_ = std::move(file);
99
100  if (!MapFileRegionToMemory(region, access)) {
101    CloseHandles();
102    return false;
103  }
104
105  return true;
106}
107
108bool MemoryMappedFile::IsValid() const {
109  return data_ != NULL;
110}
111
112// static
113void MemoryMappedFile::CalculateVMAlignedBoundaries(int64_t start,
114                                                    int64_t size,
115                                                    int64_t* aligned_start,
116                                                    int64_t* aligned_size,
117                                                    int32_t* offset) {
118  // Sadly, on Windows, the mmap alignment is not just equal to the page size.
119  const int64_t mask =
120      static_cast<int64_t>(SysInfo::VMAllocationGranularity()) - 1;
121  DCHECK_LT(mask, std::numeric_limits<int32_t>::max());
122  *offset = start & mask;
123  *aligned_start = start & ~mask;
124  *aligned_size = (size + *offset + mask) & ~mask;
125}
126#endif
127
128}  // namespace base
129