github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/database/leveldb.chai2010/src/port/env_windows.cc (about) 1 #include <stdio.h> 2 #include <string.h> 3 #include <deque> 4 #include <process.h> 5 6 #include "leveldb/env.h" 7 #include "leveldb/slice.h" 8 #include "port/port.h" 9 #include "util/logging.h" 10 #include "port/win_logger.h" 11 12 // To properly support file names on Windows we should be using Unicode 13 // (WCHAR) strings. To accomodate existing interface which uses std::string, 14 // we use the following convention: 15 // * all filenames that we return (e.g. from GetTestDirectory()) are 16 // utf8-encoded 17 // * we'll try to interpret all input file names as if they're 18 // utf8-encoded. If they're not valid utf8 strings, we'll try 19 // to interpret them according to a current code page 20 // This just works for names that don't use characters outside ascii 21 // and for those that do, the caller needs to be aware of this convention 22 // whenever it bubbles up to the user-level API. 23 24 namespace leveldb { 25 26 static Status IOError(const std::string& context, DWORD err = (DWORD)-1) { 27 char *err_msg = NULL; 28 Status s; 29 if ((DWORD)-1 == err) 30 err = GetLastError(); 31 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 32 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 33 (LPSTR)&err_msg, 0, NULL); 34 if (!err_msg) 35 return Status::IOError(context); 36 s = Status::IOError(context, err_msg); 37 LocalFree(err_msg); 38 return s; 39 } 40 41 class WinSequentialFile: public SequentialFile { 42 private: 43 std::string fname_; 44 HANDLE file_; 45 46 public: 47 WinSequentialFile(const std::string& fname, HANDLE f) 48 : fname_(fname), file_(f) { } 49 virtual ~WinSequentialFile() { CloseHandle(file_); } 50 51 virtual Status Read(size_t n, Slice* result, char* scratch) { 52 DWORD n2 = n; 53 DWORD r = 0; 54 BOOL ok = ReadFile(file_, (void*)scratch, n2, &r, NULL); 55 *result = Slice(scratch, r); 56 if (!ok) { 57 // We leave status as ok if we hit the end of the file 58 if (GetLastError() != ERROR_HANDLE_EOF) { 59 return IOError(fname_); 60 } 61 } 62 return Status::OK(); 63 } 64 65 virtual Status Skip(uint64_t n) { 66 LARGE_INTEGER pos; 67 pos.QuadPart = n; 68 DWORD res = SetFilePointerEx(file_, pos, NULL, FILE_CURRENT); 69 if (res == 0) 70 return IOError(fname_); 71 return Status::OK(); 72 } 73 }; 74 75 class WinRandomAccessFile: public RandomAccessFile { 76 private: 77 std::string fname_; 78 HANDLE file_; 79 80 public: 81 WinRandomAccessFile(const std::string& fname, HANDLE file) 82 : fname_(fname), file_(file) { } 83 virtual ~WinRandomAccessFile() { CloseHandle(file_); } 84 85 virtual Status Read(uint64_t offset, size_t n, Slice* result, 86 char* scratch) const { 87 OVERLAPPED overlapped = { 0 }; 88 overlapped.Offset = static_cast<DWORD>(offset); 89 overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32); 90 DWORD bytes_read = 0; 91 BOOL success = ReadFile(file_, scratch, n, &bytes_read, &overlapped); 92 *result = Slice(scratch, bytes_read); 93 return success != FALSE ? Status::OK() : Status::IOError(fname_); 94 } 95 }; 96 97 class WinWritableFile : public WritableFile { 98 private: 99 std::string name_; 100 HANDLE file_; 101 102 public: 103 WinWritableFile(std::string name, HANDLE h) : name_(name), file_(h) { 104 } 105 106 virtual ~WinWritableFile() { 107 Close(); 108 } 109 110 virtual Status Append(const Slice& data) { 111 DWORD n = data.size(); 112 DWORD pos = 0; 113 while (pos < n) { 114 DWORD written = 0; 115 BOOL ok = WriteFile(file_, data.data() + pos, n - pos, &written, NULL); 116 if (!ok) 117 return IOError(name_+ "Append: cannot write"); 118 pos += written; 119 } 120 return Status::OK(); 121 } 122 123 virtual Status Close() { 124 if (INVALID_HANDLE_VALUE == file_) 125 return Status::OK(); 126 Status s = Sync(); 127 CloseHandle(file_); 128 file_ = INVALID_HANDLE_VALUE; 129 return s; 130 } 131 132 virtual Status Flush() { 133 return Status::OK(); 134 } 135 136 virtual Status Sync() { 137 BOOL ok = FlushFileBuffers(file_); 138 if (!ok) 139 return IOError(name_); 140 return Status::OK(); 141 } 142 }; 143 144 namespace { 145 146 #define DIR_SEP_CHAR L'\\' 147 #define DIR_SEP_STR L"\\" 148 149 WCHAR *ToWcharFromCodePage(const char *src, UINT cp) { 150 int required_buf_size = MultiByteToWideChar(cp, 0, src, -1, NULL, 0); 151 if (0 == required_buf_size) // indicates an error 152 return NULL; 153 WCHAR *res = reinterpret_cast<WCHAR*>(malloc(sizeof(WCHAR) * required_buf_size)); 154 if (!res) 155 return NULL; 156 MultiByteToWideChar(cp, 0, src, -1, res, required_buf_size); 157 return res; 158 } 159 160 // try to convert to WCHAR string trying most common code pages 161 // to be as permissive as we can be 162 WCHAR *ToWcharPermissive(const char *s) { 163 WCHAR *ws = ToWcharFromCodePage(s, CP_UTF8); 164 if (ws != NULL) 165 return ws; 166 ws = ToWcharFromCodePage(s, CP_ACP); 167 if (ws != NULL) 168 return ws; 169 ws = ToWcharFromCodePage(s, CP_OEMCP); 170 return ws; 171 } 172 173 char *ToUtf8(const WCHAR *s) { 174 int required_buf_size = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL); 175 char *res = (char*)malloc(sizeof(char) * required_buf_size); 176 if (!res) 177 return NULL; 178 WideCharToMultiByte(CP_UTF8, 0, s, -1, res, required_buf_size, NULL, NULL); 179 return res; 180 } 181 182 static size_t WstrLen(const WCHAR *s) { 183 if (NULL == s) 184 return 0; 185 return wcslen(s); 186 } 187 188 static WCHAR *WstrJoin(const WCHAR *s1, const WCHAR *s2, const WCHAR *s3=NULL) { 189 size_t s1_len = WstrLen(s1); 190 size_t s2_len = WstrLen(s2); 191 size_t s3_len = WstrLen(s3); 192 size_t len =s1_len + s2_len + s3_len + 1; 193 WCHAR *res = (WCHAR*)malloc(sizeof(WCHAR) * len); 194 if (!res) 195 return NULL; 196 WCHAR *tmp = res; 197 if (s1 != NULL) { 198 memcpy(tmp, s1, s1_len * sizeof(WCHAR)); 199 tmp += s1_len; 200 } 201 if (s2 != NULL) { 202 memcpy(tmp, s2, s2_len * sizeof(WCHAR)); 203 tmp += s2_len; 204 } 205 if (s3 != NULL) { 206 memcpy(tmp, s3, s3_len * sizeof(WCHAR)); 207 tmp += s3_len; 208 } 209 *tmp = 0; 210 return res; 211 } 212 213 static bool WstrEndsWith(const WCHAR *s1, WCHAR c) { 214 size_t len = WstrLen(s1); 215 return ((len > 0) && (s1[len-1] == c)); 216 } 217 218 static WCHAR *WstrPathJoin(const WCHAR *s1, const WCHAR *s2) { 219 if (WstrEndsWith(s1, DIR_SEP_CHAR)) 220 return WstrJoin(s1, s2); 221 return WstrJoin(s1, DIR_SEP_STR, s2); 222 } 223 224 // Return true if s is "." or "..", which are 2 directories 225 // we should skip when enumerating a directory 226 static bool SkipDir(const WCHAR *s) { 227 if (*s == L'.') { 228 if (s[1] == 0) 229 return true; 230 return ((s[1] == '.') && (s[2] == 0)); 231 } 232 return false; 233 } 234 235 class WinFileLock : public FileLock { 236 public: 237 WinFileLock(const std::string &fname, HANDLE file) 238 : fname_(fname), file_(file) { 239 } 240 241 virtual ~WinFileLock() { 242 Close(); 243 } 244 245 bool Close() { 246 bool ok = true; 247 if (file_ != INVALID_HANDLE_VALUE) 248 ok = (CloseHandle(file_) != FALSE); 249 file_ = INVALID_HANDLE_VALUE; 250 return ok; 251 } 252 253 std::string fname_; 254 HANDLE file_; 255 }; 256 257 class WinEnv : public Env { 258 public: 259 WinEnv(); 260 virtual ~WinEnv() { 261 fprintf(stderr, "Destroying Env::Default()\n"); 262 //exit(1); 263 } 264 265 virtual Status NewSequentialFile(const std::string& fname, 266 SequentialFile** result) { 267 *result = NULL; 268 WCHAR *file_name = ToWcharPermissive(fname.c_str()); 269 if (file_name == NULL) { 270 return Status::InvalidArgument("Invalid file name"); 271 } 272 HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 273 free((void*)file_name); 274 if (h == INVALID_HANDLE_VALUE) { 275 return IOError(fname); 276 } 277 *result = new WinSequentialFile(fname, h); 278 return Status::OK(); 279 } 280 281 virtual Status NewRandomAccessFile(const std::string& fname, 282 RandomAccessFile** result) { 283 *result = NULL; 284 WCHAR *file_name = ToWcharPermissive(fname.c_str()); 285 if (file_name == NULL) { 286 return Status::InvalidArgument("Invalid file name"); 287 } 288 HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 289 free((void*)file_name); 290 if (h == INVALID_HANDLE_VALUE) { 291 return IOError(fname); 292 } 293 *result = new WinRandomAccessFile(fname, h); 294 return Status::OK(); 295 } 296 297 virtual Status NewWritableFile(const std::string& fname, 298 WritableFile** result) { 299 *result = NULL; 300 WCHAR *file_name = ToWcharPermissive(fname.c_str()); 301 if (file_name == NULL) 302 return Status::InvalidArgument("Invalid file name"); 303 HANDLE h = CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 304 free((void*)file_name); 305 if (h == INVALID_HANDLE_VALUE) { 306 return IOError(fname); 307 } 308 *result = new WinWritableFile(fname, h); 309 return Status::OK(); 310 } 311 312 virtual bool FileExists(const std::string& fname) { 313 WCHAR *file_name = ToWcharPermissive(fname.c_str()); 314 if (file_name == NULL) 315 return false; 316 317 WIN32_FILE_ATTRIBUTE_DATA file_info; 318 BOOL res = GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_info); 319 free((void*)file_name); 320 if (0 == res) 321 return false; 322 323 if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) 324 return false; 325 return true; 326 } 327 328 virtual Status GetChildren(const std::string& dir, 329 std::vector<std::string>* result) { 330 result->clear(); 331 WCHAR *dir_name = ToWcharPermissive(dir.c_str()); 332 if (dir_name == NULL) 333 return Status::InvalidArgument("Invalid file name"); 334 WCHAR *pattern = WstrPathJoin(dir_name, L"*"); 335 free(dir_name); 336 if (NULL == pattern) 337 return Status::InvalidArgument("Invalid file name"); 338 WIN32_FIND_DATAW file_data; 339 HANDLE h = FindFirstFileW(pattern, &file_data); 340 free(pattern); 341 if (INVALID_HANDLE_VALUE == h) { 342 if (ERROR_FILE_NOT_FOUND == GetLastError()) 343 return Status::OK(); 344 return IOError(dir); 345 } 346 for (;;) { 347 WCHAR *s = file_data.cFileName; 348 if (!SkipDir(s)) { 349 char *s2 = ToUtf8(s); 350 result->push_back(s2); 351 free(s2); 352 } 353 if (FALSE == FindNextFileW(h, &file_data)) 354 break; 355 } 356 FindClose(h); 357 return Status::OK(); 358 } 359 360 virtual Status DeleteFile(const std::string& fname) { 361 WCHAR *file_path = ToWcharPermissive(fname.c_str()); 362 if (file_path == NULL) 363 return Status::InvalidArgument("Invalid file name"); 364 365 BOOL ok = DeleteFileW(file_path); 366 free(file_path); 367 if (!ok) { 368 DWORD err = GetLastError(); 369 if ((ERROR_PATH_NOT_FOUND == err) || (ERROR_FILE_NOT_FOUND == err)) 370 return Status::OK(); 371 return IOError("DeleteFile " + fname); 372 } 373 return Status::OK(); 374 } 375 376 bool CreateDirIfNotExists(const WCHAR *dir) { 377 BOOL ok = CreateDirectoryW(dir, NULL); 378 if (ok) 379 return true; 380 return (ERROR_ALREADY_EXISTS == GetLastError()); 381 } 382 383 bool DirExists(const WCHAR *dir) { 384 WIN32_FILE_ATTRIBUTE_DATA file_info; 385 BOOL res = GetFileAttributesExW(dir, GetFileExInfoStandard, &file_info); 386 if (0 == res) 387 return false; 388 389 return (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 390 } 391 392 WCHAR *WstrDupN(const WCHAR *s, size_t len) { 393 void *res = malloc((len + 1) * sizeof(WCHAR)); 394 if (!res) 395 return NULL; 396 memcpy(res, s, len * sizeof(WCHAR)); 397 WCHAR *res2 = reinterpret_cast<WCHAR*>(res); 398 res2[len] = 0; 399 return res2; 400 } 401 402 bool IsPathSep(WCHAR c) { 403 return (c == '\\') || (c == '/'); 404 } 405 406 WCHAR *GetPathParent(const WCHAR *path) { 407 const WCHAR *last_sep = NULL; 408 const WCHAR *tmp = path; 409 // find the last path separator 410 // (ignoring one at the end of the string) 411 while (*tmp) { 412 if (IsPathSep(*tmp)) { 413 if (0 != tmp[1]) 414 last_sep = tmp; 415 } 416 ++tmp; 417 } 418 if (NULL == last_sep) 419 return NULL; 420 size_t len = last_sep - path; 421 return WstrDupN(path, len); 422 } 423 424 bool CreateDirRecursive(WCHAR *dir) { 425 WCHAR *parent = GetPathParent(dir); 426 bool ok = true; 427 if (parent && !DirExists(parent)) { 428 ok = CreateDirRecursive(parent); 429 } 430 free(parent); 431 if (!ok) 432 return false; 433 return CreateDirIfNotExists(dir); 434 } 435 436 virtual Status CreateDir(const std::string& name) { 437 WCHAR *dir = ToWcharPermissive(name.c_str()); 438 if (dir == NULL) 439 return Status::InvalidArgument("Invalid file name"); 440 bool ok = CreateDirRecursive(dir); 441 free(dir); 442 if (!ok) 443 return IOError(name); 444 return Status::OK(); 445 } 446 447 #if 1 448 virtual Status DeleteDir(const std::string& name) { 449 WCHAR *dir = ToWcharPermissive(name.c_str()); 450 if (dir == NULL) 451 return Status::InvalidArgument("Invalid file name"); 452 BOOL ok = RemoveDirectoryW(dir); 453 free(dir); 454 if (!ok) 455 return IOError(name); 456 return Status::OK(); 457 } 458 #else 459 virtual Status DeleteDir(const std::string& dirname) { 460 WCHAR *dir = ToWcharPermissive(dirname.c_str()); 461 if (dir == NULL) 462 return Status::InvalidArgument("Invalid file name"); 463 464 SHFILEOPSTRUCTW fileop = { 0 }; 465 fileop.wFunc = FO_DELETE; 466 fileop.pFrom = (const WCHAR*)dir; 467 fileop.fFlags = FOF_NO_UI; 468 int res = SHFileOperationW(&fileop); 469 free((void*)dir); 470 if (res == 0 && fileop.fAnyOperationsAborted == FALSE) 471 return Status::OK(); 472 return IOError("DeleteDir " + dirname); 473 } 474 #endif 475 476 virtual Status GetFileSize(const std::string& fname, uint64_t* size) { 477 478 WCHAR *file_name = ToWcharPermissive(fname.c_str()); 479 if (file_name == NULL) 480 return Status::InvalidArgument("Invalid file name"); 481 HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 482 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 483 free(file_name); 484 if (h == INVALID_HANDLE_VALUE) 485 return IOError("GetFileSize " + fname); 486 487 // Not using GetFileAttributesEx() as it doesn't interact well with symlinks, etc. 488 LARGE_INTEGER lsize; 489 BOOL ok = GetFileSizeEx(h, &lsize); 490 CloseHandle(h); 491 if (!ok) 492 return IOError("GetFileSize " + fname); 493 494 *size = static_cast<uint64_t>(lsize.QuadPart); 495 return Status::OK(); 496 } 497 498 virtual Status RenameFile(const std::string& src, const std::string& target) { 499 WCHAR *src2 = ToWcharPermissive(src.c_str()); 500 WCHAR *target2 = ToWcharPermissive(target.c_str()); 501 if ((src2 == NULL) || (target2 == NULL)) { 502 free(src2); 503 free(target2); 504 return Status::InvalidArgument("Invalid file name"); 505 } 506 BOOL ok = MoveFileExW(src2, target2, MOVEFILE_REPLACE_EXISTING); 507 free(src2); 508 free(target2); 509 if (!ok) 510 return IOError("RenameFile " + src + " " + target); 511 return Status::OK(); 512 } 513 514 virtual Status LockFile(const std::string& fname, FileLock** lock) { 515 *lock = NULL; 516 WCHAR *file_name = ToWcharPermissive(fname.c_str()); 517 if (file_name == NULL) { 518 return Status::InvalidArgument("Invalid file name"); 519 } 520 HANDLE h = CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 521 free((void*)file_name); 522 if (h == INVALID_HANDLE_VALUE) { 523 return IOError("LockFile " + fname); 524 } 525 *lock = new WinFileLock(fname, h); 526 return Status::OK(); 527 } 528 529 virtual Status UnlockFile(FileLock* lock) { 530 Status s; 531 WinFileLock* my_lock = reinterpret_cast<WinFileLock*>(lock); 532 if (!my_lock->Close()) { 533 s = Status::IOError(my_lock->fname_, "Could not close lock file."); 534 } 535 delete my_lock; 536 return Status::OK(); 537 } 538 539 virtual void Schedule(void (*function)(void*), void* arg); 540 541 virtual void StartThread(void (*function)(void* arg), void* arg); 542 543 virtual Status GetTestDirectory(std::string* result) { 544 WCHAR buf[MAX_PATH]; 545 DWORD res = GetTempPathW(MAX_PATH, buf); 546 if (0 == res) { 547 return IOError("Can't get test directory"); 548 } 549 char *s = ToUtf8(buf); 550 if (!s) { 551 return IOError("Can't get test directory"); 552 } 553 *result = std::string(s); 554 free(s); 555 return Status::OK(); 556 } 557 558 virtual Status NewLogger(const std::string& fname, Logger** result) { 559 *result = NULL; 560 FILE* f = fopen(fname.c_str(), "wt"); 561 if (f == NULL) 562 return Status::IOError(fname, strerror(errno)); 563 *result = new WinLogger(f); 564 return Status::OK(); 565 } 566 567 virtual uint64_t NowMicros() { 568 LARGE_INTEGER count; 569 QueryPerformanceCounter(&count); 570 return count.QuadPart * 1000000LL / freq_.QuadPart; 571 } 572 573 virtual void SleepForMicroseconds(int micros) { 574 // round up to the next millisecond 575 Sleep((micros + 999) / 1000); 576 } 577 578 private: 579 LARGE_INTEGER freq_; 580 581 // BGThread() is the body of the background thread 582 void BGThread(); 583 584 static unsigned __stdcall BGThreadWrapper(void* arg) { 585 (reinterpret_cast<WinEnv*>(arg))->BGThread(); 586 _endthreadex(0); 587 return 0; 588 } 589 590 leveldb::port::Mutex mu_; 591 leveldb::port::CondVar bgsignal_; 592 HANDLE bgthread_; 593 594 // Entry per Schedule() call 595 struct BGItem { void* arg; void (*function)(void*); }; 596 typedef std::deque<BGItem> BGQueue; 597 BGQueue queue_; 598 }; 599 600 601 WinEnv::WinEnv() : bgthread_(NULL), bgsignal_(&mu_) { 602 QueryPerformanceFrequency(&freq_); 603 } 604 605 void WinEnv::Schedule(void (*function)(void*), void* arg) { 606 mu_.Lock(); 607 608 // Start background thread if necessary 609 if (NULL == bgthread_) { 610 bgthread_ = (HANDLE)_beginthreadex(NULL, 0, &WinEnv::BGThreadWrapper, this, 0, NULL); 611 } 612 613 // Add to priority queue 614 queue_.push_back(BGItem()); 615 queue_.back().function = function; 616 queue_.back().arg = arg; 617 618 mu_.Unlock(); 619 620 bgsignal_.Signal(); 621 } 622 623 void WinEnv::BGThread() { 624 while (true) { 625 // Wait until there is an item that is ready to run 626 mu_.Lock(); 627 628 while (queue_.empty()) { 629 bgsignal_.Wait(); 630 } 631 632 void (*function)(void*) = queue_.front().function; 633 void* arg = queue_.front().arg; 634 queue_.pop_front(); 635 636 mu_.Unlock(); 637 (*function)(arg); 638 } 639 // TODO: CloseHandle(bgthread_) ?? 640 } 641 642 namespace { 643 struct StartThreadState { 644 void (*user_function)(void*); 645 void* arg; 646 HANDLE threadHandle; 647 }; 648 } 649 650 static unsigned __stdcall StartThreadWrapper(void* arg) { 651 StartThreadState* state = reinterpret_cast<StartThreadState*>(arg); 652 state->user_function(state->arg); 653 _endthreadex(0); 654 CloseHandle(state->threadHandle); 655 delete state; 656 return 0; 657 } 658 659 void WinEnv::StartThread(void (*function)(void* arg), void* arg) { 660 StartThreadState* state = new StartThreadState; 661 state->user_function = function; 662 state->arg = arg; 663 state->threadHandle = (HANDLE)_beginthreadex(NULL, 0, &StartThreadWrapper, state, 0, NULL); 664 } 665 } 666 667 static Env* default_env; 668 static void InitDefaultEnv() { default_env = new WinEnv(); } 669 static leveldb::port::Mutex default_env_mutex; 670 671 Env* Env::Default() { 672 default_env_mutex.Lock(); 673 if (NULL == default_env) 674 InitDefaultEnv(); 675 default_env_mutex.Unlock(); 676 return default_env; 677 } 678 679 }