Bläddra i källkod

Fix #2435: allow mmap to open files held open for writing (#2438)

* Add test for #2435 mmap::open with concurrent writer

Verifies that detail::mmap can open a file held open with GENERIC_WRITE
by another handle (e.g. an active log file). Currently fails on Windows
because CreateFile2 omits FILE_SHARE_WRITE.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix #2435: allow mmap to open files held open for writing

Add FILE_SHARE_WRITE to the share mode passed to ::CreateFile2 so
detail::mmap can open a file even when another process holds it open
with GENERIC_WRITE (e.g. an active log file). Without this, CreateFile2
fails with ERROR_SHARING_VIOLATION because the new opener's share mode
must permit the existing handle's access mode.

This brings the Windows path's behavior in line with the POSIX path
which uses ::open(O_RDONLY) and is unaffected by other processes'
write handles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
yhirose 3 månader sedan
förälder
incheckning
c2678f0186
2 ändrade filer med 28 tillägg och 2 borttagningar
  1. 3 2
      httplib.h
  2. 25 0
      test/test.cc

+ 3 - 2
httplib.h

@@ -5319,8 +5319,9 @@ inline bool mmap::open(const char *path) {
   auto wpath = u8string_to_wstring(path);
   if (wpath.empty()) { return false; }
 
-  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
-                         OPEN_EXISTING, NULL);
+  hFile_ =
+      ::CreateFile2(wpath.c_str(), GENERIC_READ,
+                    FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING, NULL);
 
   if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
 

+ 25 - 0
test/test.cc

@@ -7939,6 +7939,31 @@ TEST(MountTest, MultibytesPathName) {
   EXPECT_EQ(U8("日本語コンテンツ"), res->body);
 }
 
+#ifdef _WIN32
+// Issue #2435: mmap::open() must succeed even when another handle holds
+// the file open for writing (e.g. an active log file).
+TEST(MmapTest, OpenWhileFileHeldForWriting) {
+  const char *path = "mmap_concurrent_writer_test.txt";
+  const char *content = "hello";
+
+  {
+    std::ofstream f(path, std::ios::binary);
+    f.write(content, static_cast<std::streamsize>(strlen(content)));
+  }
+  auto file_cleanup = detail::scope_exit([&] { std::remove(path); });
+
+  HANDLE writer = ::CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  ASSERT_NE(INVALID_HANDLE_VALUE, writer);
+  auto handle_cleanup = detail::scope_exit([&] { ::CloseHandle(writer); });
+
+  detail::mmap m(path);
+  ASSERT_TRUE(m.is_open());
+  EXPECT_EQ(strlen(content), m.size());
+  EXPECT_EQ(0, std::memcmp(content, m.data(), strlen(content)));
+}
+#endif
+
 TEST(KeepAliveTest, ReadTimeout) {
   Server svr;