env_win.cc 44.5 KB
Newer Older
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
S
Siying Dong 已提交
2 3 4
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
D
Dmitri Smirnov 已提交
5 6 7 8 9
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

10 11
#if defined(OS_WIN)

12
#include <direct.h>  // _rmdir, _mkdir, _getcwd
D
Dmitri Smirnov 已提交
13
#include <errno.h>
14 15 16
#include <io.h>   // _access
#include <rpc.h>  // for uuid generation
#include <shlwapi.h>
D
Dmitri Smirnov 已提交
17
#include <sys/stat.h>
18 19
#include <sys/types.h>
#include <windows.h>
D
Dmitri Smirnov 已提交
20

21 22 23
#include <algorithm>
#include <ctime>
#include <thread>
D
Dmitri Smirnov 已提交
24

25 26 27
#include "monitoring/iostats_context_imp.h"
#include "monitoring/thread_status_updater.h"
#include "monitoring/thread_status_util.h"
28 29
#include "port/port.h"
#include "port/port_dirent.h"
30
#include "port/win/env_win.h"
31 32 33 34 35
#include "port/win/io_win.h"
#include "port/win/win_logger.h"
#include "port/win/win_thread.h"
#include "rocksdb/env.h"
#include "rocksdb/slice.h"
D
Dmitri Smirnov 已提交
36
#include "strsafe.h"
37
#include "util/string_util.h"
D
Dmitri Smirnov 已提交
38

39 40 41 42
// Undefine the functions  windows might use (again)...
#undef GetCurrentTime
#undef DeleteFile
#undef LoadLibrary
D
Dmitri Smirnov 已提交
43

