Chromium Code Reviews| 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..9028757c49a675d5d74c8ca1859de0b4d4af6e4a 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,72 @@ 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) { |
| + if (i > original_file_len) { |
|
Mark Mentovai
2017/05/05 18:31:44
I don’t see anything here that requires file_ to h
bcwhite
2017/05/08 14:15:35
Correct. It's making the assumption that whatever
|
| + if (pwrite(file_.GetPlatformFile(), "", 1, i) != 1) |
| + return false; |
| + } |
| + } |
| + } |
| + |
| break; |
| } |
| + |
| data_ = static_cast<uint8_t*>(mmap(NULL, map_size, flags, MAP_SHARED, |
| file_.GetPlatformFile(), map_start)); |
| if (data_ == MAP_FAILED) { |