blob: b1efe887a05a77d23965d6789147797213574f69 [file] [log] [blame]
[email protected]c3088cb2013-02-24 21:55:451// 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
bcwhitee41b5ce82017-05-11 20:27:507#include <fcntl.h>
avi543540e2015-12-24 05:15:328#include <stddef.h>
9#include <stdint.h>
[email protected]c3088cb2013-02-24 21:55:4510#include <sys/mman.h>
11#include <sys/stat.h>
12#include <unistd.h>
13
14#include "base/logging.h"
[email protected]c3088cb2013-02-24 21:55:4515#include "base/threading/thread_restrictions.h"
avi543540e2015-12-24 05:15:3216#include "build/build_config.h"
[email protected]c3088cb2013-02-24 21:55:4517
bcwhitee41b5ce82017-05-11 20:27:5018#if defined(OS_ANDROID)
19#include <android/api-level.h>
20#endif
21
[email protected]c3088cb2013-02-24 21:55:4522namespace base {
23
[email protected]58ba3ea2014-01-03 22:14:1524MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) {
[email protected]c3088cb2013-02-24 21:55:4525}
26
mkosiba3c766cc2015-01-09 13:10:2227#if !defined(OS_NACL)
[email protected]3fa9ff82014-07-28 17:57:4028bool MemoryMappedFile::MapFileRegionToMemory(
bcwhitef0dfab02016-05-19 21:18:3329 const MemoryMappedFile::Region& region,
30 Access access) {
[email protected]c3088cb2013-02-24 21:55:4531 ThreadRestrictions::AssertIOAllowed();
32
[email protected]3fa9ff82014-07-28 17:57:4033 off_t map_start = 0;
34 size_t map_size = 0;
avid0181f32015-12-10 19:41:4735 int32_t data_offset = 0;
[email protected]3fa9ff82014-07-28 17:57:4036
37 if (region == MemoryMappedFile::Region::kWholeFile) {
avid0181f32015-12-10 19:41:4738 int64_t file_len = file_.GetLength();
mkolom089ec1a22016-12-09 10:25:0739 if (file_len < 0) {
[email protected]3fa9ff82014-07-28 17:57:4040 DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
41 return false;
42 }
43 map_size = static_cast<size_t>(file_len);
44 length_ = map_size;
45 } else {
46 // The region can be arbitrarily aligned. mmap, instead, requires both the
47 // start and size to be page-aligned. Hence, we map here the page-aligned
48 // outer region [|aligned_start|, |aligned_start| + |size|] which contains
49 // |region| and then add up the |data_offset| displacement.
avid0181f32015-12-10 19:41:4750 int64_t aligned_start = 0;
51 int64_t aligned_size = 0;
[email protected]3fa9ff82014-07-28 17:57:4052 CalculateVMAlignedBoundaries(region.offset,
53 region.size,
54 &aligned_start,
55 &aligned_size,
56 &data_offset);
57
58 // Ensure that the casts in the mmap call below are sane.
59 if (aligned_start < 0 || aligned_size < 0 ||
60 aligned_start > std::numeric_limits<off_t>::max() ||
avid0181f32015-12-10 19:41:4761 static_cast<uint64_t>(aligned_size) >
[email protected]3fa9ff82014-07-28 17:57:4062 std::numeric_limits<size_t>::max() ||
avid0181f32015-12-10 19:41:4763 static_cast<uint64_t>(region.size) >
64 std::numeric_limits<size_t>::max()) {
[email protected]3fa9ff82014-07-28 17:57:4065 DLOG(ERROR) << "Region bounds are not valid for mmap";
66 return false;
67 }
68
69 map_start = static_cast<off_t>(aligned_start);
70 map_size = static_cast<size_t>(aligned_size);
71 length_ = static_cast<size_t>(region.size);
72 }
73
bcwhitef0dfab02016-05-19 21:18:3374 int flags = 0;
75 switch (access) {
76 case READ_ONLY:
77 flags |= PROT_READ;
78 break;
bcwhitee41b5ce82017-05-11 20:27:5079
bcwhitef0dfab02016-05-19 21:18:3380 case READ_WRITE:
81 flags |= PROT_READ | PROT_WRITE;
82 break;
bcwhitee41b5ce82017-05-11 20:27:5083
bcwhitef0dfab02016-05-19 21:18:3384 case READ_WRITE_EXTEND:
bcwhitee41b5ce82017-05-11 20:27:5085 flags |= PROT_READ | PROT_WRITE;
86
bcwhitef8389362017-06-15 15:51:3587 const int64_t new_file_len = region.offset + region.size;
88
bcwhitef0dfab02016-05-19 21:18:3389 // POSIX won't auto-extend the file when it is written so it must first
90 // be explicitly extended to the maximum size. Zeros will fill the new
bcwhitef8389362017-06-15 15:51:3591 // space. It is assumed that the existing file is fully realized as
92 // otherwise the entire file would have to be read and possibly written.
bcwhitee41b5ce82017-05-11 20:27:5093 const int64_t original_file_len = file_.GetLength();
94 if (original_file_len < 0) {
mkolom089ec1a22016-12-09 10:25:0795 DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
96 return false;
97 }
bcwhitee41b5ce82017-05-11 20:27:5098
99 // Increase the actual length of the file, if necessary. This can fail if
100 // the disk is full and the OS doesn't support sparse files.
bcwhitef8389362017-06-15 15:51:35101 if (!file_.SetLength(std::max(original_file_len, new_file_len))) {
bcwhitee41b5ce82017-05-11 20:27:50102 DPLOG(ERROR) << "ftruncate " << file_.GetPlatformFile();
103 return false;
104 }
105
106 // Realize the extent of the file so that it can't fail (and crash) later
107 // when trying to write to a memory page that can't be created. This can
108 // fail if the disk is full and the file is sparse.
109 //
110 // Only Android API>=21 supports the fallocate call. Older versions need
111 // to manually extend the file by writing zeros at block intervals.
112 //
113 // Mac OSX doesn't support this call but the primary filesystem doesn't
114 // support sparse files so is unneeded.
115 bool do_manual_extension = false;
116
117#if defined(OS_ANDROID) && __ANDROID_API__ < 21
118 do_manual_extension = true;
119#elif !defined(OS_MACOSX)
120 if (posix_fallocate(file_.GetPlatformFile(), region.offset,
121 region.size) != 0) {
122 DPLOG(ERROR) << "posix_fallocate " << file_.GetPlatformFile();
123 // This can fail because the filesystem doesn't support it so don't
124 // give up just yet. Try the manual method below.
125 do_manual_extension = true;
126 }
127#endif
128
bcwhitef8389362017-06-15 15:51:35129 // Manually realize the extended file by writing bytes to it at intervals.
bcwhitee41b5ce82017-05-11 20:27:50130 if (do_manual_extension) {
bcwhitef8389362017-06-15 15:51:35131 int64_t block_size = 512; // Start with something safe.
bcwhitee41b5ce82017-05-11 20:27:50132 struct stat statbuf;
bcwhitef8389362017-06-15 15:51:35133 if (fstat(file_.GetPlatformFile(), &statbuf) == 0 &&
134 statbuf.st_blksize > 0) {
bcwhitee41b5ce82017-05-11 20:27:50135 block_size = statbuf.st_blksize;
bcwhitef8389362017-06-15 15:51:35136 }
137
138 // Write starting at the next block boundary after the old file length.
139 const int64_t extension_start =
140 (original_file_len + block_size - 1) & ~(block_size - 1);
141 for (int64_t i = extension_start; i < new_file_len; i += block_size) {
bcwhitee41b5ce82017-05-11 20:27:50142 char existing_byte;
143 if (pread(file_.GetPlatformFile(), &existing_byte, 1, i) != 1)
144 return false; // Can't read? Not viable.
145 if (existing_byte != 0)
146 continue; // Block has data so must already exist.
147 if (pwrite(file_.GetPlatformFile(), &existing_byte, 1, i) != 1)
148 return false; // Can't write? Not viable.
149 }
150 }
151
bcwhitef0dfab02016-05-19 21:18:33152 break;
153 }
bcwhitee41b5ce82017-05-11 20:27:50154
bcwhitef0dfab02016-05-19 21:18:33155 data_ = static_cast<uint8_t*>(mmap(NULL, map_size, flags, MAP_SHARED,
avid0181f32015-12-10 19:41:47156 file_.GetPlatformFile(), map_start));
[email protected]3fa9ff82014-07-28 17:57:40157 if (data_ == MAP_FAILED) {
158 DPLOG(ERROR) << "mmap " << file_.GetPlatformFile();
[email protected]c3088cb2013-02-24 21:55:45159 return false;
160 }
[email protected]c3088cb2013-02-24 21:55:45161
[email protected]3fa9ff82014-07-28 17:57:40162 data_ += data_offset;
163 return true;
[email protected]c3088cb2013-02-24 21:55:45164}
mkosiba3c766cc2015-01-09 13:10:22165#endif
[email protected]c3088cb2013-02-24 21:55:45166
167void MemoryMappedFile::CloseHandles() {
168 ThreadRestrictions::AssertIOAllowed();
169
170 if (data_ != NULL)
171 munmap(data_, length_);
[email protected]58ba3ea2014-01-03 22:14:15172 file_.Close();
[email protected]c3088cb2013-02-24 21:55:45173
174 data_ = NULL;
175 length_ = 0;
[email protected]c3088cb2013-02-24 21:55:45176}
177
178} // namespace base