Index: base/files/memory_mapped_file_posix.cc |
diff --git a/base/files/memory_mapped_file_posix.cc b/base/files/memory_mapped_file_posix.cc |
index 90ba6f49c1573f6fb4fb1f56bc33f35b36febb06..55b754d83a01723e8e7aa76475bd90d299bca266 100644 |
--- a/base/files/memory_mapped_file_posix.cc |
+++ b/base/files/memory_mapped_file_posix.cc |
@@ -4,6 +4,7 @@ |
#include "base/files/memory_mapped_file.h" |
+#include <fcntl.h> |
#include <stddef.h> |
#include <stdint.h> |
#include <sys/mman.h> |
@@ -14,6 +15,10 @@ |
#include "base/threading/thread_restrictions.h" |
#include "build/build_config.h" |
+#if defined(OS_ANDROID) |
+#include <android/api-level.h> |
+#endif |
+ |
namespace base { |
MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) { |
@@ -71,22 +76,75 @@ bool MemoryMappedFile::MapFileRegionToMemory( |
case READ_ONLY: |
flags |= PROT_READ; |
break; |
+ |
case READ_WRITE: |
flags |= PROT_READ | PROT_WRITE; |
break; |
+ |
case READ_WRITE_EXTEND: |
+ flags |= PROT_READ | PROT_WRITE; |
+ |
// POSIX won't auto-extend the file when it is written so it must first |
// be explicitly extended to the maximum size. Zeros will fill the new |
// space. |
- auto file_len = file_.GetLength(); |
- if (file_len < 0) { |
+ const int64_t original_file_len = file_.GetLength(); |
+ if (original_file_len < 0) { |
DPLOG(ERROR) << "fstat " << file_.GetPlatformFile(); |
return false; |
} |
- file_.SetLength(std::max(file_len, region.offset + region.size)); |
- flags |= PROT_READ | PROT_WRITE; |
+ |
+ // Increase the actual length of the file, if necessary. This can fail if |
+ // the disk is full and the OS doesn't support sparse files. |
+ if (!file_.SetLength( |
+ std::max(original_file_len, region.offset + region.size))) { |
+ DPLOG(ERROR) << "ftruncate " << file_.GetPlatformFile(); |
+ return false; |
+ } |
+ |
+ // Realize the extent of the file so that it can't fail (and crash) later |
+ // when trying to write to a memory page that can't be created. This can |
+ // fail if the disk is full and the file is sparse. |
+ // |
+ // Only Android API>=21 supports the fallocate call. Older versions need |
+ // to manually extend the file by writing zeros at block intervals. |
+ // |
+ // Mac OSX doesn't support this call but the primary filesystem doesn't |
+ // support sparse files so is unneeded. |
+ bool do_manual_extension = false; |
+ |
+#if defined(OS_ANDROID) && __ANDROID_API__ < 21 |
+ do_manual_extension = true; |
+#elif !defined(OS_MACOSX) |
+ if (posix_fallocate(file_.GetPlatformFile(), region.offset, |
+ region.size) != 0) { |
+ DPLOG(ERROR) << "posix_fallocate " << file_.GetPlatformFile(); |
+ // This can fail because the filesystem doesn't support it so don't |
+ // give up just yet. Try the manual method below. |
+ do_manual_extension = true; |
+ } |
+#endif |
+ |
+ // Manually realize the entire file by writing bytes to it at intervals. |
+ if (do_manual_extension) { |
+ int64_t block_size = 1024; // Start with something safe. |
+ struct stat statbuf; |
+ if (fstat(file_.GetPlatformFile(), &statbuf) == 0) |
+ block_size = statbuf.st_blksize; |
+ const off_t map_end = map_start + static_cast<off_t>(map_size); |
+ for (off_t i = map_start; i < map_end; i += block_size) { |
+ char existing_byte; |
+ if (pread(file_.GetPlatformFile(), &existing_byte, 1, i) != 1) |
+ return false; // Can't read? Not viable. |
+ if (existing_byte != 0) |
+ continue; // Block has data so must already exist. |
+ if (pwrite(file_.GetPlatformFile(), &existing_byte, 1, i) != 1) |
+ return false; // Can't write? Not viable. |
+ } |
+ } |
+ |
break; |
} |
+ |
data_ = static_cast<uint8_t*>(mmap(NULL, map_size, flags, MAP_SHARED, |
file_.GetPlatformFile(), map_start)); |
if (data_ == MAP_FAILED) { |