44
namespace ROCKSDB_NAMESPACE {
D
Dmitri Smirnov 已提交
45 46 47 48 49

ThreadStatusUpdater* CreateThreadStatusUpdater() {
  return new ThreadStatusUpdater();
}

D
Dmitri Smirnov 已提交
50 51
namespace {

52 53
// Sector size used when physical sector size cannot be obtained from device.
static const size_t kSectorSize = 512;
D
Dmitri Smirnov 已提交
54

D
Dmitri Smirnov 已提交
55 56 57 58
// RAII helpers for HANDLEs
const auto CloseHandleFunc = [](HANDLE h) { ::CloseHandle(h); };
typedef std::unique_ptr<void, decltype(CloseHandleFunc)> UniqueCloseHandlePtr;

D
Dmitri Smirnov 已提交
59 60 61
const auto FindCloseFunc = [](HANDLE h) { ::FindClose(h); };
typedef std::unique_ptr<void, decltype(FindCloseFunc)> UniqueFindClosePtr;

62 63
void WinthreadCall(const char* label, std::error_code result) {
  if (0 != result.value()) {
64 65
    fprintf(stderr, "Winthread %s: %s\n", label,
            errnoStr(result.value()).c_str());
66
    abort();
D
Dmitri Smirnov 已提交
67
  }
68
}
D
Dmitri Smirnov 已提交
69

70
}  // namespace
D
Dmitri Smirnov 已提交
71

72
namespace port {
73 74
WinClock::WinClock()
    : perf_counter_frequency_(0),
B
Burton Li 已提交
75
      nano_seconds_per_period_(0),
76 77 78
      GetSystemTimePreciseAsFileTime_(NULL) {
  {
    LARGE_INTEGER qpf;
T
Tamir Duberstein 已提交
79
    BOOL ret __attribute__((__unused__));
80
    ret = QueryPerformanceFrequency(&qpf);
81 82
    assert(ret == TRUE);
    perf_counter_frequency_ = qpf.QuadPart;
B
Burton Li 已提交
83 84 85 86

    if (std::nano::den % perf_counter_frequency_ == 0) {
      nano_seconds_per_period_ = std::nano::den / perf_counter_frequency_;
    }
D
Dmitri Smirnov 已提交
87 88
  }

89 90
  HMODULE module = GetModuleHandle("kernel32.dll");
  if (module != NULL) {
91 92
    GetSystemTimePreciseAsFileTime_ = (FnGetSystemTimePreciseAsFileTime)(
        void*)GetProcAddress(module, "GetSystemTimePreciseAsFileTime");
D
Dmitri Smirnov 已提交
93 94 95
  }
}

96 97
void WinClock::SleepForMicroseconds(int micros) {
  std::this_thread::sleep_for(std::chrono::microseconds(micros));
D
Dmitri Smirnov 已提交
98 99
}

100 101
std::string WinClock::TimeToString(uint64_t secondsSince1970) {
  std::string result;
D
Dmitri Smirnov 已提交
102

103 104
  const time_t seconds = secondsSince1970;
  const int maxsize = 64;
105

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
  struct tm t;
  errno_t ret = localtime_s(&t, &seconds);

  if (ret) {
    result = std::to_string(seconds);
  } else {
    result.resize(maxsize);
    char* p = &result[0];

    int len =
        snprintf(p, maxsize, "%04d/%02d/%02d-%02d:%02d:%02d ", t.tm_year + 1900,
                 t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
    assert(len > 0);

    result.resize(len);
D
Dmitri Smirnov 已提交
121 122
  }

123
  return result;
D
Dmitri Smirnov 已提交
124 125
}

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
uint64_t WinClock::NowMicros() {
  if (GetSystemTimePreciseAsFileTime_ != NULL) {
    // all std::chrono clocks on windows proved to return
    // values that may repeat that is not good enough for some uses.
    const int64_t c_UnixEpochStartTicks = 116444736000000000LL;
    const int64_t c_FtToMicroSec = 10;

    // This interface needs to return system time and not
    // just any microseconds because it is often used as an argument
    // to TimedWait() on condition variable
    FILETIME ftSystemTime;
    GetSystemTimePreciseAsFileTime_(&ftSystemTime);

    LARGE_INTEGER li;
    li.LowPart = ftSystemTime.dwLowDateTime;
    li.HighPart = ftSystemTime.dwHighDateTime;
    // Subtract unix epoch start
    li.QuadPart -= c_UnixEpochStartTicks;
    // Convert to microsecs
    li.QuadPart /= c_FtToMicroSec;
    return li.QuadPart;
D
Dmitri Smirnov 已提交
147
  }
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  using namespace std::chrono;
  return duration_cast<microseconds>(system_clock::now().time_since_epoch())
      .count();
}

uint64_t WinClock::NowNanos() {
  if (nano_seconds_per_period_ != 0) {
    // all std::chrono clocks on windows have the same resolution that is only
    // good enough for microseconds but not nanoseconds
    // On Windows 8 and Windows 2012 Server
    // GetSystemTimePreciseAsFileTime(&current_time) can be used
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    // Convert performance counter to nanoseconds by precomputed ratio.
    // Directly multiply nano::den with li.QuadPart causes overflow.
    // Only do this when nano::den is divisible by perf_counter_frequency_,
    // which most likely is the case in reality. If it's not, fall back to
    // high_resolution_clock, which may be less precise under old compilers.
    li.QuadPart *= nano_seconds_per_period_;
    return li.QuadPart;
  }
  using namespace std::chrono;
  return duration_cast<nanoseconds>(
             high_resolution_clock::now().time_since_epoch())
      .count();
D
Dmitri Smirnov 已提交
173 174
}

175
Status WinClock::GetCurrentTime(int64_t* unix_time) {
176 177 178
  time_t time = std::time(nullptr);
  if (time == (time_t)(-1)) {
    return Status::NotSupported("Failed to get time");
D
Dmitri Smirnov 已提交
179 180
  }

181 182
  *unix_time = time;
  return Status::OK();
D
Dmitri Smirnov 已提交
183 184
}

185
WinFileSystem::WinFileSystem(const std::shared_ptr<SystemClock>& clock)
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    : clock_(clock), page_size_(4 * 1024), allocation_granularity_(page_size_) {
  SYSTEM_INFO sinfo;
  GetSystemInfo(&sinfo);

  page_size_ = sinfo.dwPageSize;
  allocation_granularity_ = sinfo.dwAllocationGranularity;
}

const std::shared_ptr<WinFileSystem>& WinFileSystem::Default() {
  static std::shared_ptr<WinFileSystem> fs =
      std::make_shared<WinFileSystem>(WinClock::Default());
  return fs;
}

WinEnvIO::WinEnvIO(Env* hosted_env) : hosted_env_(hosted_env) {}

WinEnvIO::~WinEnvIO() {}

IOStatus WinFileSystem::DeleteFile(const std::string& fname,
                                   const IOOptions& /*options*/,
                                   IODebugContext* /*dbg*/) {
  IOStatus result;

  BOOL ret = RX_DeleteFile(RX_FN(fname).c_str());

  if (!ret) {
    auto lastError = GetLastError();
    result = IOErrorFromWindowsError("Failed to delete: " + fname, lastError);
  }

  return result;
}

IOStatus WinFileSystem::Truncate(const std::string& fname, size_t size,
                                 const IOOptions& /*options*/,
                                 IODebugContext* /*dbg*/) {
  IOStatus s;
  int result = ROCKSDB_NAMESPACE::port::Truncate(fname, size);
  if (result != 0) {
    s = IOError("Failed to truncate: " + fname, errno);
  }
  return s;
}

IOStatus WinFileSystem::NewSequentialFile(
    const std::string& fname, const FileOptions& options,
    std::unique_ptr<FSSequentialFile>* result, IODebugContext* /*dbg*/) {
  IOStatus s;
D
Dmitri Smirnov 已提交
234

235
  result->reset();
D
Dmitri Smirnov 已提交
236

237 238 239 240
  // Corruption test needs to rename and delete files of these kind
  // while they are still open with another handle. For that reason we
  // allow share_write and delete(allows rename).
  HANDLE hFile = INVALID_HANDLE_VALUE;
D
Dmitri Smirnov 已提交
241 242 243 244 245 246 247

  DWORD fileFlags = FILE_ATTRIBUTE_READONLY;

  if (options.use_direct_reads && !options.use_mmap_reads) {
    fileFlags |= FILE_FLAG_NO_BUFFERING;
  }

248 249
  {
    IOSTATS_TIMER_GUARD(open_nanos);
250
    hFile = RX_CreateFile(
251 252 253 254
        RX_FN(fname).c_str(), GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
        OPEN_EXISTING,  // Original fopen mode is "rb"
        fileFlags, NULL);
255
  }
D
Dmitri Smirnov 已提交
256

257 258 259
  if (INVALID_HANDLE_VALUE == hFile) {
    auto lastError = GetLastError();
    s = IOErrorFromWindowsError("Failed to open NewSequentialFile" + fname,
260
                                lastError);
261 262
  } else {
    result->reset(new WinSequentialFile(fname, hFile, options));
D
Dmitri Smirnov 已提交
263
  }
264 265
  return s;
}
D
Dmitri Smirnov 已提交
266

267 268 269
IOStatus WinFileSystem::NewRandomAccessFile(
    const std::string& fname, const FileOptions& options,
    std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* dbg) {
270
  result->reset();
271
  IOStatus s;
D
Dmitri Smirnov 已提交
272

273 274 275
  // Open the file for read-only random access
  // Random access is to disable read-ahead as the system reads too much data
  DWORD fileFlags = FILE_ATTRIBUTE_READONLY;
D
Dmitri Smirnov 已提交
276

A
Aaron Gao 已提交
277
  if (options.use_direct_reads && !options.use_mmap_reads) {
278 279 280
    fileFlags |= FILE_FLAG_NO_BUFFERING;
  } else {
    fileFlags |= FILE_FLAG_RANDOM_ACCESS;
D
Dmitri Smirnov 已提交
281
  }
D
Dmitri Smirnov 已提交
282

283 284 285 286 287
  /// Shared access is necessary for corruption test to pass
  // almost all tests would work with a possible exception of fault_injection
  HANDLE hFile = 0;
  {
    IOSTATS_TIMER_GUARD(open_nanos);
288 289 290 291
    hFile =
        RX_CreateFile(RX_FN(fname).c_str(), GENERIC_READ,
                      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                      NULL, OPEN_EXISTING, fileFlags, NULL);
D
Dmitri Smirnov 已提交
292 293
  }

294 295 296
  if (INVALID_HANDLE_VALUE == hFile) {
    auto lastError = GetLastError();
    return IOErrorFromWindowsError(
297
        "NewRandomAccessFile failed to Create/Open: " + fname, lastError);
D
Dmitri Smirnov 已提交
298 299
  }

300
  UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc);
D
Dmitri Smirnov 已提交
301

302 303 304 305
  // CAUTION! This will map the entire file into the process address space
  if (options.use_mmap_reads && sizeof(void*) >= 8) {
    // Use mmap when virtual address-space is plentiful.
    uint64_t fileSize;
D
Dmitri Smirnov 已提交
306

307
    s = GetFileSize(fname, IOOptions(), &fileSize, dbg);
308

309 310 311
    if (s.ok()) {
      // Will not map empty files
      if (fileSize == 0) {
312 313
        return IOError("NewRandomAccessFile failed to map empty file: " + fname,
                       EINVAL);
D
Dmitri Smirnov 已提交
314
      }
315

316
      HANDLE hMap = RX_CreateFileMapping(hFile, NULL, PAGE_READONLY,
317 318 319
                                         0,  // At its present length
                                         0,
                                         NULL);  // Mapping name
D
Dmitri Smirnov 已提交
320

321 322 323
      if (!hMap) {
        auto lastError = GetLastError();
        return IOErrorFromWindowsError(
324 325
            "Failed to create file mapping for NewRandomAccessFile: " + fname,
            lastError);
D
Dmitri Smirnov 已提交
326 327
      }

328
      UniqueCloseHandlePtr mapGuard(hMap, CloseHandleFunc);
D
Dmitri Smirnov 已提交
329

330
      const void* mapped_region =
331 332 333 334 335
          MapViewOfFileEx(hMap, FILE_MAP_READ,
                          0,  // High DWORD of access start
                          0,  // Low DWORD
                          static_cast<SIZE_T>(fileSize),
                          NULL);  // Let the OS choose the mapping
D
Dmitri Smirnov 已提交
336

337 338
      if (!mapped_region) {
        auto lastError = GetLastError();
S
sdong 已提交
339
        return IOErrorFromWindowsError(
340 341
            "Failed to MapViewOfFile for NewRandomAccessFile: " + fname,
            lastError);
S
sdong 已提交
342
      }
343

344
      result->reset(new WinMmapReadableFile(fname, hFile, hMap, mapped_region,
345
                                            static_cast<size_t>(fileSize)));
346 347 348

      mapGuard.release();
      fileGuard.release();
D
Dmitri Smirnov 已提交
349
    }
350
  } else {
351 352
    result->reset(new WinRandomAccessFile(
        fname, hFile, std::max(GetSectorSize(fname), page_size_), options));
353 354 355 356
    fileGuard.release();
  }
  return s;
}
D
Dmitri Smirnov 已提交
357

358 359 360
IOStatus WinFileSystem::OpenWritableFile(
    const std::string& fname, const FileOptions& options,
    std::unique_ptr<FSWritableFile>* result, bool reopen) {
361
  const size_t c_BufferCapacity = 64 * 1024;
D
Dmitri Smirnov 已提交
362

363
  EnvOptions local_options(options);
D
Dmitri Smirnov 已提交
364

365
  result->reset();
366
  IOStatus s;
D
Dmitri Smirnov 已提交
367

368
  DWORD fileFlags = FILE_ATTRIBUTE_NORMAL;
D
Dmitri Smirnov 已提交
369

A
Aaron Gao 已提交
370
  if (local_options.use_direct_writes && !local_options.use_mmap_writes) {
371
    fileFlags = FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
D
Dmitri Smirnov 已提交
372 373
  }

374 375 376 377 378 379 380
  // Desired access. We are want to write only here but if we want to memory
  // map
  // the file then there is no write only mode so we have to create it
  // Read/Write
  // However, MapViewOfFile specifies only Write only
  DWORD desired_access = GENERIC_WRITE;
  DWORD shared_mode = FILE_SHARE_READ;
D
Dmitri Smirnov 已提交
381

382 383
  if (local_options.use_mmap_writes) {
    desired_access |= GENERIC_READ;
D
Dmitri Smirnov 已提交
384
  } else {
385 386 387 388
    // Adding this solely for tests to pass (fault_injection_test,
    // wal_manager_test).
    shared_mode |= (FILE_SHARE_WRITE | FILE_SHARE_DELETE);
  }
D
Dmitri Smirnov 已提交
389

390 391 392 393 394 395
  // This will always truncate the file
  DWORD creation_disposition = CREATE_ALWAYS;
  if (reopen) {
    creation_disposition = OPEN_ALWAYS;
  }

396 397 398
  HANDLE hFile = 0;
  {
    IOSTATS_TIMER_GUARD(open_nanos);
399
    hFile = RX_CreateFile(
400 401 402
        RX_FN(fname).c_str(),
        desired_access,  // Access desired
        shared_mode,
403
        NULL,  // Security attributes
404 405
        // Posix env says (reopen) ? (O_CREATE | O_APPEND) : O_CREAT | O_TRUNC
        creation_disposition,
406 407
        fileFlags,  // Flags
        NULL);      // Template File
408 409 410 411 412
  }

  if (INVALID_HANDLE_VALUE == hFile) {
    auto lastError = GetLastError();
    return IOErrorFromWindowsError(
413
        "Failed to create a NewWriteableFile: " + fname, lastError);
D
Dmitri Smirnov 已提交
414 415
  }

416 417 418 419 420 421 422 423
  // We will start writing at the end, appending
  if (reopen) {
    LARGE_INTEGER zero_move;
    zero_move.QuadPart = 0;
    BOOL ret = SetFilePointerEx(hFile, zero_move, NULL, FILE_END);
    if (!ret) {
      auto lastError = GetLastError();
      return IOErrorFromWindowsError(
424 425
          "Failed to create a ReopenWritableFile move to the end: " + fname,
          lastError);
426 427 428
    }
  }

429 430 431 432
  if (options.use_mmap_writes) {
    // We usually do not use mmmapping on SSD and thus we pass memory
    // page_size
    result->reset(new WinMmapFile(fname, hFile, page_size_,
433
                                  allocation_granularity_, local_options));
434 435 436
  } else {
    // Here we want the buffer allocation to be aligned by the SSD page size
    // and to be a multiple of it
437 438 439
    result->reset(new WinWritableFile(
        fname, hFile, std::max(GetSectorSize(fname), GetPageSize()),
        c_BufferCapacity, local_options));
440
  }
441 442
  return s;
}
443

444 445 446 447 448 449
IOStatus WinFileSystem::NewWritableFile(const std::string& fname,
                                        const FileOptions& options,
                                        std::unique_ptr<FSWritableFile>* result,
                                        IODebugContext* /*dbg*/) {
  return OpenWritableFile(fname, options, result, false);
}
450

451 452 453 454 455 456 457 458 459 460 461
IOStatus WinFileSystem::ReopenWritableFile(
    const std::string& fname, const FileOptions& options,
    std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) {
  return OpenWritableFile(fname, options, result, true);
}

IOStatus WinFileSystem::NewRandomRWFile(const std::string& fname,
                                        const FileOptions& options,
                                        std::unique_ptr<FSRandomRWFile>* result,
                                        IODebugContext* /*dbg*/) {
  IOStatus s;
462 463 464 465 466

  // Open the file for read-only random access
  // Random access is to disable read-ahead as the system reads too much data
  DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
  DWORD shared_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
467
  DWORD creation_disposition = OPEN_EXISTING;  // Fail if file does not exist
468 469
  DWORD file_flags = FILE_FLAG_RANDOM_ACCESS;

A
Aaron Gao 已提交
470
  if (options.use_direct_reads && options.use_direct_writes) {
471 472 473 474 475 476 477 478
    file_flags |= FILE_FLAG_NO_BUFFERING;
  }

  /// Shared access is necessary for corruption test to pass
  // almost all tests would work with a possible exception of fault_injection
  HANDLE hFile = 0;
  {
    IOSTATS_TIMER_GUARD(open_nanos);
479 480 481
    hFile = RX_CreateFile(RX_FN(fname).c_str(), desired_access, shared_mode,
                          NULL,  // Security attributes
                          creation_disposition, file_flags, NULL);
482 483 484 485 486
  }

  if (INVALID_HANDLE_VALUE == hFile) {
    auto lastError = GetLastError();
    return IOErrorFromWindowsError(
487
        "NewRandomRWFile failed to Create/Open: " + fname, lastError);
488 489 490
  }

  UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc);
491 492
  result->reset(new WinRandomRWFile(
      fname, hFile, std::max(GetSectorSize(fname), GetPageSize()), options));
493 494 495 496 497
  fileGuard.release();

