leveldb-1.23_windows_paths.patch 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. diff --git a/util/env_windows.cc b/util/env_windows.cc
  2. index 449f564..812c728 100644
  3. --- a/util/env_windows.cc
  4. +++ b/util/env_windows.cc
  5. @@ -375,8 +375,9 @@ class WindowsEnv : public Env {
  6. *result = nullptr;
  7. DWORD desired_access = GENERIC_READ;
  8. DWORD share_mode = FILE_SHARE_READ;
  9. - ScopedHandle handle = ::CreateFileA(
  10. - filename.c_str(), desired_access, share_mode,
  11. + auto wFilename = toUtf16(filename);
  12. + ScopedHandle handle = ::CreateFileW(
  13. + wFilename.c_str(), desired_access, share_mode,
  14. /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
  15. /*hTemplateFile=*/nullptr);
  16. if (!handle.is_valid()) {
  17. @@ -392,8 +393,9 @@ class WindowsEnv : public Env {
  18. *result = nullptr;
  19. DWORD desired_access = GENERIC_READ;
  20. DWORD share_mode = FILE_SHARE_READ;
  21. + auto wFilename = toUtf16(filename);
  22. ScopedHandle handle =
  23. - ::CreateFileA(filename.c_str(), desired_access, share_mode,
  24. + ::CreateFileW(wFilename.c_str(), desired_access, share_mode,
  25. /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
  26. FILE_ATTRIBUTE_READONLY,
  27. /*hTemplateFile=*/nullptr);
  28. @@ -413,11 +415,12 @@ class WindowsEnv : public Env {
  29. }
  30. ScopedHandle mapping =
  31. - ::CreateFileMappingA(handle.get(),
  32. - /*security attributes=*/nullptr, PAGE_READONLY,
  33. - /*dwMaximumSizeHigh=*/0,
  34. - /*dwMaximumSizeLow=*/0,
  35. - /*lpName=*/nullptr);
  36. + ::CreateFileMappingW(handle.get(),
  37. + /*security attributes=*/nullptr,
  38. + PAGE_READONLY,
  39. + /*dwMaximumSizeHigh=*/0,
  40. + /*dwMaximumSizeLow=*/0,
  41. + /*lpName=*/nullptr);
  42. if (mapping.is_valid()) {
  43. void* mmap_base = ::MapViewOfFile(mapping.get(), FILE_MAP_READ,
  44. /*dwFileOffsetHigh=*/0,
  45. @@ -438,8 +441,9 @@ class WindowsEnv : public Env {
  46. WritableFile** result) override {
  47. DWORD desired_access = GENERIC_WRITE;
  48. DWORD share_mode = 0; // Exclusive access.
  49. - ScopedHandle handle = ::CreateFileA(
  50. - filename.c_str(), desired_access, share_mode,
  51. + auto wFilename = toUtf16(filename);
  52. + ScopedHandle handle = ::CreateFileW(
  53. + wFilename.c_str(), desired_access, share_mode,
  54. /*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  55. /*hTemplateFile=*/nullptr);
  56. if (!handle.is_valid()) {
  57. @@ -455,8 +459,9 @@ class WindowsEnv : public Env {
  58. WritableFile** result) override {
  59. DWORD desired_access = FILE_APPEND_DATA;
  60. DWORD share_mode = 0; // Exclusive access.
  61. - ScopedHandle handle = ::CreateFileA(
  62. - filename.c_str(), desired_access, share_mode,
  63. + auto wFilename = toUtf16(filename);
  64. + ScopedHandle handle = ::CreateFileW(
  65. + wFilename.c_str(), desired_access, share_mode,
  66. /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  67. /*hTemplateFile=*/nullptr);
  68. if (!handle.is_valid()) {
  69. @@ -469,14 +474,16 @@ class WindowsEnv : public Env {
  70. }
  71. bool FileExists(const std::string& filename) override {
  72. - return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
  73. + auto wFilename = toUtf16(filename);
  74. + return GetFileAttributesW(wFilename.c_str()) != INVALID_FILE_ATTRIBUTES;
  75. }
  76. Status GetChildren(const std::string& directory_path,
  77. std::vector<std::string>* result) override {
  78. const std::string find_pattern = directory_path + "\\*";
  79. - WIN32_FIND_DATAA find_data;
  80. - HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
  81. + WIN32_FIND_DATAW find_data;
  82. + auto wFind_pattern = toUtf16(find_pattern);
  83. + HANDLE dir_handle = ::FindFirstFileW(wFind_pattern.c_str(), &find_data);
  84. if (dir_handle == INVALID_HANDLE_VALUE) {
  85. DWORD last_error = ::GetLastError();
  86. if (last_error == ERROR_FILE_NOT_FOUND) {
  87. @@ -488,11 +495,12 @@ class WindowsEnv : public Env {
  88. char base_name[_MAX_FNAME];
  89. char ext[_MAX_EXT];
  90. - if (!_splitpath_s(find_data.cFileName, nullptr, 0, nullptr, 0, base_name,
  91. + auto find_data_filename = toUtf8(find_data.cFileName);
  92. + if (!_splitpath_s(find_data_filename.c_str(), nullptr, 0, nullptr, 0, base_name,
  93. ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) {
  94. result->emplace_back(std::string(base_name) + ext);
  95. }
  96. - } while (::FindNextFileA(dir_handle, &find_data));
  97. + } while (::FindNextFileW(dir_handle, &find_data));
  98. DWORD last_error = ::GetLastError();
  99. ::FindClose(dir_handle);
  100. if (last_error != ERROR_NO_MORE_FILES) {
  101. @@ -501,22 +509,25 @@ class WindowsEnv : public Env {
  102. return Status::OK();
  103. }
  104. - Status RemoveFile(const std::string& filename) override {
  105. - if (!::DeleteFileA(filename.c_str())) {
  106. + Status RemoveFile(const std::string& filename) {
  107. + auto wFilename = toUtf16(filename);
  108. + if (!::DeleteFileW(wFilename.c_str())) {
  109. return WindowsError(filename, ::GetLastError());
  110. }
  111. return Status::OK();
  112. }
  113. Status CreateDir(const std::string& dirname) override {
  114. - if (!::CreateDirectoryA(dirname.c_str(), nullptr)) {
  115. + auto wDirname = toUtf16(dirname);
  116. + if (!::CreateDirectoryW(wDirname.c_str(), nullptr)) {
  117. return WindowsError(dirname, ::GetLastError());
  118. }
  119. return Status::OK();
  120. }
  121. Status RemoveDir(const std::string& dirname) override {
  122. - if (!::RemoveDirectoryA(dirname.c_str())) {
  123. + auto wDirname = toUtf16(dirname);
  124. + if (!::RemoveDirectoryW(wDirname.c_str())) {
  125. return WindowsError(dirname, ::GetLastError());
  126. }
  127. return Status::OK();
  128. @@ -524,7 +535,8 @@ class WindowsEnv : public Env {
  129. Status GetFileSize(const std::string& filename, uint64_t* size) override {
  130. WIN32_FILE_ATTRIBUTE_DATA file_attributes;
  131. - if (!::GetFileAttributesExA(filename.c_str(), GetFileExInfoStandard,
  132. + auto wFilename = toUtf16(filename);
  133. + if (!::GetFileAttributesExW(wFilename.c_str(), GetFileExInfoStandard,
  134. &file_attributes)) {
  135. return WindowsError(filename, ::GetLastError());
  136. }
  137. @@ -538,7 +550,9 @@ class WindowsEnv : public Env {
  138. Status RenameFile(const std::string& from, const std::string& to) override {
  139. // Try a simple move first. It will only succeed when |to| doesn't already
  140. // exist.
  141. - if (::MoveFileA(from.c_str(), to.c_str())) {
  142. + auto wFrom = toUtf16(from);
  143. + auto wTo = toUtf16(to);
  144. + if (::MoveFileW(wFrom.c_str(), wTo.c_str())) {
  145. return Status::OK();
  146. }
  147. DWORD move_error = ::GetLastError();
  148. @@ -547,7 +561,7 @@ class WindowsEnv : public Env {
  149. // succeed when |to| does exist. When writing to a network share, we may not
  150. // be able to change the ACLs. Ignore ACL errors then
  151. // (REPLACEFILE_IGNORE_MERGE_ERRORS).
  152. - if (::ReplaceFileA(to.c_str(), from.c_str(), /*lpBackupFileName=*/nullptr,
  153. + if (::ReplaceFileW(wTo.c_str(), wFrom.c_str(), /*lpBackupFileName=*/nullptr,
  154. REPLACEFILE_IGNORE_MERGE_ERRORS,
  155. /*lpExclude=*/nullptr, /*lpReserved=*/nullptr)) {
  156. return Status::OK();
  157. @@ -567,8 +581,9 @@ class WindowsEnv : public Env {
  158. Status LockFile(const std::string& filename, FileLock** lock) override {
  159. *lock = nullptr;
  160. Status result;
  161. - ScopedHandle handle = ::CreateFileA(
  162. - filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
  163. + auto wFilename = toUtf16(filename);
  164. + ScopedHandle handle = ::CreateFileW(
  165. + wFilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
  166. /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  167. nullptr);
  168. if (!handle.is_valid()) {
  169. @@ -608,10 +623,11 @@ class WindowsEnv : public Env {
  170. return Status::OK();
  171. }
  172. - char tmp_path[MAX_PATH];
  173. - if (!GetTempPathA(ARRAYSIZE(tmp_path), tmp_path)) {
  174. + wchar_t wtmp_path[MAX_PATH];
  175. + if (!GetTempPathW(ARRAYSIZE(wtmp_path), wtmp_path)) {
  176. return WindowsError("GetTempPath", ::GetLastError());
  177. }
  178. + std::string tmp_path = toUtf8(std::wstring(wtmp_path));
  179. std::stringstream ss;
  180. ss << tmp_path << "leveldbtest-" << std::this_thread::get_id();
  181. *result = ss.str();
  182. @@ -622,7 +638,8 @@ class WindowsEnv : public Env {
  183. }
  184. Status NewLogger(const std::string& filename, Logger** result) override {
  185. - std::FILE* fp = std::fopen(filename.c_str(), "w");
  186. + auto wFilename = toUtf16(filename);
  187. + std::FILE* fp = _wfopen(wFilename.c_str(), L"w");
  188. if (fp == nullptr) {
  189. *result = nullptr;
  190. return WindowsError(filename, ::GetLastError());
  191. @@ -678,6 +695,31 @@ class WindowsEnv : public Env {
  192. GUARDED_BY(background_work_mutex_);
  193. Limiter mmap_limiter_; // Thread-safe.
  194. +
  195. + // Converts a Windows wide multi-byte UTF-16 string to a UTF-8 string.
  196. + // See http://utf8everywhere.org/#windows
  197. + std::string toUtf8(const std::wstring& wstr) {
  198. + if (wstr.empty()) return std::string();
  199. + int size_needed = WideCharToMultiByte(
  200. + CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
  201. + std::string strTo(size_needed, 0);
  202. + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0],
  203. + size_needed, NULL, NULL);
  204. + return strTo;
  205. + }
  206. +
  207. + // Converts a UTF-8 string to a Windows UTF-16 multi-byte wide character
  208. + // string.
  209. + // See http://utf8everywhere.org/#windows
  210. + std::wstring toUtf16(const std::string& str) {
  211. + if (str.empty()) return std::wstring();
  212. + int size_needed = MultiByteToWideChar(
  213. + CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
  214. + std::wstring strTo(size_needed, 0);
  215. + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &strTo[0],
  216. + size_needed);
  217. + return strTo;
  218. + }
  219. };
  220. // Return the maximum number of concurrent mmaps.
  221. diff --git a/util/env_windows_test.cc b/util/env_windows_test.cc
  222. index d6822d2..d108ad9 100644
  223. --- a/util/env_windows_test.cc
  224. +++ b/util/env_windows_test.cc
  225. @@ -55,6 +55,70 @@ TEST_F(EnvWindowsTest, TestOpenOnRead) {
  226. ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
  227. }
  228. +TEST_F(EnvWindowsTest, TestOpenOnRead_Unicode) {
  229. + // Write some test data to a single file that will be opened |n| times.
  230. + std::string test_dir;
  231. + ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  232. + std::string test_file = test_dir + u8"/open_on_run🏃_read.txt";
  233. +
  234. + std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
  235. + std::wstring wideUtf8Path = converter.from_bytes(test_file);
  236. + FILE* f = _wfopen(wideUtf8Path.c_str(), L"w");
  237. + ASSERT_TRUE(f != nullptr);
  238. + const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
  239. + fputs(kFileData, f);
  240. + fclose(f);
  241. +
  242. + // Open test file some number above the sum of the two limits to force
  243. + // leveldb::WindowsEnv to switch from mapping the file into memory
  244. + // to basic file reading.
  245. + const int kNumFiles = kMMapLimit + 5;
  246. + leveldb::RandomAccessFile* files[kNumFiles] = {0};
  247. + for (int i = 0; i < kNumFiles; i++) {
  248. + ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(test_file, &files[i]));
  249. + }
  250. + char scratch;
  251. + Slice read_result;
  252. + for (int i = 0; i < kNumFiles; i++) {
  253. + ASSERT_LEVELDB_OK(files[i]->Read(i, 1, &read_result, &scratch));
  254. + ASSERT_EQ(kFileData[i], read_result[0]);
  255. + }
  256. + for (int i = 0; i < kNumFiles; i++) {
  257. + delete files[i];
  258. + }
  259. + ASSERT_LEVELDB_OK(env_->DeleteFile(test_file));
  260. +}
  261. +
  262. +TEST_F(EnvWindowsTest, TestGetChildrenEmpty) {
  263. + // Create some dummy files.
  264. + std::string test_dir;
  265. + ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  266. +
  267. + std::vector<std::string> result;
  268. + ASSERT_LEVELDB_OK(env_->GetChildren(test_dir, &result));
  269. + ASSERT_EQ(2, result.size()); // "." and ".." are always returned.
  270. +}
  271. +
  272. +TEST_F(EnvWindowsTest, TestGetChildren_ChildFiles) {
  273. + // Create some dummy files.
  274. + std::string test_dir;
  275. + ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  276. +
  277. + int childFilesCount = 10;
  278. + for (int i = 0; i < childFilesCount; i++) {
  279. + std::string test_file = test_dir + u8"/run🏃_and_jump🦘_" + std::to_string(i) + ".txt";
  280. + std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
  281. + std::wstring wTest_file = converter.from_bytes(test_file);
  282. + FILE* f = _wfopen(wTest_file.c_str(), L"w");
  283. + ASSERT_TRUE(f != nullptr);
  284. + fclose(f);
  285. + }
  286. +
  287. + std::vector<std::string> result;
  288. + ASSERT_LEVELDB_OK(env_->GetChildren(test_dir, &result));
  289. + ASSERT_EQ(childFilesCount + 2, result.size()); // "." and ".." are returned.
  290. +}
  291. +
  292. } // namespace leveldb
  293. int main(int argc, char** argv) {