MemoryFile_Win32.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /*
  2. * Tencent is pleased to support the open source community by making
  3. * MMKV available.
  4. *
  5. * Copyright (C) 2019 THL A29 Limited, a Tencent company.
  6. * All rights reserved.
  7. *
  8. * Licensed under the BSD 3-Clause License (the "License"); you may not use
  9. * this file except in compliance with the License. You may obtain a copy of
  10. * the License at
  11. *
  12. * https://opensource.org/licenses/BSD-3-Clause
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. #include "MemoryFile.h"
  21. #ifdef MMKV_WIN32
  22. # include "InterProcessLock.h"
  23. # include "MMBuffer.h"
  24. # include "MMKVLog.h"
  25. # include "ScopedLock.hpp"
  26. # include "ThreadLock.h"
  27. # include <cassert>
  28. # include <strsafe.h>
  29. # include <filesystem>
  30. using namespace std;
  31. namespace fs = std::filesystem;
  32. namespace mmkv {
  33. static bool getFileSize(MMKVFileHandle_t fd, size_t &size);
  34. static bool getFileSize(const wchar_t *filename, size_t &size);
  35. static bool ftruncate(MMKVFileHandle_t file, size_t size);
  36. File::File(MMKVPath_t path, OpenFlag flag) : m_path(std::move(path)), m_fd(INVALID_HANDLE_VALUE), m_flag(flag) {
  37. open();
  38. }
  39. static pair<int, int> OpenFlag2NativeFlag(OpenFlag flag) {
  40. int access = 0, create = OPEN_EXISTING;
  41. if ((flag & OpenFlagRWMask) == OpenFlag::ReadWrite) {
  42. access = (GENERIC_READ | GENERIC_WRITE);
  43. } else if (flag & OpenFlag::ReadOnly) {
  44. access |= GENERIC_READ;
  45. } else if (flag & OpenFlag::WriteOnly) {
  46. access |= GENERIC_WRITE;
  47. }
  48. if (flag & OpenFlag::Create) {
  49. create = OPEN_ALWAYS;
  50. }
  51. if (flag & OpenFlag::Excel) {
  52. access = CREATE_NEW;
  53. }
  54. if (flag & OpenFlag::Truncate) {
  55. access = CREATE_ALWAYS;
  56. }
  57. return {access, create};
  58. }
  59. bool File::open() {
  60. if (isFileValid()) {
  61. return true;
  62. }
  63. auto pair = OpenFlag2NativeFlag(m_flag);
  64. m_fd = CreateFile(m_path.c_str(), pair.first, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
  65. pair.second, FILE_ATTRIBUTE_NORMAL, nullptr);
  66. if (!isFileValid()) {
  67. MMKVError("fail to open:[%ls], flag %x, error %d", m_path.c_str(), m_flag, GetLastError());
  68. return false;
  69. }
  70. MMKVInfo("open fd[%p], flag %x, %ls", m_fd, m_flag, m_path.c_str());
  71. return true;
  72. }
  73. void File::close() {
  74. if (isFileValid()) {
  75. MMKVInfo("closing fd[%p], %ls", m_fd, m_path.c_str());
  76. if (CloseHandle(m_fd)) {
  77. m_fd = INVALID_HANDLE_VALUE;
  78. } else {
  79. MMKVError("fail to close [%ls], %d", m_path.c_str(), GetLastError());
  80. }
  81. }
  82. }
  83. size_t File::getActualFileSize() const {
  84. size_t size = 0;
  85. if (isFileValid()) {
  86. mmkv::getFileSize(m_fd, size);
  87. } else {
  88. mmkv::getFileSize(m_path.c_str(), size);
  89. }
  90. return size;
  91. }
  92. MemoryFile::MemoryFile(MMKVPath_t path, size_t expectedCapacity, bool readOnly, bool mayflyFD)
  93. : m_diskFile(std::move(path), readOnly ? OpenFlag::ReadOnly : (OpenFlag::ReadWrite | OpenFlag::Create))
  94. , m_fileMapping(nullptr)
  95. , m_ptr(nullptr)
  96. , m_size(0)
  97. , m_readOnly(readOnly)
  98. , m_isMayflyFD(mayflyFD) {
  99. reloadFromFile(expectedCapacity);
  100. }
  101. bool MemoryFile::truncate(size_t size, FileLock *fileLock) {
  102. if (m_isMayflyFD) {
  103. openIfNeeded();
  104. }
  105. if (!m_diskFile.isFileValid()) {
  106. return false;
  107. }
  108. if (size == m_size) {
  109. return true;
  110. }
  111. if (m_readOnly) {
  112. // truncate readonly file not allow
  113. return false;
  114. }
  115. auto oldSize = m_size;
  116. m_size = size;
  117. // round up to (n * pagesize)
  118. if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
  119. m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
  120. }
  121. // Win32 won't ftruncate a file if there's active file mmapping/handle, we have to unmmap/close ahead
  122. bool needMMapOnFailure = false;
  123. if (m_ptr) {
  124. // if we have a valid file mapping before, we should restore it regardless
  125. needMMapOnFailure = true;
  126. if (!UnmapViewOfFile(m_ptr)) {
  127. MMKVError("fail to munmap [%ls], %d", m_diskFile.m_path.c_str(), GetLastError());
  128. }
  129. m_ptr = nullptr;
  130. }
  131. if (m_fileMapping) {
  132. CloseHandle(m_fileMapping);
  133. m_fileMapping = nullptr;
  134. }
  135. if (!ftruncate(m_diskFile.getFd(), m_size)) {
  136. MMKVError("fail to truncate [%ls] to size %zu", m_diskFile.m_path.c_str(), m_size);
  137. m_size = oldSize;
  138. if (needMMapOnFailure) {
  139. mmapOrCleanup(fileLock);
  140. }
  141. return false;
  142. }
  143. if (m_size > oldSize) {
  144. if (!zeroFillFile(m_diskFile.getFd(), oldSize, m_size - oldSize)) {
  145. MMKVError("fail to zeroFile [%ls] to size %zu", m_diskFile.m_path.c_str(), m_size);
  146. m_size = oldSize;
  147. if (needMMapOnFailure) {
  148. mmapOrCleanup(fileLock);
  149. }
  150. return false;
  151. }
  152. }
  153. return mmapOrCleanup(fileLock);
  154. }
  155. bool MemoryFile::msync(SyncFlag syncFlag) {
  156. if (m_readOnly) {
  157. // there's no point in msync() readonly memory
  158. return true;
  159. }
  160. if (m_ptr) {
  161. if (FlushViewOfFile(m_ptr, m_size)) {
  162. if (syncFlag == MMKV_SYNC && openIfNeeded()) {
  163. auto ret = FlushFileBuffers(m_diskFile.getFd());
  164. if (!ret) {
  165. MMKVError("fail to FlushFileBuffers [%ls]:%d", m_diskFile.m_path.c_str(), GetLastError());
  166. }
  167. cleanMayflyFD();
  168. return ret;
  169. }
  170. return true;
  171. }
  172. MMKVError("fail to FlushViewOfFile [%ls]:%d", m_diskFile.m_path.c_str(), GetLastError());
  173. return false;
  174. }
  175. return false;
  176. }
  177. bool MemoryFile::mmapOrCleanup(FileLock *fileLock) {
  178. auto mode = m_readOnly ? PAGE_READONLY : PAGE_READWRITE;
  179. m_fileMapping = CreateFileMapping(m_diskFile.getFd(), nullptr, mode, 0, 0, nullptr);
  180. if (!m_fileMapping) {
  181. MMKVError("fail to CreateFileMapping [%ls], mode %x, %d", m_diskFile.m_path.c_str(), mode, GetLastError());
  182. return false;
  183. } else {
  184. auto viewMode = m_readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;
  185. m_ptr = (char*)MapViewOfFile(m_fileMapping, viewMode, 0, 0, 0);
  186. if (!m_ptr) {
  187. MMKVError("fail to mmap [%ls], mode %x, %d", m_diskFile.m_path.c_str(), viewMode, GetLastError());
  188. doCleanMemoryCache(true);
  189. return false;
  190. }
  191. MMKVInfo("mmap to address [%p], [%ls]", m_ptr, m_diskFile.m_path.c_str());
  192. if (m_isMayflyFD && fileLock) {
  193. fileLock->destroyAndUnLock();
  194. }
  195. cleanMayflyFD();
  196. return true;
  197. }
  198. }
  199. void MemoryFile::reloadFromFile(size_t expectedCapacity) {
  200. if (isFileValid()) {
  201. MMKVWarning("calling reloadFromFile while the cache [%ls] is still valid", m_diskFile.m_path.c_str());
  202. assert(0);
  203. clearMemoryCache();
  204. }
  205. if (openIfNeeded()) {
  206. FileLock fileLock(m_diskFile.getFd());
  207. InterProcessLock lock(&fileLock, SharedLockType);
  208. SCOPED_LOCK(&lock);
  209. mmkv::getFileSize(m_diskFile.getFd(), m_size);
  210. size_t expectedSize = std::max<size_t>(DEFAULT_MMAP_SIZE, roundUp<size_t>(expectedCapacity, DEFAULT_MMAP_SIZE));
  211. // round up to (n * pagesize)
  212. if (!m_readOnly && (m_size < expectedSize || (m_size % DEFAULT_MMAP_SIZE != 0))) {
  213. InterProcessLock exclusiveLock(&fileLock, ExclusiveLockType);
  214. SCOPED_LOCK(&exclusiveLock);
  215. size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;;
  216. roundSize = std::max<size_t>(expectedSize, roundSize);
  217. truncate(roundSize, &fileLock);
  218. } else {
  219. mmapOrCleanup(&fileLock);
  220. }
  221. }
  222. }
  223. void MemoryFile::doCleanMemoryCache(bool forceClean) {
  224. if (m_ptr) {
  225. UnmapViewOfFile(m_ptr);
  226. m_ptr = nullptr;
  227. }
  228. if (m_fileMapping) {
  229. CloseHandle(m_fileMapping);
  230. m_fileMapping = nullptr;
  231. }
  232. m_diskFile.close();
  233. }
  234. bool MemoryFile::openIfNeeded() {
  235. if (!m_diskFile.isFileValid()) {
  236. return m_diskFile.open();
  237. }
  238. return true;
  239. }
  240. void MemoryFile::cleanMayflyFD() {
  241. if (m_isMayflyFD) {
  242. if (m_diskFile.isFileValid()) {
  243. m_diskFile.close();
  244. }
  245. if (m_fileMapping) {
  246. CloseHandle(m_fileMapping);
  247. m_fileMapping = nullptr;
  248. }
  249. }
  250. }
  251. size_t MemoryFile::getActualFileSize() {
  252. if (!m_isMayflyFD && !m_diskFile.isFileValid()) {
  253. return 0;
  254. }
  255. return m_diskFile.getActualFileSize();
  256. }
  257. MMKVFileHandle_t MemoryFile::getFd() {
  258. if (m_isMayflyFD) {
  259. openIfNeeded();
  260. }
  261. return m_diskFile.getFd();
  262. }
  263. size_t getPageSize() {
  264. SYSTEM_INFO system_info;
  265. GetSystemInfo(&system_info);
  266. return system_info.dwPageSize;
  267. }
  268. MMKVPath_t absolutePath(const MMKVPath_t& path) {
  269. fs::path relative_path(path);
  270. fs::path absolute_path = fs::absolute(relative_path);
  271. fs::path normalized = fs::weakly_canonical(absolute_path);
  272. return normalized.wstring();
  273. }
  274. bool isFileExist(const MMKVPath_t &nsFilePath) {
  275. if (nsFilePath.empty()) {
  276. return false;
  277. }
  278. auto attribute = GetFileAttributes(nsFilePath.c_str());
  279. return (attribute != INVALID_FILE_ATTRIBUTES);
  280. }
  281. bool mkPath(const MMKVPath_t &str) {
  282. wchar_t *path = _wcsdup(str.c_str());
  283. bool done = false;
  284. wchar_t *slash = path;
  285. while (!done) {
  286. slash += wcsspn(slash, L"\\");
  287. slash += wcscspn(slash, L"\\");
  288. done = (*slash == L'\0');
  289. *slash = L'\0';
  290. auto attribute = GetFileAttributes(path);
  291. if (attribute == INVALID_FILE_ATTRIBUTES) {
  292. if (!CreateDirectory(path, nullptr)) {
  293. MMKVError("fail to create dir:%ls, %d", str.c_str(), GetLastError());
  294. free(path);
  295. return false;
  296. }
  297. } else if (!(attribute & FILE_ATTRIBUTE_DIRECTORY)) {
  298. MMKVError("%ls attribute:%d not a directry", str.c_str(), attribute);
  299. free(path);
  300. return false;
  301. }
  302. *slash = L'\\';
  303. }
  304. free(path);
  305. return true;
  306. }
  307. MMBuffer *readWholeFile(const MMKVPath_t &nsFilePath) {
  308. MMBuffer *buffer = nullptr;
  309. auto fd = CreateFile(nsFilePath.c_str(), GENERIC_READ | GENERIC_WRITE,
  310. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
  311. FILE_ATTRIBUTE_NORMAL, nullptr);
  312. if (fd != INVALID_HANDLE_VALUE) {
  313. size_t fileLength = 0;
  314. getFileSize(fd, fileLength);
  315. if (fileLength > 0) {
  316. buffer = new MMBuffer(static_cast<size_t>(fileLength));
  317. SetFilePointer(fd, 0, 0, FILE_BEGIN);
  318. DWORD readSize = 0;
  319. if (ReadFile(fd, buffer->getPtr(), (DWORD) fileLength, &readSize, nullptr)) {
  320. //fileSize = readSize;
  321. } else {
  322. MMKVWarning("fail to read %ls: %d", nsFilePath.c_str(), GetLastError());
  323. delete buffer;
  324. buffer = nullptr;
  325. }
  326. }
  327. CloseHandle(fd);
  328. } else {
  329. MMKVWarning("fail to open %ls: %d", nsFilePath.c_str(), GetLastError());
  330. }
  331. return buffer;
  332. }
  333. bool zeroFillFile(MMKVFileHandle_t file, size_t startPos, size_t size) {
  334. if (file == INVALID_HANDLE_VALUE) {
  335. return false;
  336. }
  337. if (size == 0) {
  338. return true;
  339. }
  340. LARGE_INTEGER position;
  341. position.QuadPart = startPos;
  342. if (!SetFilePointerEx(file, position, nullptr, FILE_BEGIN)) {
  343. MMKVError("fail to lseek fd[%p], error:%d", file, GetLastError());
  344. return false;
  345. }
  346. static const char zeros[4096] = {0};
  347. while (size >= sizeof(zeros)) {
  348. DWORD bytesWritten = 0;
  349. if (!WriteFile(file, zeros, sizeof(zeros), &bytesWritten, nullptr)) {
  350. MMKVError("fail to write fd[%p], error:%d", file, GetLastError());
  351. return false;
  352. }
  353. size -= bytesWritten;
  354. }
  355. if (size > 0) {
  356. DWORD bytesWritten = 0;
  357. if (!WriteFile(file, zeros, (DWORD) size, &bytesWritten, nullptr)) {
  358. MMKVError("fail to write fd[%p], error:%d", file, GetLastError());
  359. return false;
  360. }
  361. }
  362. return true;
  363. }
  364. static bool ftruncate(MMKVFileHandle_t file, size_t size) {
  365. LARGE_INTEGER large;
  366. large.QuadPart = size;
  367. if (SetFilePointerEx(file, large, 0, FILE_BEGIN)) {
  368. if (SetEndOfFile(file)) {
  369. return true;
  370. }
  371. MMKVError("fail to SetEndOfFile:%d", GetLastError());
  372. return false;
  373. } else {
  374. MMKVError("fail to SetFilePointer:%d", GetLastError());
  375. return false;
  376. }
  377. }
  378. static bool getFileSize(MMKVFileHandle_t fd, size_t &size) {
  379. LARGE_INTEGER filesize = {0};
  380. if (GetFileSizeEx(fd, &filesize)) {
  381. size = static_cast<size_t>(filesize.QuadPart);
  382. return true;
  383. }
  384. return false;
  385. }
  386. bool getFileSize(const wchar_t *filename, size_t &size) {
  387. WIN32_FILE_ATTRIBUTE_DATA fileAttr;
  388. if (GetFileAttributesEx(filename, GetFileExInfoStandard, &fileAttr)) {
  389. size = ((ULONGLONG)fileAttr.nFileSizeHigh << 32) | fileAttr.nFileSizeLow;
  390. return true;
  391. }
  392. return false;
  393. }
  394. static pair<MMKVPath_t, MMKVFileHandle_t> createUniqueTempFile(const wchar_t *prefix) {
  395. wchar_t lpTempPathBuffer[MAX_PATH];
  396. // Gets the temp path env string (no guarantee it's a valid path).
  397. auto dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer);
  398. if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
  399. MMKVError("GetTempPath failed %d", GetLastError());
  400. return {L"", INVALID_HANDLE_VALUE};
  401. }
  402. // Generates a temporary file name.
  403. wchar_t szTempFileName[MAX_PATH];
  404. if (!GetTempFileName(lpTempPathBuffer, prefix, 0, szTempFileName)) {
  405. MMKVError("GetTempFileName failed %d", GetLastError());
  406. return {L"", INVALID_HANDLE_VALUE};
  407. }
  408. auto hTempFile =
  409. CreateFile(szTempFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
  410. if (hTempFile == INVALID_HANDLE_VALUE) {
  411. MMKVError("fail to create unique temp file [%ls], %d", szTempFileName, GetLastError());
  412. return {L"", INVALID_HANDLE_VALUE};
  413. }
  414. MMKVDebug("create unique temp file [%ls] with fd[%p]", szTempFileName, hTempFile);
  415. return {MMKVPath_t(szTempFileName), hTempFile};
  416. }
  417. bool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
  418. if (MoveFileEx(srcPath.c_str(), dstPath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) == 0) {
  419. MMKVError("MoveFileEx [%ls] to [%ls] failed %d", srcPath.c_str(), dstPath.c_str(), GetLastError());
  420. return false;
  421. }
  422. return true;
  423. }
  424. bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate) {
  425. if (dstFD == INVALID_HANDLE_VALUE) {
  426. return false;
  427. }
  428. bool ret = false;
  429. File srcFile(srcPath, OpenFlag::ReadOnly);
  430. if (!srcFile.isFileValid()) {
  431. return false;
  432. }
  433. auto bufferSize = getPageSize();
  434. auto buffer = (char *) malloc(bufferSize);
  435. if (!buffer) {
  436. MMKVError("fail to malloc size %zu, %d(%s)", bufferSize, errno, strerror(errno));
  437. goto errorOut;
  438. }
  439. SetFilePointer(dstFD, 0, 0, FILE_BEGIN);
  440. // the Win32 platform don't have sendfile()/fcopyfile() equivalent, do it the hard way
  441. while (true) {
  442. DWORD sizeRead = 0;
  443. if (!ReadFile(srcFile.getFd(), buffer, (DWORD) bufferSize, &sizeRead, nullptr)) {
  444. MMKVError("fail to read %ls: %d", srcPath.c_str(), GetLastError());
  445. goto errorOut;
  446. }
  447. DWORD sizeWrite = 0;
  448. if (!WriteFile(dstFD, buffer, sizeRead, &sizeWrite, nullptr)) {
  449. MMKVError("fail to write fd [%d], %d", dstFD, GetLastError());
  450. goto errorOut;
  451. }
  452. if (sizeRead < bufferSize) {
  453. break;
  454. }
  455. }
  456. if (needTruncate) {
  457. size_t dstFileSize = 0;
  458. getFileSize(dstFD, dstFileSize);
  459. auto srcFileSize = srcFile.getActualFileSize();
  460. if ((dstFileSize != srcFileSize) && !ftruncate(dstFD, static_cast<off_t>(srcFileSize))) {
  461. MMKVError("fail to truncate [%d] to size [%zu]", dstFD, srcFileSize);
  462. goto errorOut;
  463. }
  464. }
  465. ret = true;
  466. MMKVInfo("copy content from %ls to fd[%d] finish", srcPath.c_str(), dstFD);
  467. errorOut:
  468. free(buffer);
  469. return ret;
  470. }
  471. // copy to a temp file then rename it
  472. // this is the best we can do on Win32
  473. bool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
  474. auto pair = createUniqueTempFile(L"MMKV");
  475. auto tmpFD = pair.second;
  476. auto &tmpPath = pair.first;
  477. if (tmpFD == INVALID_HANDLE_VALUE) {
  478. return false;
  479. }
  480. bool renamed = false;
  481. if (copyFileContent(srcPath, tmpFD, false)) {
  482. MMKVInfo("copyed file [%ls] to [%ls]", srcPath.c_str(), tmpPath.c_str());
  483. CloseHandle(tmpFD);
  484. renamed = tryAtomicRename(tmpPath.c_str(), dstPath.c_str());
  485. if (renamed) {
  486. MMKVInfo("copyfile [%ls] to [%ls] finish.", srcPath.c_str(), dstPath.c_str());
  487. }
  488. } else {
  489. CloseHandle(tmpFD);
  490. }
  491. if (!renamed) {
  492. DeleteFile(tmpPath.c_str());
  493. }
  494. return renamed;
  495. }
  496. bool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
  497. File dstFile(dstPath, OpenFlag::WriteOnly | OpenFlag::Create | OpenFlag::Truncate);
  498. if (!dstFile.isFileValid()) {
  499. return false;
  500. }
  501. auto ret = copyFileContent(srcPath, dstFile.getFd(), false);
  502. if (!ret) {
  503. MMKVError("fail to copyfile(): target file %ls", dstPath.c_str());
  504. } else {
  505. MMKVInfo("copy content from %ls to [%ls] finish", srcPath.c_str(), dstPath.c_str());
  506. }
  507. return ret;
  508. }
  509. bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD) {
  510. return copyFileContent(srcPath, dstFD, true);
  511. }
  512. void walkInDir(const MMKVPath_t &dirPath,
  513. WalkType type,
  514. const std::function<void(const MMKVPath_t &, WalkType)> &walker) {
  515. wchar_t szDir[MAX_PATH];
  516. StringCchCopy(szDir, MAX_PATH, dirPath.c_str());
  517. StringCchCat(szDir, MAX_PATH, L"\\*");
  518. WIN32_FIND_DATA ffd;
  519. auto hFind = FindFirstFile(szDir, &ffd);
  520. if (hFind == INVALID_HANDLE_VALUE) {
  521. return;
  522. }
  523. do {
  524. if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  525. if (type & WalkFolder) {
  526. if (wcscmp(ffd.cFileName, L".") == 0 || wcscmp(ffd.cFileName, L"..") == 0) {
  527. continue;
  528. }
  529. walker(dirPath + L"\\" + ffd.cFileName, WalkFolder);
  530. }
  531. } else if (type & WalkFile) {
  532. walker(dirPath + L"\\" + ffd.cFileName, WalkFile);
  533. }
  534. } while (FindNextFile(hFind, &ffd) != 0);
  535. auto dwError = GetLastError();
  536. if (dwError != ERROR_NO_MORE_FILES) {
  537. MMKVError("WalkInDir fail %d", dwError);
  538. }
  539. FindClose(hFind);
  540. }
  541. bool isDiskOfMMAPFileCorrupted(MemoryFile *file, bool &needReportReadFail) {
  542. // make sure the file is valid
  543. __try {
  544. auto data = *((uint32_t*) file->getMemory());
  545. MMKVInfo("first byte of the file: 0x%x", data);
  546. }
  547. __except (GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
  548. needReportReadFail = true;
  549. DWORD errorCode = GetExceptionCode();
  550. MMKVError("fail to mmap [%ls], %d", file->getPath().c_str(), errorCode);
  551. return true;
  552. }
  553. return false;
  554. }
  555. bool deleteFile(const MMKVPath_t &path) {
  556. if (!DeleteFile(path.c_str())) {
  557. MMKVError("failed to delete file [%ls], %d", path.c_str(), GetLastError());
  558. return false;
  559. }
  560. return true;
  561. }
  562. } // namespace mmkv
  563. std::wstring string2MMKVPath_t(const std::string &str) {
  564. auto length = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
  565. auto buffer = new wchar_t[length];
  566. MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buffer, length);
  567. wstring result(buffer);
  568. delete[] buffer;
  569. return result;
  570. }
  571. std::string MMKVPath_t2String(const MMKVPath_t &str) {
  572. auto length = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, nullptr, 0, 0, 0);
  573. auto buffer = new char[length];
  574. WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, buffer, length, 0, 0);
  575. string result(buffer);
  576. delete[] buffer;
  577. return result;
  578. }
  579. #endif // MMKV_WIN32