  return s;
}

498 499 500
IOStatus WinFileSystem::NewMemoryMappedFileBuffer(
    const std::string& fname, std::unique_ptr<MemoryMappedFileBuffer>* result) {
  IOStatus s;
D
Dmitri Smirnov 已提交
501 502 503 504 505 506 507
  result->reset();

  DWORD fileFlags = FILE_ATTRIBUTE_READONLY;

  HANDLE hFile = INVALID_HANDLE_VALUE;
  {
    IOSTATS_TIMER_GUARD(open_nanos);
508
    hFile = RX_CreateFile(
509
        RX_FN(fname).c_str(), GENERIC_READ | GENERIC_WRITE,
510
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
511
        OPEN_EXISTING,  // Open only if it exists
512
        fileFlags, NULL);
D
Dmitri Smirnov 已提交
513 514 515 516
  }

  if (INVALID_HANDLE_VALUE == hFile) {
    auto lastError = GetLastError();
517 518
    s = IOErrorFromWindowsError(
        "Failed to open NewMemoryMappedFileBuffer: " + fname, lastError);
D
Dmitri Smirnov 已提交
519 520 521 522 523
    return s;
  }
  UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc);

  uint64_t fileSize = 0;
524
  s = GetFileSize(fname, IOOptions(), &fileSize, nullptr);
D
Dmitri Smirnov 已提交
525 526 527 528 529
  if (!s.ok()) {
    return s;
  }
  // Will not map empty files
  if (fileSize == 0) {
530
    return IOStatus::NotSupported(
531
        "NewMemoryMappedFileBuffer can not map zero length files: " + fname);
D
Dmitri Smirnov 已提交
532 533 534 535
  }

  // size_t is 32-bit with 32-bit builds
  if (fileSize > std::numeric_limits<size_t>::max()) {
536 537 538
    return IOStatus::NotSupported(
        "The specified file size does not fit into 32-bit memory addressing: " +
        fname);
D
Dmitri Smirnov 已提交
539 540
  }

541
  HANDLE hMap = RX_CreateFileMapping(hFile, NULL, PAGE_READWRITE,
542 543 544
                                     0,  // Whole file at its present length
                                     0,
                                     NULL);  // Mapping name
D
Dmitri Smirnov 已提交
545 546 547 548

  if (!hMap) {
    auto lastError = GetLastError();
    return IOErrorFromWindowsError(
549
        "Failed to create file mapping for: " + fname, lastError);
D
Dmitri Smirnov 已提交
550 551 552 553
  }
  UniqueCloseHandlePtr mapGuard(hMap, CloseHandleFunc);

  void* base = MapViewOfFileEx(hMap, FILE_MAP_WRITE,
554 555 556 557
                               0,  // High DWORD of access start
                               0,  // Low DWORD
                               static_cast<SIZE_T>(fileSize),
                               NULL);  // Let the OS choose the mapping
D
Dmitri Smirnov 已提交
558 559 560 561

  if (!base) {
    auto lastError = GetLastError();
    return IOErrorFromWindowsError(
562 563
        "Failed to MapViewOfFile for NewMemoryMappedFileBuffer: " + fname,
        lastError);
D
Dmitri Smirnov 已提交
564 565
  }

566 567
  result->reset(new WinMemoryMappedBuffer(hFile, hMap, base,
                                          static_cast<size_t>(fileSize)));
D
Dmitri Smirnov 已提交
568 569 570 571 572 573 574

  mapGuard.release();
  fileGuard.release();

  return s;
}

575 576 577 578 579
IOStatus WinFileSystem::NewDirectory(const std::string& name,
                                     const IOOptions& /*options*/,
                                     std::unique_ptr<FSDirectory>* result,
                                     IODebugContext* /*dbg*/) {
  IOStatus s;
580 581
  // Must be nullptr on failure
  result->reset();
D
Dmitri Smirnov 已提交
582

583
  if (!DirExists(name)) {
584
    s = IOErrorFromWindowsError("open folder: " + name, ERROR_DIRECTORY);
D
Dmitri Smirnov 已提交
585 586 587 588 589 590
    return s;
  }

  HANDLE handle = INVALID_HANDLE_VALUE;
  // 0 - for access means read metadata
  {
591
    IOSTATS_TIMER_GUARD(open_nanos);
592 593
    handle = RX_CreateFile(
        RX_FN(name).c_str(), 0,
594
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
595
        OPEN_EXISTING,
596
        FILE_FLAG_BACKUP_SEMANTICS,  // make opening folders possible
597
        NULL);
598
  }
D
Dmitri Smirnov 已提交
599 600 601

  if (INVALID_HANDLE_VALUE == handle) {
    auto lastError = GetLastError();
602
    s = IOErrorFromWindowsError("open folder: " + name, lastError);
D
Dmitri Smirnov 已提交
603 604 605 606 607
    return s;
  }

  result->reset(new WinDirectory(handle));

608 609
  return s;
}
D
Dmitri Smirnov 已提交
610

611 612 613 614
IOStatus WinFileSystem::FileExists(const std::string& fname,
                                   const IOOptions& /*opts*/,
                                   IODebugContext* /*dbg*/) {
  IOStatus s;
D
Dmitri Smirnov 已提交
615 616 617 618
  // TODO: This does not follow symbolic links at this point
  // which is consistent with _access() impl on windows
  // but can be added
  WIN32_FILE_ATTRIBUTE_DATA attrs;
619 620
  if (FALSE == RX_GetFileAttributesEx(RX_FN(fname).c_str(),
                                      GetFileExInfoStandard, &attrs)) {
D
Dmitri Smirnov 已提交
621 622
    auto lastError = GetLastError();
    switch (lastError) {
623 624 625 626 627 628 629 630 631 632
      case ERROR_ACCESS_DENIED:
      case ERROR_NOT_FOUND:
      case ERROR_FILE_NOT_FOUND:
      case ERROR_PATH_NOT_FOUND:
        s = IOStatus::NotFound();
        break;
      default:
        s = IOErrorFromWindowsError("Unexpected error for: " + fname,
                                    lastError);
        break;
D
Dmitri Smirnov 已提交
633 634 635
    }
  }
  return s;
636
}
D
Dmitri Smirnov 已提交
637

638 639 640 641 642
IOStatus WinFileSystem::GetChildren(const std::string& dir,
                                    const IOOptions& /*opts*/,
                                    std::vector<std::string>* result,
                                    IODebugContext* /*dbg*/) {
  IOStatus status;
D
Dmitri Smirnov 已提交
643
  result->clear();
D
Dmitri Smirnov 已提交
644

645 646
  RX_WIN32_FIND_DATA data;
  memset(&data, 0, sizeof(data));
D
Dmitri Smirnov 已提交
647 648
  std::string pattern(dir);
  pattern.append("\\").append("*");
D
Dmitri Smirnov 已提交
649

650 651 652 653 654 655
  HANDLE handle =
      RX_FindFirstFileEx(RX_FN(pattern).c_str(),
                         // Do not want alternative name
                         FindExInfoBasic, &data, FindExSearchNameMatch,
                         NULL,  // lpSearchFilter
                         0);
D
Dmitri Smirnov 已提交
656

D
Dmitri Smirnov 已提交
657 658 659
  if (handle == INVALID_HANDLE_VALUE) {
    auto lastError = GetLastError();
    switch (lastError) {
660 661 662 663 664 665 666 667 668
      case ERROR_NOT_FOUND:
      case ERROR_ACCESS_DENIED:
      case ERROR_FILE_NOT_FOUND:
      case ERROR_PATH_NOT_FOUND:
        status = IOStatus::NotFound();
        break;
      default:
        status = IOErrorFromWindowsError("Failed to GetChhildren for: " + dir,
                                         lastError);
669
    }
D
Dmitri Smirnov 已提交
670
    return status;
671
  }
672

D
Dmitri Smirnov 已提交
673 674 675 676 677 678
  UniqueFindClosePtr fc(handle, FindCloseFunc);

  // For safety
  data.cFileName[MAX_PATH - 1] = 0;

  while (true) {
679 680 681 682
    // filter out '.' and '..' directory entries
    // which appear only on some platforms
    const bool ignore =
        ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
683 684
        (RX_FNCMP(data.cFileName, ".") == 0 ||
         RX_FNCMP(data.cFileName, "..") == 0);
685 686 687 688 689
    if (!ignore) {
      auto x = RX_FILESTRING(data.cFileName, RX_FNLEN(data.cFileName));
      result->push_back(FN_TO_RX(x));
    }

690
    BOOL ret = -RX_FindNextFile(handle, &data);
D
Dmitri Smirnov 已提交
691 692 693 694 695 696 697 698
    // If the function fails the return value is zero
    // and non-zero otherwise. Not TRUE or FALSE.
    if (ret == FALSE) {
      // Posix does not care why we stopped
      break;
    }
    data.cFileName[MAX_PATH - 1] = 0;
  }
699 700
  return status;
}
D
Dmitri Smirnov 已提交
701

702 703 704 705
IOStatus WinFileSystem::CreateDir(const std::string& name,
                                  const IOOptions& /*opts*/,
                                  IODebugContext* /*dbg*/) {
  IOStatus result;
706
  BOOL ret = RX_CreateDirectory(RX_FN(name).c_str(), NULL);
D
Dmitri Smirnov 已提交
707 708
  if (!ret) {
    auto lastError = GetLastError();
709 710
    result = IOErrorFromWindowsError("Failed to create a directory: " + name,
                                     lastError);
D
Dmitri Smirnov 已提交
711 712
  }

713 714
  return result;
}
D
Dmitri Smirnov 已提交
715

716 717 718 719
IOStatus WinFileSystem::CreateDirIfMissing(const std::string& name,
                                           const IOOptions& /*opts*/,
                                           IODebugContext* /*dbg*/) {
  IOStatus result;
720

D
Dmitri Smirnov 已提交
721 722 723 724
  if (DirExists(name)) {
    return result;
  }

725
  BOOL ret = RX_CreateDirectory(RX_FN(name).c_str(), NULL);
D
Dmitri Smirnov 已提交
726 727 728
  if (!ret) {
    auto lastError = GetLastError();
    if (lastError != ERROR_ALREADY_EXISTS) {
729 730
      result = IOErrorFromWindowsError("Failed to create a directory: " + name,
                                       lastError);
D
Dmitri Smirnov 已提交
731
    } else {
732
      result = IOStatus::IOError(name + ": exists but is not a directory");
D
Dmitri Smirnov 已提交
733 734
    }
  }
735 736
  return result;
}
D
Dmitri Smirnov 已提交
737

