Unverified Commit 9c7ba5f4 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #855 from capnproto/empty-mmap

Fix filesystem API: Empty mmaps should succeed.
parents 6b6f824a 97673947
...@@ -276,6 +276,11 @@ KJ_TEST("DiskFile") { ...@@ -276,6 +276,11 @@ KJ_TEST("DiskFile") {
KJ_EXPECT(file->readAllText() == ""); KJ_EXPECT(file->readAllText() == "");
// mmaping empty file should work
KJ_EXPECT(file->mmap(0, 0).size() == 0);
KJ_EXPECT(file->mmapPrivate(0, 0).size() == 0);
KJ_EXPECT(file->mmapWritable(0, 0)->get().size() == 0);
file->writeAll("foo"); file->writeAll("foo");
KJ_EXPECT(file->readAllText() == "foo"); KJ_EXPECT(file->readAllText() == "foo");
...@@ -294,6 +299,14 @@ KJ_TEST("DiskFile") { ...@@ -294,6 +299,14 @@ KJ_TEST("DiskFile") {
file->truncate(18); file->truncate(18);
KJ_EXPECT(file->readAllText() == kj::StringPtr("foobaz\0\0\0\0\0\0\0\0\0\0\0\0", 18)); KJ_EXPECT(file->readAllText() == kj::StringPtr("foobaz\0\0\0\0\0\0\0\0\0\0\0\0", 18));
// empty mappings work, even if useless
KJ_EXPECT(file->mmap(0, 0).size() == 0);
KJ_EXPECT(file->mmapPrivate(0, 0).size() == 0);
KJ_EXPECT(file->mmapWritable(0, 0)->get().size() == 0);
KJ_EXPECT(file->mmap(2, 0).size() == 0);
KJ_EXPECT(file->mmapPrivate(2, 0).size() == 0);
KJ_EXPECT(file->mmapWritable(2, 0)->get().size() == 0);
{ {
auto mapping = file->mmap(0, 18); auto mapping = file->mmap(0, 18);
auto privateMapping = file->mmapPrivate(0, 18); auto privateMapping = file->mmapPrivate(0, 18);
......
...@@ -346,6 +346,7 @@ public: ...@@ -346,6 +346,7 @@ public:
} }
Array<const byte> mmap(uint64_t offset, uint64_t size) const { Array<const byte> mmap(uint64_t offset, uint64_t size) const {
if (size == 0) return nullptr; // zero-length mmap() returns EINVAL, so avoid it
auto range = getMmapRange(offset, size); auto range = getMmapRange(offset, size);
const void* mapping = ::mmap(NULL, range.size, PROT_READ, MAP_SHARED, fd, range.offset); const void* mapping = ::mmap(NULL, range.size, PROT_READ, MAP_SHARED, fd, range.offset);
if (mapping == MAP_FAILED) { if (mapping == MAP_FAILED) {
...@@ -356,6 +357,7 @@ public: ...@@ -356,6 +357,7 @@ public:
} }
Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const { Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const {
if (size == 0) return nullptr; // zero-length mmap() returns EINVAL, so avoid it
auto range = getMmapRange(offset, size); auto range = getMmapRange(offset, size);
void* mapping = ::mmap(NULL, range.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, range.offset); void* mapping = ::mmap(NULL, range.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, range.offset);
if (mapping == MAP_FAILED) { if (mapping == MAP_FAILED) {
...@@ -454,6 +456,7 @@ public: ...@@ -454,6 +456,7 @@ public:
void changed(ArrayPtr<byte> slice) const override { void changed(ArrayPtr<byte> slice) const override {
KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(), KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(),
"byte range is not part of this mapping"); "byte range is not part of this mapping");
if (slice.size() == 0) return;
// msync() requires page-alignment, apparently, so use getMmapRange() to accomplish that. // msync() requires page-alignment, apparently, so use getMmapRange() to accomplish that.
auto range = getMmapRange(reinterpret_cast<uintptr_t>(slice.begin()), slice.size()); auto range = getMmapRange(reinterpret_cast<uintptr_t>(slice.begin()), slice.size());
...@@ -463,6 +466,7 @@ public: ...@@ -463,6 +466,7 @@ public:
void sync(ArrayPtr<byte> slice) const override { void sync(ArrayPtr<byte> slice) const override {
KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(), KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(),
"byte range is not part of this mapping"); "byte range is not part of this mapping");
if (slice.size() == 0) return;
// msync() requires page-alignment, apparently, so use getMmapRange() to accomplish that. // msync() requires page-alignment, apparently, so use getMmapRange() to accomplish that.
auto range = getMmapRange(reinterpret_cast<uintptr_t>(slice.begin()), slice.size()); auto range = getMmapRange(reinterpret_cast<uintptr_t>(slice.begin()), slice.size());
...@@ -474,6 +478,10 @@ public: ...@@ -474,6 +478,10 @@ public:
}; };
Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const { Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const {
if (size == 0) {
// zero-length mmap() returns EINVAL, so avoid it
return heap<WritableFileMappingImpl>(nullptr);
}
auto range = getMmapRange(offset, size); auto range = getMmapRange(offset, size);
void* mapping = ::mmap(NULL, range.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, range.offset); void* mapping = ::mmap(NULL, range.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, range.offset);
if (mapping == MAP_FAILED) { if (mapping == MAP_FAILED) {
......
...@@ -307,16 +307,7 @@ constexpr MmapDisposer mmapDisposer = MmapDisposer(); ...@@ -307,16 +307,7 @@ constexpr MmapDisposer mmapDisposer = MmapDisposer();
void* win32Mmap(HANDLE handle, MmapRange range, DWORD pageProtect, DWORD access) { void* win32Mmap(HANDLE handle, MmapRange range, DWORD pageProtect, DWORD access) {
HANDLE mappingHandle; HANDLE mappingHandle;
mappingHandle = CreateFileMappingW(handle, NULL, pageProtect, 0, 0, NULL); KJ_WIN32(mappingHandle = CreateFileMappingW(handle, NULL, pageProtect, 0, 0, NULL));
if (mappingHandle == INVALID_HANDLE_VALUE) {
auto error = GetLastError();
if (error == ERROR_FILE_INVALID && range.size == 0) {
// The documentation says that CreateFileMapping will fail with ERROR_FILE_INVALID if the
// file size is zero. Ugh.
return nullptr;
}
KJ_FAIL_WIN32("CreateFileMapping", error);
}
KJ_DEFER(KJ_WIN32(CloseHandle(mappingHandle)) { break; }); KJ_DEFER(KJ_WIN32(CloseHandle(mappingHandle)) { break; });
void* mapping = MapViewOfFile(mappingHandle, access, void* mapping = MapViewOfFile(mappingHandle, access,
...@@ -428,6 +419,7 @@ public: ...@@ -428,6 +419,7 @@ public:
} }
Array<const byte> mmap(uint64_t offset, uint64_t size) const { Array<const byte> mmap(uint64_t offset, uint64_t size) const {
if (size == 0) return nullptr; // Windows won't allow zero-length mappings
auto range = getMmapRange(offset, size); auto range = getMmapRange(offset, size);
const void* mapping = win32Mmap(handle, range, PAGE_READONLY, FILE_MAP_READ); const void* mapping = win32Mmap(handle, range, PAGE_READONLY, FILE_MAP_READ);
return Array<const byte>(reinterpret_cast<const byte*>(mapping) + (offset - range.offset), return Array<const byte>(reinterpret_cast<const byte*>(mapping) + (offset - range.offset),
...@@ -435,6 +427,7 @@ public: ...@@ -435,6 +427,7 @@ public:
} }
Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const { Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const {
if (size == 0) return nullptr; // Windows won't allow zero-length mappings
auto range = getMmapRange(offset, size); auto range = getMmapRange(offset, size);
void* mapping = win32Mmap(handle, range, PAGE_READONLY, FILE_MAP_COPY); void* mapping = win32Mmap(handle, range, PAGE_READONLY, FILE_MAP_COPY);
return Array<byte>(reinterpret_cast<byte*>(mapping) + (offset - range.offset), return Array<byte>(reinterpret_cast<byte*>(mapping) + (offset - range.offset),
...@@ -552,7 +545,8 @@ public: ...@@ -552,7 +545,8 @@ public:
KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(), KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(),
"byte range is not part of this mapping"); "byte range is not part of this mapping");
// Zero is treated specially by FlushViewOfFile(), so check for it. // Zero is treated specially by FlushViewOfFile(), so check for it. (This also handles the
// case where `bytes` is actually empty and not a real mapping.)
if (slice.size() > 0) { if (slice.size() > 0) {
KJ_WIN32(FlushViewOfFile(slice.begin(), slice.size())); KJ_WIN32(FlushViewOfFile(slice.begin(), slice.size()));
} }
...@@ -563,6 +557,10 @@ public: ...@@ -563,6 +557,10 @@ public:
}; };
Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const { Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const {
if (size == 0) {
// Windows won't allow zero-length mappings
return heap<WritableFileMappingImpl>(nullptr);
}
auto range = getMmapRange(offset, size); auto range = getMmapRange(offset, size);
void* mapping = win32Mmap(handle, range, PAGE_READWRITE, FILE_MAP_ALL_ACCESS); void* mapping = win32Mmap(handle, range, PAGE_READWRITE, FILE_MAP_ALL_ACCESS);
auto array = Array<byte>(reinterpret_cast<byte*>(mapping) + (offset - range.offset), auto array = Array<byte>(reinterpret_cast<byte*>(mapping) + (offset - range.offset),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment