| 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) {
|
|
|