738 739 740 741
IOStatus WinFileSystem::DeleteDir(const std::string& name,
                                  const IOOptions& /*options*/,
                                  IODebugContext* /*dbg*/) {
  IOStatus result;
742
  BOOL ret = RX_RemoveDirectory(RX_FN(name).c_str());
D
Dmitri Smirnov 已提交
743 744
  if (!ret) {
    auto lastError = GetLastError();
745 746
    result =
        IOErrorFromWindowsError("Failed to remove dir: " + name, lastError);
D
Dmitri Smirnov 已提交
747
  }
748 749
  return result;
}
D
Dmitri Smirnov 已提交
750

751 752 753 754
IOStatus WinFileSystem::GetFileSize(const std::string& fname,
                                    const IOOptions& /*opts*/, uint64_t* size,
                                    IODebugContext* /*dbg*/) {
  IOStatus s;
755

756
  WIN32_FILE_ATTRIBUTE_DATA attrs;
757 758
  if (RX_GetFileAttributesEx(RX_FN(fname).c_str(), GetFileExInfoStandard,
                             &attrs)) {
759 760 761 762 763 764 765
    ULARGE_INTEGER file_size;
    file_size.HighPart = attrs.nFileSizeHigh;
    file_size.LowPart = attrs.nFileSizeLow;
    *size = file_size.QuadPart;
  } else {
    auto lastError = GetLastError();
    s = IOErrorFromWindowsError("Can not get size for: " + fname, lastError);
D
Dmitri Smirnov 已提交
766
  }
767 768
  return s;
}
D
Dmitri Smirnov 已提交
769

770
uint64_t WinFileSystem::FileTimeToUnixTime(const FILETIME& ftTime) {
771 772 773 774 775 776 777 778 779 780 781 782 783
  const uint64_t c_FileTimePerSecond = 10000000U;
  // UNIX epoch starts on 1970-01-01T00:00:00Z
  // Windows FILETIME starts on 1601-01-01T00:00:00Z
  // Therefore, we need to subtract the below number of seconds from
  // the seconds that we obtain from FILETIME with an obvious loss of
  // precision
  const uint64_t c_SecondBeforeUnixEpoch = 11644473600U;

  ULARGE_INTEGER li;
  li.HighPart = ftTime.dwHighDateTime;
  li.LowPart = ftTime.dwLowDateTime;

  uint64_t result =
784
      (li.QuadPart / c_FileTimePerSecond) - c_SecondBeforeUnixEpoch;
785 786 787
  return result;
}

788 789 790 791 792
IOStatus WinFileSystem::GetFileModificationTime(const std::string& fname,
                                                const IOOptions& /*opts*/,
                                                uint64_t* file_mtime,
                                                IODebugContext* /*dbg*/) {
  IOStatus s;
793 794

  WIN32_FILE_ATTRIBUTE_DATA attrs;
795
  if (RX_GetFileAttributesEx(RX_FN(fname).c_str(), GetFileExInfoStandard,
796
                             &attrs)) {
797 798 799 800
    *file_mtime = FileTimeToUnixTime(attrs.ftLastWriteTime);
  } else {
    auto lastError = GetLastError();
    s = IOErrorFromWindowsError(
801
        "Can not get file modification time for: " + fname, lastError);
802
    *file_mtime = 0;
D
Dmitri Smirnov 已提交
803 804
  }

805 806
  return s;
}
D
Dmitri Smirnov 已提交
807

808 809 810 811 812
IOStatus WinFileSystem::RenameFile(const std::string& src,
                                   const std::string& target,
                                   const IOOptions& /*opts*/,
                                   IODebugContext* /*dbg*/) {
  IOStatus result;
813

814 815
  // rename() is not capable of replacing the existing file as on Linux
  // so use OS API directly
816 817
  if (!RX_MoveFileEx(RX_FN(src).c_str(), RX_FN(target).c_str(),
                     MOVEFILE_REPLACE_EXISTING)) {
818
    DWORD lastError = GetLastError();
D
Dmitri Smirnov 已提交
819

820 821
    std::string text("Failed to rename: ");
    text.append(src).append(" to: ").append(target);
D
Dmitri Smirnov 已提交
822

823
    result = IOErrorFromWindowsError(text, lastError);
D
Dmitri Smirnov 已提交
824 825
  }

826 827
  return result;
}
828

829 830 831 832 833
IOStatus WinFileSystem::LinkFile(const std::string& src,
                                 const std::string& target,
                                 const IOOptions& /*opts*/,
                                 IODebugContext* /*dbg*/) {
  IOStatus result;
D
Dmitri Smirnov 已提交
834

835
  if (!RX_CreateHardLink(RX_FN(target).c_str(), RX_FN(src).c_str(), NULL)) {
836
    DWORD lastError = GetLastError();
837
    if (lastError == ERROR_NOT_SAME_DEVICE) {
838
      return IOStatus::NotSupported("No cross FS links allowed");
839
    }
840

841 842
    std::string text("Failed to link: ");
    text.append(src).append(" to: ").append(target);
843

844
    result = IOErrorFromWindowsError(text, lastError);
845 846
  }

847 848
  return result;
}
849

850 851 852 853 854 855 856 857
IOStatus WinFileSystem::NumFileLinks(const std::string& fname,
                                     const IOOptions& /*opts*/, uint64_t* count,
                                     IODebugContext* /*dbg*/) {
  IOStatus s;
  HANDLE handle =
      RX_CreateFile(RX_FN(fname).c_str(), 0,
                    FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
858 859 860

  if (INVALID_HANDLE_VALUE == handle) {
    auto lastError = GetLastError();
861
    s = IOErrorFromWindowsError("NumFileLinks: " + fname, lastError);
862 863 864 865
    return s;
  }
  UniqueCloseHandlePtr handle_guard(handle, CloseHandleFunc);
  FILE_STANDARD_INFO standard_info;
866 867 868
  if (0 != GetFileInformationByHandleEx(handle, FileStandardInfo,
                                        &standard_info,
                                        sizeof(standard_info))) {
869 870 871
    *count = standard_info.NumberOfLinks;
  } else {
    auto lastError = GetLastError();
872 873
    s = IOErrorFromWindowsError("GetFileInformationByHandleEx: " + fname,
                                lastError);
874 875 876 877
  }
  return s;
}

878 879 880 881
IOStatus WinFileSystem::AreFilesSame(const std::string& first,
                                     const std::string& second,
                                     const IOOptions& /*opts*/, bool* res,
                                     IODebugContext* /*dbg*/) {
D
Dmitri Smirnov 已提交
882 883
// For MinGW builds
#if (_WIN32_WINNT == _WIN32_WINNT_VISTA)
884
  IOStatus s = IOStatus::NotSupported();
D
Dmitri Smirnov 已提交
885 886
#else
  assert(res != nullptr);
887
  IOStatus s;
D
Dmitri Smirnov 已提交
888
  if (res == nullptr) {
889
    s = IOStatus::InvalidArgument("res");
D
Dmitri Smirnov 已提交
890 891 892 893
    return s;
  }

  // 0 - for access means read metadata
894 895
  HANDLE file_1 = RX_CreateFile(
      RX_FN(first).c_str(), 0,
896
      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
897
      OPEN_EXISTING,
898
      FILE_FLAG_BACKUP_SEMANTICS,  // make opening folders possible
899
      NULL);
D
Dmitri Smirnov 已提交
900 901 902

  if (INVALID_HANDLE_VALUE == file_1) {
    auto lastError = GetLastError();
903
    s = IOErrorFromWindowsError("open file: " + first, lastError);
D
Dmitri Smirnov 已提交
904 905 906 907
    return s;
  }
  UniqueCloseHandlePtr g_1(file_1, CloseHandleFunc);

908 909
  HANDLE file_2 = RX_CreateFile(
      RX_FN(second).c_str(), 0,
910 911 912
      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
      OPEN_EXISTING,
      FILE_FLAG_BACKUP_SEMANTICS,  // make opening folders possible
913
      NULL);
D
Dmitri Smirnov 已提交
914 915 916

  if (INVALID_HANDLE_VALUE == file_2) {
    auto lastError = GetLastError();
917
    s = IOErrorFromWindowsError("open file: " + second, lastError);
D
Dmitri Smirnov 已提交
918 919 920 921 922 923
    return s;
  }
  UniqueCloseHandlePtr g_2(file_2, CloseHandleFunc);

  FILE_ID_INFO FileInfo_1;
  BOOL result = GetFileInformationByHandleEx(file_1, FileIdInfo, &FileInfo_1,
924
                                             sizeof(FileInfo_1));
D
Dmitri Smirnov 已提交
925 926 927

  if (!result) {
    auto lastError = GetLastError();
928
    s = IOErrorFromWindowsError("stat file: " + first, lastError);
D
Dmitri Smirnov 已提交
929 930 931
    return s;
  }

932 933 934
  FILE_ID_INFO FileInfo_2;
  result = GetFileInformationByHandleEx(file_2, FileIdInfo, &FileInfo_2,
                                        sizeof(FileInfo_2));
D
Dmitri Smirnov 已提交
935 936 937

  if (!result) {
    auto lastError = GetLastError();
938
    s = IOErrorFromWindowsError("stat file: " + second, lastError);
D
Dmitri Smirnov 已提交
939 940 941 942
    return s;
  }

  if (FileInfo_1.VolumeSerialNumber == FileInfo_2.VolumeSerialNumber) {
943 944 945
    *res =
        (0 == memcmp(FileInfo_1.FileId.Identifier, FileInfo_2.FileId.Identifier,
                     sizeof(FileInfo_1.FileId.Identifier)));
D
Dmitri Smirnov 已提交
946 947 948 949 950 951 952
  } else {
    *res = false;
  }
#endif
  return s;
}

953 954 955
IOStatus WinFileSystem::LockFile(const std::string& lockFname,
                                 const IOOptions& /*opts*/, FileLock** lock,
                                 IODebugContext* /*dbg*/) {
956
  assert(lock != nullptr);
957

958
  *lock = NULL;
959
  IOStatus result;
960

961 962
  // No-sharing, this is a LOCK file
  const DWORD ExclusiveAccessON = 0;
963

964 965 966 967 968 969
  // Obtain exclusive access to the LOCK file
  // Previously, instead of NORMAL attr we set DELETE on close and that worked
  // well except with fault_injection test that insists on deleting it.
  HANDLE hFile = 0;
  {
    IOSTATS_TIMER_GUARD(open_nanos);
970
    hFile = RX_CreateFile(RX_FN(lockFname).c_str(),
971 972
                          (GENERIC_READ | GENERIC_WRITE), ExclusiveAccessON,
                          NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
973 974
  }

975 976
  if (INVALID_HANDLE_VALUE == hFile) {
    auto lastError = GetLastError();
977 978
    result = IOErrorFromWindowsError("Failed to create lock file: " + lockFname,
                                     lastError);
979 980
  } else {
    *lock = new WinFileLock(hFile);
D
Dmitri Smirnov 已提交
981
  }
S
sdong 已提交
982

983 984
  return result;
}
D
Dmitri Smirnov 已提交
985

986 987 988
IOStatus WinFileSystem::UnlockFile(FileLock* lock, const IOOptions& /*opts*/,
                                   IODebugContext* /*dbg*/) {
  IOStatus result;
989

990
  assert(lock != nullptr);
991

992
  delete lock;
D
Dmitri Smirnov 已提交
993

994 995
  return result;
}
996

997 998 999
IOStatus WinFileSystem::GetTestDirectory(const IOOptions& opts,
                                         std::string* result,
                                         IODebugContext* dbg) {
1000
  std::string output;
D
Dmitri Smirnov 已提交
1001

1002 1003 1004 1005 1006
  const char* env = getenv("TEST_TMPDIR");
  if (env && env[0] != '\0') {
    output = env;
  } else {
    env = getenv("TMP");
D
Dmitri Smirnov 已提交
1007

1008 1009 1010 1011
    if (env && env[0] != '\0') {
      output = env;
    } else {
      output = "c:\\tmp";
D
Dmitri Smirnov 已提交
1012
    }
1013
  }
1014
  CreateDir(output, opts, dbg);
1015

1016
  output.append("\\testrocksdb-");
L
Lucian Petrut 已提交
1017
  output.append(std::to_string(GetCurrentProcessId()));
1018

1019
  CreateDir(output, opts, dbg);
D
Dmitri Smirnov 已提交
1020

1021
  output.swap(*result);
D
Dmitri Smirnov 已提交
1022

1023
  return IOStatus::OK();
1024
}
D
Dmitri Smirnov 已提交
1025

1026 1027 1028 1029 1030
IOStatus WinFileSystem::NewLogger(const std::string& fname,
                                  const IOOptions& /*opts*/,
                                  std::shared_ptr<Logger>* result,
                                  IODebugContext* /*dbg*/) {
  IOStatus s;
D
Dmitri Smirnov 已提交
1031

1032
  result->reset();
D
Dmitri Smirnov 已提交
1033

1034 1035 1036
  HANDLE hFile = 0;
  {
    IOSTATS_TIMER_GUARD(open_nanos);
1037
    hFile = RX_CreateFile(
1038 1039 1040 1041 1042 1043 1044 1045
        RX_FN(fname).c_str(), GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_DELETE,  // In RocksDb log files are
        // renamed and deleted before
        // they are closed. This enables
        // doing so.
        NULL,
        CREATE_ALWAYS,  // Original fopen mode is "w"
        FILE_ATTRIBUTE_NORMAL, NULL);
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
  }

  if (INVALID_HANDLE_VALUE == hFile) {
    auto lastError = GetLastError();
    s = IOErrorFromWindowsError("Failed to open LogFile" + fname, lastError);
  } else {
    {
      // With log files we want to set the true creation time as of now
      // because the system
      // for some reason caches the attributes of the previous file that just
      // been renamed from
      // this name so auto_roll_logger_test fails
      FILETIME ft;
      GetSystemTimeAsFileTime(&ft);
      // Set creation, last access and last write time to the same value
      SetFileTime(hFile, &ft, &ft, &ft);
    }
1063
    result->reset(new WinLogger(&WinEnvThreads::gettid, clock_.get(), hFile));
1064 1065 1066
  }
  return s;
}
D
Dmitri Smirnov 已提交
1067

1068 1069 1070
IOStatus WinFileSystem::IsDirectory(const std::string& path,
                                    const IOOptions& /*opts*/, bool* is_dir,
                                    IODebugContext* /*dbg*/) {
1071 1072 1073 1074
  BOOL ret = RX_PathIsDirectory(RX_FN(path).c_str());
  if (is_dir) {
    *is_dir = ret ? true : false;
  }
1075
  return IOStatus::OK();
1076
}
D
Dmitri Smirnov 已提交
1077

1078 1079 1080
Status WinEnvIO::GetHostName(char* name, uint64_t len) {
  Status s;
  DWORD nSize = static_cast<DWORD>(
1081
      std::min<uint64_t>(len, std::numeric_limits<DWORD>::max()));
D
Dmitri Smirnov 已提交
1082

1083 1084 1085 1086 1087
  if (!::GetComputerNameA(name, &nSize)) {
    auto lastError = GetLastError();
    s = IOErrorFromWindowsError("GetHostName", lastError);
  } else {
    name[nSize] = 0;
1088
  }
D
Dmitri Smirnov 已提交
1089

1090 1091 1092
  return s;
}

1093 1094 1095 1096
IOStatus WinFileSystem::GetAbsolutePath(const std::string& db_path,
                                        const IOOptions& /*options*/,
                                        std::string* output_path,
                                        IODebugContext* dbg) {
1097
  // Check if we already have an absolute path
D
Dmitri Smirnov 已提交
1098 1099 1100
  // For test compatibility we will consider starting slash as an
  // absolute path
  if ((!db_path.empty() && (db_path[0] == '\\' || db_path[0] == '/')) ||
1101
      !RX_PathIsRelative(RX_FN(db_path).c_str())) {
1102
    *output_path = db_path;
1103
    return IOStatus::OK();
D
Dmitri Smirnov 已提交
1104 1105
  }

1106
  RX_FILESTRING result;
D
Dmitri Smirnov 已提交
1107
  result.resize(MAX_PATH);
D
Dmitri Smirnov 已提交
1108

D
Dmitri Smirnov 已提交
1109 1110
  // Hopefully no changes the current directory while we do this
  // however _getcwd also suffers from the same limitation
1111
  DWORD len = RX_GetCurrentDirectory(MAX_PATH, &result[0]);
D
Dmitri Smirnov 已提交
1112 1113 1114
  if (len == 0) {
    auto lastError = GetLastError();
    return IOErrorFromWindowsError("Failed to get current working directory",
1115
                                   lastError);
1116
  }
D
Dmitri Smirnov 已提交
1117

D
Dmitri Smirnov 已提交
1118
  result.resize(len);
1119
  std::string res = FN_TO_RX(result);
1120

1121
  res.swap(*output_path);
1122
  return IOStatus::OK();
1123
}
D
Dmitri Smirnov 已提交
1124

1125 1126 1127 1128
IOStatus WinFileSystem::GetFreeSpace(const std::string& path,
                                     const IOOptions& /*options*/,
                                     uint64_t* diskfree,
                                     IODebugContext* /*dbg*/) {
1129 1130 1131 1132 1133
  assert(diskfree != nullptr);
  ULARGE_INTEGER freeBytes;
  BOOL f = RX_GetDiskFreeSpaceEx(RX_FN(path).c_str(), &freeBytes, NULL, NULL);
  if (f) {
    *diskfree = freeBytes.QuadPart;
1134
    return IOStatus::OK();
1135 1136 1137 1138 1139 1140 1141
  } else {
    DWORD lastError = GetLastError();
    return IOErrorFromWindowsError("Failed to get free space: " + path,
                                   lastError);
  }
}

1142 1143 1144
FileOptions WinFileSystem::OptimizeForLogWrite(
    const FileOptions& file_options, const DBOptions& db_options) const {
  FileOptions optimized(file_options);
D
Dmitri Smirnov 已提交
1145
  // These two the same as default optimizations
1146
  optimized.bytes_per_sync = db_options.wal_bytes_per_sync;
1147 1148
  optimized.writable_file_max_buffer_size =
      db_options.writable_file_max_buffer_size;
D
Dmitri Smirnov 已提交
1149 1150 1151 1152 1153 1154

  // This adversely affects %999 on windows
  optimized.use_mmap_writes = false;
  // Direct writes will produce a huge perf impact on
  // Windows. Pre-allocate space for WAL.
  optimized.use_direct_writes = false;
1155 1156
  return optimized;
}
D
Dmitri Smirnov 已提交
1157

1158 1159 1160
FileOptions WinFileSystem::OptimizeForManifestWrite(
    const FileOptions& options) const {
  FileOptions optimized(options);
1161
  optimized.use_mmap_writes = false;
D
Dmitri Smirnov 已提交
1162 1163 1164 1165
  optimized.use_direct_reads = false;
  return optimized;
}

1166 1167 1168
FileOptions WinFileSystem::OptimizeForManifestRead(
    const FileOptions& file_options) const {
  FileOptions optimized(file_options);
D
Dmitri Smirnov 已提交
1169 1170
  optimized.use_mmap_writes = false;
  optimized.use_direct_reads = false;
1171 1172 1173 1174
  return optimized;
}

// Returns true iff the named directory exists and is a directory.
1175
bool WinFileSystem::DirExists(const std::string& dname) {
1176
  WIN32_FILE_ATTRIBUTE_DATA attrs;
1177 1178
  if (RX_GetFileAttributesEx(RX_FN(dname).c_str(), GetFileExInfoStandard,
                             &attrs)) {
1179
    return 0 != (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
D
Dmitri Smirnov 已提交
1180
  }
1181 1182
  return false;
}
D
Dmitri Smirnov 已提交
1183

1184
size_t WinFileSystem::GetSectorSize(const std::string& fname) {
D
Dmitri Smirnov 已提交
1185 1186
  size_t sector_size = kSectorSize;

1187
  if (RX_PathIsRelative(RX_FN(fname).c_str())) {
D
Dmitri Smirnov 已提交
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
    return sector_size;
  }

  // obtain device handle
  char devicename[7] = "\\\\.\\";
  int erresult = strncat_s(devicename, sizeof(devicename), fname.c_str(), 2);

  if (erresult) {
    assert(false);
    return sector_size;
  }

1200 1201
  HANDLE hDevice = CreateFile(devicename, 0, 0, nullptr, OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL, nullptr);
D
Dmitri Smirnov 已提交
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213

  if (hDevice == INVALID_HANDLE_VALUE) {
    return sector_size;
  }

  STORAGE_PROPERTY_QUERY spropertyquery;
  spropertyquery.PropertyId = StorageAccessAlignmentProperty;
  spropertyquery.QueryType = PropertyStandardQuery;

  BYTE output_buffer[sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)];
  DWORD output_bytes = 0;

1214 1215 1216 1217
  BOOL ret = DeviceIoControl(
      hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &spropertyquery,
      sizeof(spropertyquery), output_buffer,
      sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR), &output_bytes, nullptr);
D
Dmitri Smirnov 已提交
1218 1219

  if (ret) {
1220 1221
    sector_size = ((STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR*)output_buffer)
                      ->BytesPerLogicalSector;
D
Dmitri Smirnov 已提交
1222
  } else {
1223 1224
    // many devices do not support StorageProcessAlignmentProperty. Any failure
    // here and we fall back to logical alignment
D
Dmitri Smirnov 已提交
1225

1226 1227 1228
    DISK_GEOMETRY_EX geometry = {0};
    ret = DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, nullptr, 0,
                          &geometry, sizeof(geometry), &output_bytes, nullptr);
D
Dmitri Smirnov 已提交
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
    if (ret) {
      sector_size = geometry.Geometry.BytesPerSector;
    }
  }

  if (hDevice != INVALID_HANDLE_VALUE) {
    CloseHandle(hDevice);
  }

  return sector_size;
}

1241 1242
////////////////////////////////////////////////////////////////////////
// WinEnvThreads
D
Dmitri Smirnov 已提交
1243

1244 1245
WinEnvThreads::WinEnvThreads(Env* hosted_env)
    : hosted_env_(hosted_env), thread_pools_(Env::Priority::TOTAL) {
1246 1247
  for (int pool_id = 0; pool_id < Env::Priority::TOTAL; ++pool_id) {
    thread_pools_[pool_id].SetThreadPriority(
1248
        static_cast<Env::Priority>(pool_id));
1249 1250
    // This allows later initializing the thread-local-env of each thread.
    thread_pools_[pool_id].SetHostEnv(hosted_env);
D
Dmitri Smirnov 已提交
1251
  }
1252
}
D
Dmitri Smirnov 已提交
1253

1254 1255
WinEnvThreads::~WinEnvThreads() {
  WaitForJoin();
D
Dmitri Smirnov 已提交
1256

1257 1258
  for (auto& thpool : thread_pools_) {
    thpool.JoinAllThreads();
D
Dmitri Smirnov 已提交
1259 1260 1261
  }
}

1262
void WinEnvThreads::Schedule(void (*function)(void*), void* arg,
1263
                             Env::Priority pri, void* tag,
1264
                             void (*unschedFunction)(void* arg)) {
A
Andrew Kryczka 已提交
1265
  assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1266 1267
  thread_pools_[pri].Schedule(function, arg, tag, unschedFunction);
}
D
Dmitri Smirnov 已提交
1268

1269 1270 1271
int WinEnvThreads::UnSchedule(void* arg, Env::Priority pri) {
  return thread_pools_[pri].UnSchedule(arg);
}
D
Dmitri Smirnov 已提交
1272

1273
namespace {
D
Dmitri Smirnov 已提交
1274

1275 1276 1277 1278
struct StartThreadState {
  void (*user_function)(void*);
  void* arg;
};
D
Dmitri Smirnov 已提交
1279

1280 1281
void* StartThreadWrapper(void* arg) {
  std::unique_ptr<StartThreadState> state(
1282
      reinterpret_cast<StartThreadState*>(arg));
1283 1284
  state->user_function(state->arg);
  return nullptr;
1285
}
D
Dmitri Smirnov 已提交
1286

1287 1288 1289
}  // namespace

void WinEnvThreads::StartThread(void (*function)(void* arg), void* arg) {
1290 1291 1292 1293
  std::unique_ptr<StartThreadState> state(new StartThreadState);
  state->user_function = function;
  state->arg = arg;
  try {
1294
    ROCKSDB_NAMESPACE::port::WindowsThread th(&StartThreadWrapper, state.get());
1295
    state.release();
D
Dmitri Smirnov 已提交
1296

1297 1298
    std::lock_guard<std::mutex> lg(mu_);
    threads_to_join_.push_back(std::move(th));
D
Dmitri Smirnov 已提交
1299

1300 1301
  } catch (const std::system_error& ex) {
    WinthreadCall("start thread", ex.code());
D
Dmitri Smirnov 已提交
1302
  }
1303
}
D
Dmitri Smirnov 已提交
1304

1305 1306 1307
void WinEnvThreads::WaitForJoin() {
  for (auto& th : threads_to_join_) {
    th.join();
D
Dmitri Smirnov 已提交
1308
  }
1309 1310
  threads_to_join_.clear();
}
D
Dmitri Smirnov 已提交
1311

1312
unsigned int WinEnvThreads::GetThreadPoolQueueLen(Env::Priority pri) const {
A
Andrew Kryczka 已提交
1313
  assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1314 1315
  return thread_pools_[pri].GetQueueLen();
}
D
Dmitri Smirnov 已提交
1316

1317 1318 1319 1320
uint64_t WinEnvThreads::gettid() {
  uint64_t thread_id = GetCurrentThreadId();
  return thread_id;
}
D
Dmitri Smirnov 已提交
1321

1322
uint64_t WinEnvThreads::GetThreadID() const { return gettid(); }
D
Dmitri Smirnov 已提交
1323

1324
void WinEnvThreads::SetBackgroundThreads(int num, Env::Priority pri) {
A
Andrew Kryczka 已提交
1325
  assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1326 1327
  thread_pools_[pri].SetBackgroundThreads(num);
}
D
Dmitri Smirnov 已提交
1328

1329
int WinEnvThreads::GetBackgroundThreads(Env::Priority pri) {
A
Andrew Kryczka 已提交
1330
  assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1331 1332 1333
  return thread_pools_[pri].GetBackgroundThreads();
}

1334
void WinEnvThreads::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) {
A
Andrew Kryczka 已提交
1335
  assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1336 1337
  thread_pools_[pri].IncBackgroundThreadsIfNeeded(num);
}
D
Dmitri Smirnov 已提交
1338

1339 1340
/////////////////////////////////////////////////////////////////////////
// WinEnv
D
Dmitri Smirnov 已提交
1341

1342
WinEnv::WinEnv()
1343
    : CompositeEnv(WinFileSystem::Default(), WinClock::Default()),
1344 1345
      winenv_io_(this),
      winenv_threads_(this) {
1346 1347 1348
  // Protected member of the base class
  thread_status_updater_ = CreateThreadStatusUpdater();
}
D
Dmitri Smirnov 已提交
1349

1350 1351 1352 1353 1354
WinEnv::~WinEnv() {
  // All threads must be joined before the deletion of
  // thread_status_updater_.
  delete thread_status_updater_;
}
D
Dmitri Smirnov 已提交
1355

1356
Status WinEnv::GetThreadList(std::vector<ThreadStatus>* thread_list) {
1357 1358 1359
  assert(thread_status_updater_);
  return thread_status_updater_->GetThreadList(thread_list);
}
D
Dmitri Smirnov 已提交
1360

1361 1362 1363
Status WinEnv::GetHostName(char* name, uint64_t len) {
  return winenv_io_.GetHostName(name, len);
}
1364

1365 1366
void WinEnv::Schedule(void (*function)(void*), void* arg, Env::Priority pri,
                      void* tag, void (*unschedFunction)(void* arg)) {
1367
  return winenv_threads_.Schedule(function, arg, pri, tag, unschedFunction);
D
Dmitri Smirnov 已提交
1368 1369
}

1370 1371
int WinEnv::UnSchedule(void* arg, Env::Priority pri) {
  return winenv_threads_.UnSchedule(arg, pri);
D
Dmitri Smirnov 已提交
1372 1373
}

1374
void WinEnv::StartThread(void (*function)(void* arg), void* arg) {
1375
  return winenv_threads_.StartThread(function, arg);
D
Dmitri Smirnov 已提交
1376 1377
}

1378
void WinEnv::WaitForJoin() { return winenv_threads_.WaitForJoin(); }
D
Dmitri Smirnov 已提交
1379

1380
unsigned int WinEnv::GetThreadPoolQueueLen(Env::Priority pri) const {
1381
  return winenv_threads_.GetThreadPoolQueueLen(pri);
D
Dmitri Smirnov 已提交
1382 1383
}

1384
uint64_t WinEnv::GetThreadID() const { return winenv_threads_.GetThreadID(); }
1385

1386
// Allow increasing the number of worker threads.
1387
void WinEnv::SetBackgroundThreads(int num, Env::Priority pri) {
1388 1389
  return winenv_threads_.SetBackgroundThreads(num, pri);
}
D
Dmitri Smirnov 已提交
1390

1391 1392 1393 1394
int WinEnv::GetBackgroundThreads(Env::Priority pri) {
  return winenv_threads_.GetBackgroundThreads(pri);
}

1395
void WinEnv::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) {
1396
  return winenv_threads_.IncBackgroundThreadsIfNeeded(num, pri);
D
Dmitri Smirnov 已提交
1397 1398
}

1399
}  // namespace port
D
Dmitri Smirnov 已提交
1400 1401 1402 1403 1404 1405 1406 1407

std::string Env::GenerateUniqueId() {
  std::string result;

  UUID uuid;
  UuidCreateSequential(&uuid);

  RPC_CSTR rpc_str;
O
Orgad Shaneh 已提交
1408 1409 1410
  auto status = UuidToStringA(&uuid, &rpc_str);
  (void)status;
  assert(status == RPC_S_OK);
D
Dmitri Smirnov 已提交
1411 1412 1413

  result = reinterpret_cast<char*>(rpc_str);

O
Orgad Shaneh 已提交
1414 1415
  status = RpcStringFreeA(&rpc_str);
  assert(status == RPC_S_OK);
D
Dmitri Smirnov 已提交
1416 1417 1418 1419

  return result;
}

1420 1421 1422 1423
std::shared_ptr<FileSystem> FileSystem::Default() {
  return port::WinFileSystem::Default();
}

1424 1425 1426 1427 1428 1429
const std::shared_ptr<SystemClock>& SystemClock::Default() {
  static std::shared_ptr<SystemClock> clock =
      std::make_shared<port::WinClock>();
  return clock;
}

1430 1431 1432
std::unique_ptr<Env> NewCompositeEnv(const std::shared_ptr<FileSystem>& fs) {
  return std::unique_ptr<Env>(new CompositeEnvWrapper(Env::Default(), fs));
}
1433
}  // namespace ROCKSDB_NAMESPACE
1434 1435

#endif