]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/port/win/env_win.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / port / win / env_win.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
2// This source code is licensed under both the GPLv2 (found in the
3// COPYING file in the root directory) and Apache 2.0 License
4// (found in the LICENSE.Apache file in the root directory).
7c673cae
FG
5//
6// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7// Use of this source code is governed by a BSD-style license that can be
8// found in the LICENSE file. See the AUTHORS file for names of contributors.
9
20effc67
TL
10#if defined(OS_WIN)
11
7c673cae 12#include "port/win/env_win.h"
7c673cae 13
1e59de90 14#include <direct.h> // _rmdir, _mkdir, _getcwd
7c673cae 15#include <errno.h>
1e59de90
TL
16#include <io.h> // _access
17#include <rpc.h> // for uuid generation
18#include <shlwapi.h>
7c673cae 19#include <sys/stat.h>
1e59de90
TL
20#include <sys/types.h>
21#include <windows.h>
22#include <winioctl.h>
7c673cae 23
1e59de90
TL
24#include <algorithm>
25#include <ctime>
26#include <thread>
7c673cae
FG
27
28#include "monitoring/iostats_context_imp.h"
7c673cae
FG
29#include "monitoring/thread_status_updater.h"
30#include "monitoring/thread_status_util.h"
1e59de90
TL
31#include "port/lang.h"
32#include "port/port.h"
33#include "port/port_dirent.h"
34#include "port/win/io_win.h"
35#include "port/win/win_logger.h"
36#include "rocksdb/env.h"
37#include "rocksdb/slice.h"
11fdf7f2 38#include "strsafe.h"
1e59de90 39#include "util/string_util.h"
11fdf7f2 40
1e59de90
TL
41// Undefine the functions windows might use (again)...
42#undef GetCurrentTime
43#undef DeleteFile
44#undef LoadLibrary
7c673cae 45
f67539c2 46namespace ROCKSDB_NAMESPACE {
7c673cae
FG
47
48ThreadStatusUpdater* CreateThreadStatusUpdater() {
49 return new ThreadStatusUpdater();
50}
51
52namespace {
53
494da23a
TL
54// Sector size used when physical sector size cannot be obtained from device.
55static const size_t kSectorSize = 512;
11fdf7f2 56
7c673cae
FG
57// RAII helpers for HANDLEs
58const auto CloseHandleFunc = [](HANDLE h) { ::CloseHandle(h); };
1e59de90 59using UniqueCloseHandlePtr = std::unique_ptr<void, decltype(CloseHandleFunc)>;
7c673cae 60
11fdf7f2 61const auto FindCloseFunc = [](HANDLE h) { ::FindClose(h); };
1e59de90 62using UniqueFindClosePtr = std::unique_ptr<void, decltype(FindCloseFunc)>;
11fdf7f2 63
7c673cae
FG
64void WinthreadCall(const char* label, std::error_code result) {
65 if (0 != result.value()) {
1e59de90
TL
66 fprintf(stderr, "Winthread %s: %s\n", label,
67 errnoStr(result.value()).c_str());
7c673cae
FG
68 abort();
69 }
70}
71
1e59de90 72} // namespace
7c673cae
FG
73
74namespace port {
1e59de90
TL
75WinClock::WinClock()
76 : perf_counter_frequency_(0),
494da23a 77 nano_seconds_per_period_(0),
7c673cae 78 GetSystemTimePreciseAsFileTime_(NULL) {
7c673cae
FG
79 {
80 LARGE_INTEGER qpf;
11fdf7f2
TL
81 BOOL ret __attribute__((__unused__));
82 ret = QueryPerformanceFrequency(&qpf);
7c673cae
FG
83 assert(ret == TRUE);
84 perf_counter_frequency_ = qpf.QuadPart;
494da23a
TL
85
86 if (std::nano::den % perf_counter_frequency_ == 0) {
87 nano_seconds_per_period_ = std::nano::den / perf_counter_frequency_;
88 }
7c673cae
FG
89 }
90
91 HMODULE module = GetModuleHandle("kernel32.dll");
92 if (module != NULL) {
1e59de90
TL
93 GetSystemTimePreciseAsFileTime_ = (FnGetSystemTimePreciseAsFileTime)(
94 void*)GetProcAddress(module, "GetSystemTimePreciseAsFileTime");
7c673cae
FG
95 }
96}
97
1e59de90
TL
98void WinClock::SleepForMicroseconds(int micros) {
99 std::this_thread::sleep_for(std::chrono::microseconds(micros));
7c673cae
FG
100}
101
1e59de90
TL
102std::string WinClock::TimeToString(uint64_t secondsSince1970) {
103 std::string result;
7c673cae 104
1e59de90
TL
105 const time_t seconds = secondsSince1970;
106 const int maxsize = 64;
494da23a 107
1e59de90
TL
108 struct tm t;
109 errno_t ret = localtime_s(&t, &seconds);
110
111 if (ret) {
112 result = std::to_string(seconds);
113 } else {
114 result.resize(maxsize);
115 char* p = &result[0];
116
117 int len =
118 snprintf(p, maxsize, "%04d/%02d/%02d-%02d:%02d:%02d ", t.tm_year + 1900,
119 t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
120 assert(len > 0);
121
122 result.resize(len);
7c673cae
FG
123 }
124
125 return result;
126}
127
1e59de90
TL
128uint64_t WinClock::NowMicros() {
129 if (GetSystemTimePreciseAsFileTime_ != NULL) {
130 // all std::chrono clocks on windows proved to return
131 // values that may repeat that is not good enough for some uses.
132 const int64_t c_UnixEpochStartTicks = 116444736000000000LL;
133 const int64_t c_FtToMicroSec = 10;
134
135 // This interface needs to return system time and not
136 // just any microseconds because it is often used as an argument
137 // to TimedWait() on condition variable
138 FILETIME ftSystemTime;
139 GetSystemTimePreciseAsFileTime_(&ftSystemTime);
140
141 LARGE_INTEGER li;
142 li.LowPart = ftSystemTime.dwLowDateTime;
143 li.HighPart = ftSystemTime.dwHighDateTime;
144 // Subtract unix epoch start
145 li.QuadPart -= c_UnixEpochStartTicks;
146 // Convert to microsecs
147 li.QuadPart /= c_FtToMicroSec;
148 return li.QuadPart;
11fdf7f2 149 }
1e59de90
TL
150 return std::chrono::duration_cast<std::chrono::microseconds>(
151 std::chrono::system_clock::now().time_since_epoch())
152 .count();
153}
154
155uint64_t WinClock::NowNanos() {
156 if (nano_seconds_per_period_ != 0) {
157 // all std::chrono clocks on windows have the same resolution that is only
158 // good enough for microseconds but not nanoseconds
159 // On Windows 8 and Windows 2012 Server
160 // GetSystemTimePreciseAsFileTime(&current_time) can be used
161 LARGE_INTEGER li;
162 QueryPerformanceCounter(&li);
163 // Convert performance counter to nanoseconds by precomputed ratio.
164 // Directly multiply nano::den with li.QuadPart causes overflow.
165 // Only do this when nano::den is divisible by perf_counter_frequency_,
166 // which most likely is the case in reality. If it's not, fall back to
167 // high_resolution_clock, which may be less precise under old compilers.
168 li.QuadPart *= nano_seconds_per_period_;
169 return li.QuadPart;
170 }
171 return std::chrono::duration_cast<std::chrono::nanoseconds>(
172 std::chrono::high_resolution_clock::now().time_since_epoch())
173 .count();
11fdf7f2
TL
174}
175
1e59de90 176Status WinClock::GetCurrentTime(int64_t* unix_time) {
7c673cae
FG
177 time_t time = std::time(nullptr);
178 if (time == (time_t)(-1)) {
179 return Status::NotSupported("Failed to get time");
180 }
181
182 *unix_time = time;
183 return Status::OK();
184}
185
1e59de90
TL
186WinFileSystem::WinFileSystem(const std::shared_ptr<SystemClock>& clock)
187 : clock_(clock), page_size_(4 * 1024), allocation_granularity_(page_size_) {
188 SYSTEM_INFO sinfo;
189 GetSystemInfo(&sinfo);
190
191 page_size_ = sinfo.dwPageSize;
192 allocation_granularity_ = sinfo.dwAllocationGranularity;
193}
194
195const std::shared_ptr<WinFileSystem>& WinFileSystem::Default() {
196 STATIC_AVOID_DESTRUCTION(std::shared_ptr<WinFileSystem>, fs)
197 (std::make_shared<WinFileSystem>(WinClock::Default()));
198 return fs;
199}
200
201WinEnvIO::WinEnvIO(Env* hosted_env) : hosted_env_(hosted_env) {}
202
203WinEnvIO::~WinEnvIO() {}
204
205IOStatus WinFileSystem::DeleteFile(const std::string& fname,
206 const IOOptions& /*options*/,
207 IODebugContext* /*dbg*/) {
208 IOStatus result;
209
210 BOOL ret = RX_DeleteFile(RX_FN(fname).c_str());
211
212 if (!ret) {
213 auto lastError = GetLastError();
214 result = IOErrorFromWindowsError("Failed to delete: " + fname, lastError);
215 }
216
217 return result;
218}
219
220IOStatus WinFileSystem::Truncate(const std::string& fname, size_t size,
221 const IOOptions& /*options*/,
222 IODebugContext* /*dbg*/) {
223 IOStatus s;
224 int result = ROCKSDB_NAMESPACE::port::Truncate(fname, size);
225 if (result != 0) {
226 s = IOError("Failed to truncate: " + fname, errno);
227 }
228 return s;
229}
230
231IOStatus WinFileSystem::NewSequentialFile(
232 const std::string& fname, const FileOptions& options,
233 std::unique_ptr<FSSequentialFile>* result, IODebugContext* /*dbg*/) {
234 IOStatus s;
7c673cae
FG
235
236 result->reset();
237
238 // Corruption test needs to rename and delete files of these kind
239 // while they are still open with another handle. For that reason we
240 // allow share_write and delete(allows rename).
241 HANDLE hFile = INVALID_HANDLE_VALUE;
242
243 DWORD fileFlags = FILE_ATTRIBUTE_READONLY;
244
245 if (options.use_direct_reads && !options.use_mmap_reads) {
246 fileFlags |= FILE_FLAG_NO_BUFFERING;
247 }
248
249 {
250 IOSTATS_TIMER_GUARD(open_nanos);
494da23a
TL
251 hFile = RX_CreateFile(
252 RX_FN(fname).c_str(), GENERIC_READ,
253 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
254 OPEN_EXISTING, // Original fopen mode is "rb"
255 fileFlags, NULL);
7c673cae
FG
256 }
257
258 if (INVALID_HANDLE_VALUE == hFile) {
259 auto lastError = GetLastError();
260 s = IOErrorFromWindowsError("Failed to open NewSequentialFile" + fname,
494da23a 261 lastError);
7c673cae
FG
262 } else {
263 result->reset(new WinSequentialFile(fname, hFile, options));
264 }
265 return s;
266}
267
1e59de90
TL
268IOStatus WinFileSystem::NewRandomAccessFile(
269 const std::string& fname, const FileOptions& options,
270 std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* dbg) {
7c673cae 271 result->reset();
1e59de90 272 IOStatus s;
7c673cae
FG
273
274 // Open the file for read-only random access
275 // Random access is to disable read-ahead as the system reads too much data
276 DWORD fileFlags = FILE_ATTRIBUTE_READONLY;
277
278 if (options.use_direct_reads && !options.use_mmap_reads) {
279 fileFlags |= FILE_FLAG_NO_BUFFERING;
280 } else {
281 fileFlags |= FILE_FLAG_RANDOM_ACCESS;
282 }
283
284 /// Shared access is necessary for corruption test to pass
285 // almost all tests would work with a possible exception of fault_injection
286 HANDLE hFile = 0;
287 {
288 IOSTATS_TIMER_GUARD(open_nanos);
1e59de90
TL
289 hFile =
290 RX_CreateFile(RX_FN(fname).c_str(), GENERIC_READ,
291 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
292 NULL, OPEN_EXISTING, fileFlags, NULL);
7c673cae
FG
293 }
294
295 if (INVALID_HANDLE_VALUE == hFile) {
296 auto lastError = GetLastError();
297 return IOErrorFromWindowsError(
494da23a 298 "NewRandomAccessFile failed to Create/Open: " + fname, lastError);
7c673cae
FG
299 }
300
301 UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc);
302
1e59de90
TL
303 // CAUTION! This will map the entire file into the process address space.
304 // Not recommended for 32-bit platforms.
305 if (options.use_mmap_reads) {
7c673cae
FG
306 uint64_t fileSize;
307
1e59de90 308 s = GetFileSize(fname, IOOptions(), &fileSize, dbg);
7c673cae
FG
309
310 if (s.ok()) {
311 // Will not map empty files
312 if (fileSize == 0) {
1e59de90
TL
313 return IOError("NewRandomAccessFile failed to map empty file: " + fname,
314 EINVAL);
7c673cae
FG
315 }
316
494da23a
TL
317 HANDLE hMap = RX_CreateFileMapping(hFile, NULL, PAGE_READONLY,
318 0, // At its present length
319 0,
320 NULL); // Mapping name
7c673cae
FG
321
322 if (!hMap) {
323 auto lastError = GetLastError();
324 return IOErrorFromWindowsError(
494da23a
TL
325 "Failed to create file mapping for NewRandomAccessFile: " + fname,
326 lastError);
7c673cae
FG
327 }
328
329 UniqueCloseHandlePtr mapGuard(hMap, CloseHandleFunc);
330
331 const void* mapped_region =
1e59de90
TL
332 MapViewOfFileEx(hMap, FILE_MAP_READ,
333 0, // High DWORD of access start
334 0, // Low DWORD
335 static_cast<SIZE_T>(fileSize),
336 NULL); // Let the OS choose the mapping
7c673cae
FG
337
338 if (!mapped_region) {
339 auto lastError = GetLastError();
340 return IOErrorFromWindowsError(
494da23a
TL
341 "Failed to MapViewOfFile for NewRandomAccessFile: " + fname,
342 lastError);
7c673cae
FG
343 }
344
345 result->reset(new WinMmapReadableFile(fname, hFile, hMap, mapped_region,
494da23a 346 static_cast<size_t>(fileSize)));
7c673cae
FG
347
348 mapGuard.release();
349 fileGuard.release();
350 }
351 } else {
1e59de90 352 result->reset(new WinRandomAccessFile(fname, hFile, page_size_, options));
7c673cae
FG
353 fileGuard.release();
354 }
355 return s;
356}
357
1e59de90
TL
358IOStatus WinFileSystem::OpenWritableFile(
359 const std::string& fname, const FileOptions& options,
360 std::unique_ptr<FSWritableFile>* result, bool reopen) {
7c673cae
FG
361 const size_t c_BufferCapacity = 64 * 1024;
362
363 EnvOptions local_options(options);
364
365 result->reset();
1e59de90 366 IOStatus s;
7c673cae
FG
367
368 DWORD fileFlags = FILE_ATTRIBUTE_NORMAL;
369
370 if (local_options.use_direct_writes && !local_options.use_mmap_writes) {
11fdf7f2 371 fileFlags = FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
7c673cae
FG
372 }
373
374 // Desired access. We are want to write only here but if we want to memory
375 // map
376 // the file then there is no write only mode so we have to create it
377 // Read/Write
378 // However, MapViewOfFile specifies only Write only
379 DWORD desired_access = GENERIC_WRITE;
380 DWORD shared_mode = FILE_SHARE_READ;
381
382 if (local_options.use_mmap_writes) {
383 desired_access |= GENERIC_READ;
384 } else {
385 // Adding this solely for tests to pass (fault_injection_test,
386 // wal_manager_test).
387 shared_mode |= (FILE_SHARE_WRITE | FILE_SHARE_DELETE);
388 }
389
11fdf7f2
TL
390 // This will always truncate the file
391 DWORD creation_disposition = CREATE_ALWAYS;
392 if (reopen) {
393 creation_disposition = OPEN_ALWAYS;
394 }
395
7c673cae
FG
396 HANDLE hFile = 0;
397 {
398 IOSTATS_TIMER_GUARD(open_nanos);
494da23a
TL
399 hFile = RX_CreateFile(
400 RX_FN(fname).c_str(),
401 desired_access, // Access desired
402 shared_mode,
1e59de90 403 NULL, // Security attributes
494da23a
TL
404 // Posix env says (reopen) ? (O_CREATE | O_APPEND) : O_CREAT | O_TRUNC
405 creation_disposition,
1e59de90
TL
406 fileFlags, // Flags
407 NULL); // Template File
7c673cae
FG
408 }
409
410 if (INVALID_HANDLE_VALUE == hFile) {
411 auto lastError = GetLastError();
412 return IOErrorFromWindowsError(
1e59de90 413 "Failed to create a NewWritableFile: " + fname, lastError);
7c673cae
FG
414 }
415
11fdf7f2
TL
416 // We will start writing at the end, appending
417 if (reopen) {
418 LARGE_INTEGER zero_move;
419 zero_move.QuadPart = 0;
420 BOOL ret = SetFilePointerEx(hFile, zero_move, NULL, FILE_END);
421 if (!ret) {
422 auto lastError = GetLastError();
423 return IOErrorFromWindowsError(
494da23a
TL
424 "Failed to create a ReopenWritableFile move to the end: " + fname,
425 lastError);
11fdf7f2
TL
426 }
427 }
428
7c673cae
FG
429 if (options.use_mmap_writes) {
430 // We usually do not use mmmapping on SSD and thus we pass memory
431 // page_size
432 result->reset(new WinMmapFile(fname, hFile, page_size_,
494da23a 433 allocation_granularity_, local_options));
7c673cae
FG
434 } else {
435 // Here we want the buffer allocation to be aligned by the SSD page size
436 // and to be a multiple of it
1e59de90 437 result->reset(new WinWritableFile(fname, hFile, GetPageSize(),
494da23a 438 c_BufferCapacity, local_options));
7c673cae
FG
439 }
440 return s;
441}
442
1e59de90
TL
443IOStatus WinFileSystem::NewWritableFile(const std::string& fname,
444 const FileOptions& options,
445 std::unique_ptr<FSWritableFile>* result,
446 IODebugContext* /*dbg*/) {
447 return OpenWritableFile(fname, options, result, false);
448}
7c673cae 449
1e59de90
TL
450IOStatus WinFileSystem::ReopenWritableFile(
451 const std::string& fname, const FileOptions& options,
452 std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) {
453 return OpenWritableFile(fname, options, result, true);
454}
455
456IOStatus WinFileSystem::NewRandomRWFile(const std::string& fname,
457 const FileOptions& options,
458 std::unique_ptr<FSRandomRWFile>* result,
459 IODebugContext* /*dbg*/) {
460 IOStatus s;
7c673cae
FG
461
462 // Open the file for read-only random access
463 // Random access is to disable read-ahead as the system reads too much data
464 DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
465 DWORD shared_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
1e59de90 466 DWORD creation_disposition = OPEN_EXISTING; // Fail if file does not exist
7c673cae
FG
467 DWORD file_flags = FILE_FLAG_RANDOM_ACCESS;
468
469 if (options.use_direct_reads && options.use_direct_writes) {
470 file_flags |= FILE_FLAG_NO_BUFFERING;
471 }
472
473 /// Shared access is necessary for corruption test to pass
474 // almost all tests would work with a possible exception of fault_injection
475 HANDLE hFile = 0;
476 {
477 IOSTATS_TIMER_GUARD(open_nanos);
1e59de90
TL
478 hFile = RX_CreateFile(RX_FN(fname).c_str(), desired_access, shared_mode,
479 NULL, // Security attributes
480 creation_disposition, file_flags, NULL);
7c673cae
FG
481 }
482
483 if (INVALID_HANDLE_VALUE == hFile) {
484 auto lastError = GetLastError();
485 return IOErrorFromWindowsError(
1e59de90 486 "NewRandomRWFile failed to Create/Open: " + fname, lastError);
7c673cae
FG
487 }
488
489 UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc);
1e59de90 490 result->reset(new WinRandomRWFile(fname, hFile, GetPageSize(), options));
11fdf7f2
TL
491 fileGuard.release();
492
493 return s;
494}
495
1e59de90
TL
496IOStatus WinFileSystem::NewMemoryMappedFileBuffer(
497 const std::string& fname, std::unique_ptr<MemoryMappedFileBuffer>* result) {
498 IOStatus s;
11fdf7f2
TL
499 result->reset();
500
501 DWORD fileFlags = FILE_ATTRIBUTE_READONLY;
502
503 HANDLE hFile = INVALID_HANDLE_VALUE;
504 {
505 IOSTATS_TIMER_GUARD(open_nanos);
494da23a
TL
506 hFile = RX_CreateFile(
507 RX_FN(fname).c_str(), GENERIC_READ | GENERIC_WRITE,
1e59de90 508 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
494da23a 509 OPEN_EXISTING, // Open only if it exists
1e59de90 510 fileFlags, NULL);
11fdf7f2
TL
511 }
512
513 if (INVALID_HANDLE_VALUE == hFile) {
514 auto lastError = GetLastError();
494da23a
TL
515 s = IOErrorFromWindowsError(
516 "Failed to open NewMemoryMappedFileBuffer: " + fname, lastError);
11fdf7f2
TL
517 return s;
518 }
519 UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc);
520
521 uint64_t fileSize = 0;
1e59de90 522 s = GetFileSize(fname, IOOptions(), &fileSize, nullptr);
11fdf7f2
TL
523 if (!s.ok()) {
524 return s;
525 }
526 // Will not map empty files
527 if (fileSize == 0) {
1e59de90 528 return IOStatus::NotSupported(
494da23a 529 "NewMemoryMappedFileBuffer can not map zero length files: " + fname);
11fdf7f2
TL
530 }
531
532 // size_t is 32-bit with 32-bit builds
533 if (fileSize > std::numeric_limits<size_t>::max()) {
1e59de90
TL
534 return IOStatus::NotSupported(
535 "The specified file size does not fit into 32-bit memory addressing: " +
536 fname);
11fdf7f2
TL
537 }
538
494da23a
TL
539 HANDLE hMap = RX_CreateFileMapping(hFile, NULL, PAGE_READWRITE,
540 0, // Whole file at its present length
541 0,
542 NULL); // Mapping name
11fdf7f2
TL
543
544 if (!hMap) {
545 auto lastError = GetLastError();
546 return IOErrorFromWindowsError(
494da23a 547 "Failed to create file mapping for: " + fname, lastError);
11fdf7f2
TL
548 }
549 UniqueCloseHandlePtr mapGuard(hMap, CloseHandleFunc);
550
551 void* base = MapViewOfFileEx(hMap, FILE_MAP_WRITE,
494da23a
TL
552 0, // High DWORD of access start
553 0, // Low DWORD
554 static_cast<SIZE_T>(fileSize),
555 NULL); // Let the OS choose the mapping
11fdf7f2
TL
556
557 if (!base) {
558 auto lastError = GetLastError();
559 return IOErrorFromWindowsError(
494da23a
TL
560 "Failed to MapViewOfFile for NewMemoryMappedFileBuffer: " + fname,
561 lastError);
11fdf7f2
TL
562 }
563
494da23a
TL
564 result->reset(new WinMemoryMappedBuffer(hFile, hMap, base,
565 static_cast<size_t>(fileSize)));
11fdf7f2
TL
566
567 mapGuard.release();
7c673cae
FG
568 fileGuard.release();
569
570 return s;
571}
572
1e59de90
TL
573IOStatus WinFileSystem::NewDirectory(const std::string& name,
574 const IOOptions& /*options*/,
575 std::unique_ptr<FSDirectory>* result,
576 IODebugContext* /*dbg*/) {
577 IOStatus s;
7c673cae
FG
578 // Must be nullptr on failure
579 result->reset();
11fdf7f2 580
7c673cae 581 if (!DirExists(name)) {
1e59de90 582 s = IOErrorFromWindowsError("open folder: " + name, ERROR_DIRECTORY);
11fdf7f2
TL
583 return s;
584 }
585
586 HANDLE handle = INVALID_HANDLE_VALUE;
587 // 0 - for access means read metadata
588 {
7c673cae 589 IOSTATS_TIMER_GUARD(open_nanos);
494da23a
TL
590 handle = RX_CreateFile(
591 RX_FN(name).c_str(), 0,
1e59de90 592 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
494da23a 593 OPEN_EXISTING,
1e59de90 594 FILE_FLAG_BACKUP_SEMANTICS, // make opening folders possible
494da23a 595 NULL);
11fdf7f2
TL
596 }
597
598 if (INVALID_HANDLE_VALUE == handle) {
599 auto lastError = GetLastError();
494da23a 600 s = IOErrorFromWindowsError("open folder: " + name, lastError);
11fdf7f2 601 return s;
7c673cae 602 }
11fdf7f2 603
1e59de90 604 result->reset(new WinDirectory(name, handle));
11fdf7f2 605
7c673cae
FG
606 return s;
607}
608
1e59de90
TL
609IOStatus WinFileSystem::FileExists(const std::string& fname,
610 const IOOptions& /*opts*/,
611 IODebugContext* /*dbg*/) {
612 IOStatus s;
11fdf7f2
TL
613 // TODO: This does not follow symbolic links at this point
614 // which is consistent with _access() impl on windows
615 // but can be added
616 WIN32_FILE_ATTRIBUTE_DATA attrs;
494da23a
TL
617 if (FALSE == RX_GetFileAttributesEx(RX_FN(fname).c_str(),
618 GetFileExInfoStandard, &attrs)) {
11fdf7f2
TL
619 auto lastError = GetLastError();
620 switch (lastError) {
1e59de90
TL
621 case ERROR_ACCESS_DENIED:
622 case ERROR_NOT_FOUND:
623 case ERROR_FILE_NOT_FOUND:
624 case ERROR_PATH_NOT_FOUND:
625 s = IOStatus::NotFound();
626 break;
627 default:
628 s = IOErrorFromWindowsError("Unexpected error for: " + fname,
629 lastError);
630 break;
11fdf7f2
TL
631 }
632 }
633 return s;
7c673cae
FG
634}
635
1e59de90
TL
636IOStatus WinFileSystem::GetChildren(const std::string& dir,
637 const IOOptions& /*opts*/,
638 std::vector<std::string>* result,
639 IODebugContext* /*dbg*/) {
640 IOStatus status;
7c673cae 641 result->clear();
7c673cae 642
494da23a
TL
643 RX_WIN32_FIND_DATA data;
644 memset(&data, 0, sizeof(data));
11fdf7f2
TL
645 std::string pattern(dir);
646 pattern.append("\\").append("*");
7c673cae 647
1e59de90
TL
648 HANDLE handle =
649 RX_FindFirstFileEx(RX_FN(pattern).c_str(),
650 // Do not want alternative name
651 FindExInfoBasic, &data, FindExSearchNameMatch,
652 NULL, // lpSearchFilter
653 0);
7c673cae 654
11fdf7f2
TL
655 if (handle == INVALID_HANDLE_VALUE) {
656 auto lastError = GetLastError();
657 switch (lastError) {
1e59de90
TL
658 case ERROR_NOT_FOUND:
659 case ERROR_ACCESS_DENIED:
660 case ERROR_FILE_NOT_FOUND:
661 case ERROR_PATH_NOT_FOUND:
662 status = IOStatus::NotFound();
663 break;
664 default:
665 status = IOErrorFromWindowsError("Failed to GetChhildren for: " + dir,
666 lastError);
7c673cae 667 }
11fdf7f2 668 return status;
7c673cae
FG
669 }
670
11fdf7f2
TL
671 UniqueFindClosePtr fc(handle, FindCloseFunc);
672
11fdf7f2
TL
673 // For safety
674 data.cFileName[MAX_PATH - 1] = 0;
675
676 while (true) {
1e59de90
TL
677 // filter out '.' and '..' directory entries
678 // which appear only on some platforms
679 const bool ignore =
680 ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
681 (RX_FNCMP(data.cFileName, ".") == 0 ||
682 RX_FNCMP(data.cFileName, "..") == 0);
683 if (!ignore) {
684 auto x = RX_FILESTRING(data.cFileName, RX_FNLEN(data.cFileName));
685 result->push_back(FN_TO_RX(x));
686 }
687
688 BOOL ret = -RX_FindNextFile(handle, &data);
11fdf7f2
TL
689 // If the function fails the return value is zero
690 // and non-zero otherwise. Not TRUE or FALSE.
691 if (ret == FALSE) {
692 // Posix does not care why we stopped
693 break;
694 }
695 data.cFileName[MAX_PATH - 1] = 0;
696 }
7c673cae
FG
697 return status;
698}
699
1e59de90
TL
700IOStatus WinFileSystem::CreateDir(const std::string& name,
701 const IOOptions& /*opts*/,
702 IODebugContext* /*dbg*/) {
703 IOStatus result;
494da23a 704 BOOL ret = RX_CreateDirectory(RX_FN(name).c_str(), NULL);
11fdf7f2
TL
705 if (!ret) {
706 auto lastError = GetLastError();
1e59de90
TL
707 result = IOErrorFromWindowsError("Failed to create a directory: " + name,
708 lastError);
7c673cae
FG
709 }
710
711 return result;
712}
713
1e59de90
TL
714IOStatus WinFileSystem::CreateDirIfMissing(const std::string& name,
715 const IOOptions& /*opts*/,
716 IODebugContext* /*dbg*/) {
717 IOStatus result;
7c673cae
FG
718
719 if (DirExists(name)) {
720 return result;
721 }
722
494da23a 723 BOOL ret = RX_CreateDirectory(RX_FN(name).c_str(), NULL);
11fdf7f2
TL
724 if (!ret) {
725 auto lastError = GetLastError();
726 if (lastError != ERROR_ALREADY_EXISTS) {
1e59de90
TL
727 result = IOErrorFromWindowsError("Failed to create a directory: " + name,
728 lastError);
7c673cae 729 } else {
1e59de90 730 result = IOStatus::IOError(name + ": exists but is not a directory");
7c673cae
FG
731 }
732 }
7c673cae
FG
733 return result;
734}
735
1e59de90
TL
736IOStatus WinFileSystem::DeleteDir(const std::string& name,
737 const IOOptions& /*options*/,
738 IODebugContext* /*dbg*/) {
739 IOStatus result;
494da23a 740 BOOL ret = RX_RemoveDirectory(RX_FN(name).c_str());
11fdf7f2
TL
741 if (!ret) {
742 auto lastError = GetLastError();
1e59de90
TL
743 result =
744 IOErrorFromWindowsError("Failed to remove dir: " + name, lastError);
7c673cae
FG
745 }
746 return result;
747}
748
1e59de90
TL
749IOStatus WinFileSystem::GetFileSize(const std::string& fname,
750 const IOOptions& /*opts*/, uint64_t* size,
751 IODebugContext* /*dbg*/) {
752 IOStatus s;
7c673cae
FG
753
754 WIN32_FILE_ATTRIBUTE_DATA attrs;
494da23a
TL
755 if (RX_GetFileAttributesEx(RX_FN(fname).c_str(), GetFileExInfoStandard,
756 &attrs)) {
7c673cae
FG
757 ULARGE_INTEGER file_size;
758 file_size.HighPart = attrs.nFileSizeHigh;
759 file_size.LowPart = attrs.nFileSizeLow;
760 *size = file_size.QuadPart;
761 } else {
762 auto lastError = GetLastError();
763 s = IOErrorFromWindowsError("Can not get size for: " + fname, lastError);
764 }
765 return s;
766}
767
1e59de90 768uint64_t WinFileSystem::FileTimeToUnixTime(const FILETIME& ftTime) {
7c673cae
FG
769 const uint64_t c_FileTimePerSecond = 10000000U;
770 // UNIX epoch starts on 1970-01-01T00:00:00Z
771 // Windows FILETIME starts on 1601-01-01T00:00:00Z
772 // Therefore, we need to subtract the below number of seconds from
773 // the seconds that we obtain from FILETIME with an obvious loss of
774 // precision
775 const uint64_t c_SecondBeforeUnixEpoch = 11644473600U;
776
777 ULARGE_INTEGER li;
778 li.HighPart = ftTime.dwHighDateTime;
779 li.LowPart = ftTime.dwLowDateTime;
780
781 uint64_t result =
1e59de90 782 (li.QuadPart / c_FileTimePerSecond) - c_SecondBeforeUnixEpoch;
7c673cae
FG
783 return result;
784}
785
1e59de90
TL
786IOStatus WinFileSystem::GetFileModificationTime(const std::string& fname,
787 const IOOptions& /*opts*/,
788 uint64_t* file_mtime,
789 IODebugContext* /*dbg*/) {
790 IOStatus s;
7c673cae
FG
791
792 WIN32_FILE_ATTRIBUTE_DATA attrs;
494da23a 793 if (RX_GetFileAttributesEx(RX_FN(fname).c_str(), GetFileExInfoStandard,
1e59de90 794 &attrs)) {
7c673cae
FG
795 *file_mtime = FileTimeToUnixTime(attrs.ftLastWriteTime);
796 } else {
797 auto lastError = GetLastError();
798 s = IOErrorFromWindowsError(
1e59de90 799 "Can not get file modification time for: " + fname, lastError);
7c673cae
FG
800 *file_mtime = 0;
801 }
802
803 return s;
804}
805
1e59de90
TL
806IOStatus WinFileSystem::RenameFile(const std::string& src,
807 const std::string& target,
808 const IOOptions& /*opts*/,
809 IODebugContext* /*dbg*/) {
810 IOStatus result;
7c673cae
FG
811
812 // rename() is not capable of replacing the existing file as on Linux
813 // so use OS API directly
494da23a
TL
814 if (!RX_MoveFileEx(RX_FN(src).c_str(), RX_FN(target).c_str(),
815 MOVEFILE_REPLACE_EXISTING)) {
7c673cae
FG
816 DWORD lastError = GetLastError();
817
818 std::string text("Failed to rename: ");
819 text.append(src).append(" to: ").append(target);
820
821 result = IOErrorFromWindowsError(text, lastError);
822 }
823
824 return result;
825}
826
1e59de90
TL
827IOStatus WinFileSystem::LinkFile(const std::string& src,
828 const std::string& target,
829 const IOOptions& /*opts*/,
830 IODebugContext* /*dbg*/) {
831 IOStatus result;
7c673cae 832
1e59de90 833 if (!RX_CreateHardLink(RX_FN(target).c_str(), RX_FN(src).c_str(), NULL)) {
7c673cae 834 DWORD lastError = GetLastError();
11fdf7f2 835 if (lastError == ERROR_NOT_SAME_DEVICE) {
1e59de90 836 return IOStatus::NotSupported("No cross FS links allowed");
11fdf7f2 837 }
7c673cae
FG
838
839 std::string text("Failed to link: ");
840 text.append(src).append(" to: ").append(target);
841
842 result = IOErrorFromWindowsError(text, lastError);
843 }
844
845 return result;
846}
847
1e59de90
TL
848IOStatus WinFileSystem::NumFileLinks(const std::string& fname,
849 const IOOptions& /*opts*/, uint64_t* count,
850 IODebugContext* /*dbg*/) {
851 IOStatus s;
852 HANDLE handle =
853 RX_CreateFile(RX_FN(fname).c_str(), 0,
854 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
855 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
11fdf7f2
TL
856
857 if (INVALID_HANDLE_VALUE == handle) {
858 auto lastError = GetLastError();
859 s = IOErrorFromWindowsError("NumFileLinks: " + fname, lastError);
860 return s;
861 }
862 UniqueCloseHandlePtr handle_guard(handle, CloseHandleFunc);
863 FILE_STANDARD_INFO standard_info;
864 if (0 != GetFileInformationByHandleEx(handle, FileStandardInfo,
865 &standard_info,
866 sizeof(standard_info))) {
867 *count = standard_info.NumberOfLinks;
868 } else {
869 auto lastError = GetLastError();
870 s = IOErrorFromWindowsError("GetFileInformationByHandleEx: " + fname,
871 lastError);
872 }
873 return s;
874}
875
1e59de90
TL
876IOStatus WinFileSystem::AreFilesSame(const std::string& first,
877 const std::string& second,
878 const IOOptions& /*opts*/, bool* res,
879 IODebugContext* /*dbg*/) {
11fdf7f2
TL
880// For MinGW builds
881#if (_WIN32_WINNT == _WIN32_WINNT_VISTA)
1e59de90 882 IOStatus s = IOStatus::NotSupported();
11fdf7f2
TL
883#else
884 assert(res != nullptr);
1e59de90 885 IOStatus s;
11fdf7f2 886 if (res == nullptr) {
1e59de90 887 s = IOStatus::InvalidArgument("res");
11fdf7f2
TL
888 return s;
889 }
890
891 // 0 - for access means read metadata
494da23a
TL
892 HANDLE file_1 = RX_CreateFile(
893 RX_FN(first).c_str(), 0,
1e59de90 894 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
494da23a 895 OPEN_EXISTING,
1e59de90 896 FILE_FLAG_BACKUP_SEMANTICS, // make opening folders possible
494da23a 897 NULL);
11fdf7f2
TL
898
899 if (INVALID_HANDLE_VALUE == file_1) {
900 auto lastError = GetLastError();
494da23a 901 s = IOErrorFromWindowsError("open file: " + first, lastError);
11fdf7f2
TL
902 return s;
903 }
904 UniqueCloseHandlePtr g_1(file_1, CloseHandleFunc);
905
494da23a
TL
906 HANDLE file_2 = RX_CreateFile(
907 RX_FN(second).c_str(), 0,
1e59de90
TL
908 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
909 OPEN_EXISTING,
910 FILE_FLAG_BACKUP_SEMANTICS, // make opening folders possible
494da23a 911 NULL);
11fdf7f2
TL
912
913 if (INVALID_HANDLE_VALUE == file_2) {
914 auto lastError = GetLastError();
494da23a 915 s = IOErrorFromWindowsError("open file: " + second, lastError);
11fdf7f2
TL
916 return s;
917 }
918 UniqueCloseHandlePtr g_2(file_2, CloseHandleFunc);
919
920 FILE_ID_INFO FileInfo_1;
921 BOOL result = GetFileInformationByHandleEx(file_1, FileIdInfo, &FileInfo_1,
494da23a 922 sizeof(FileInfo_1));
11fdf7f2
TL
923
924 if (!result) {
925 auto lastError = GetLastError();
494da23a 926 s = IOErrorFromWindowsError("stat file: " + first, lastError);
11fdf7f2
TL
927 return s;
928 }
929
1e59de90
TL
930 FILE_ID_INFO FileInfo_2;
931 result = GetFileInformationByHandleEx(file_2, FileIdInfo, &FileInfo_2,
932 sizeof(FileInfo_2));
11fdf7f2
TL
933
934 if (!result) {
935 auto lastError = GetLastError();
494da23a 936 s = IOErrorFromWindowsError("stat file: " + second, lastError);
11fdf7f2
TL
937 return s;
938 }
939
940 if (FileInfo_1.VolumeSerialNumber == FileInfo_2.VolumeSerialNumber) {
1e59de90
TL
941 *res =
942 (0 == memcmp(FileInfo_1.FileId.Identifier, FileInfo_2.FileId.Identifier,
943 sizeof(FileInfo_1.FileId.Identifier)));
11fdf7f2
TL
944 } else {
945 *res = false;
946 }
947#endif
948 return s;
949}
950
1e59de90
TL
951IOStatus WinFileSystem::LockFile(const std::string& lockFname,
952 const IOOptions& /*opts*/, FileLock** lock,
953 IODebugContext* /*dbg*/) {
7c673cae
FG
954 assert(lock != nullptr);
955
956 *lock = NULL;
1e59de90 957 IOStatus result;
7c673cae
FG
958
959 // No-sharing, this is a LOCK file
960 const DWORD ExclusiveAccessON = 0;
961
962 // Obtain exclusive access to the LOCK file
963 // Previously, instead of NORMAL attr we set DELETE on close and that worked
964 // well except with fault_injection test that insists on deleting it.
965 HANDLE hFile = 0;
966 {
967 IOSTATS_TIMER_GUARD(open_nanos);
494da23a 968 hFile = RX_CreateFile(RX_FN(lockFname).c_str(),
1e59de90
TL
969 (GENERIC_READ | GENERIC_WRITE), ExclusiveAccessON,
970 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7c673cae
FG
971 }
972
973 if (INVALID_HANDLE_VALUE == hFile) {
974 auto lastError = GetLastError();
1e59de90
TL
975 result = IOErrorFromWindowsError("Failed to create lock file: " + lockFname,
976 lastError);
7c673cae
FG
977 } else {
978 *lock = new WinFileLock(hFile);
979 }
980
981 return result;
982}
983
1e59de90
TL
984IOStatus WinFileSystem::UnlockFile(FileLock* lock, const IOOptions& /*opts*/,
985 IODebugContext* /*dbg*/) {
986 IOStatus result;
7c673cae
FG
987
988 assert(lock != nullptr);
989
990 delete lock;
991
992 return result;
993}
994
1e59de90
TL
995IOStatus WinFileSystem::GetTestDirectory(const IOOptions& opts,
996 std::string* result,
997 IODebugContext* dbg) {
7c673cae
FG
998 std::string output;
999
1000 const char* env = getenv("TEST_TMPDIR");
1001 if (env && env[0] != '\0') {
1002 output = env;
7c673cae
FG
1003 } else {
1004 env = getenv("TMP");
1005
1006 if (env && env[0] != '\0') {
1007 output = env;
1008 } else {
1009 output = "c:\\tmp";
1010 }
7c673cae 1011 }
1e59de90 1012 CreateDir(output, opts, dbg);
7c673cae
FG
1013
1014 output.append("\\testrocksdb-");
20effc67 1015 output.append(std::to_string(GetCurrentProcessId()));
7c673cae 1016
1e59de90 1017 CreateDir(output, opts, dbg);
7c673cae
FG
1018
1019 output.swap(*result);
1020
1e59de90 1021 return IOStatus::OK();
7c673cae
FG
1022}
1023
1e59de90
TL
1024IOStatus WinFileSystem::NewLogger(const std::string& fname,
1025 const IOOptions& /*opts*/,
1026 std::shared_ptr<Logger>* result,
1027 IODebugContext* /*dbg*/) {
1028 IOStatus s;
7c673cae
FG
1029
1030 result->reset();
1031
1032 HANDLE hFile = 0;
1033 {
1034 IOSTATS_TIMER_GUARD(open_nanos);
494da23a
TL
1035 hFile = RX_CreateFile(
1036 RX_FN(fname).c_str(), GENERIC_WRITE,
1037 FILE_SHARE_READ | FILE_SHARE_DELETE, // In RocksDb log files are
1038 // renamed and deleted before
1039 // they are closed. This enables
1040 // doing so.
1041 NULL,
1042 CREATE_ALWAYS, // Original fopen mode is "w"
1043 FILE_ATTRIBUTE_NORMAL, NULL);
7c673cae
FG
1044 }
1045
1046 if (INVALID_HANDLE_VALUE == hFile) {
1047 auto lastError = GetLastError();
1048 s = IOErrorFromWindowsError("Failed to open LogFile" + fname, lastError);
1049 } else {
1050 {
1051 // With log files we want to set the true creation time as of now
1052 // because the system
1053 // for some reason caches the attributes of the previous file that just
1054 // been renamed from
1055 // this name so auto_roll_logger_test fails
1056 FILETIME ft;
1057 GetSystemTimeAsFileTime(&ft);
1058 // Set creation, last access and last write time to the same value
1059 SetFileTime(hFile, &ft, &ft, &ft);
1060 }
1e59de90 1061 result->reset(new WinLogger(&WinEnvThreads::gettid, clock_.get(), hFile));
7c673cae
FG
1062 }
1063 return s;
1064}
1065
1e59de90
TL
1066IOStatus WinFileSystem::IsDirectory(const std::string& path,
1067 const IOOptions& /*opts*/, bool* is_dir,
1068 IODebugContext* /*dbg*/) {
20effc67
TL
1069 BOOL ret = RX_PathIsDirectory(RX_FN(path).c_str());
1070 if (is_dir) {
1071 *is_dir = ret ? true : false;
1072 }
1e59de90 1073 return IOStatus::OK();
7c673cae
FG
1074}
1075
1076Status WinEnvIO::GetHostName(char* name, uint64_t len) {
1077 Status s;
1078 DWORD nSize = static_cast<DWORD>(
1e59de90 1079 std::min<uint64_t>(len, std::numeric_limits<DWORD>::max()));
7c673cae
FG
1080
1081 if (!::GetComputerNameA(name, &nSize)) {
1082 auto lastError = GetLastError();
1083 s = IOErrorFromWindowsError("GetHostName", lastError);
1084 } else {
1085 name[nSize] = 0;
1086 }
1087
1088 return s;
1089}
1090
1e59de90
TL
1091IOStatus WinFileSystem::GetAbsolutePath(const std::string& db_path,
1092 const IOOptions& /*options*/,
1093 std::string* output_path,
1094 IODebugContext* dbg) {
7c673cae 1095 // Check if we already have an absolute path
11fdf7f2
TL
1096 // For test compatibility we will consider starting slash as an
1097 // absolute path
1098 if ((!db_path.empty() && (db_path[0] == '\\' || db_path[0] == '/')) ||
1e59de90 1099 !RX_PathIsRelative(RX_FN(db_path).c_str())) {
7c673cae 1100 *output_path = db_path;
1e59de90 1101 return IOStatus::OK();
7c673cae
FG
1102 }
1103
494da23a 1104 RX_FILESTRING result;
11fdf7f2 1105 result.resize(MAX_PATH);
7c673cae 1106
11fdf7f2
TL
1107 // Hopefully no changes the current directory while we do this
1108 // however _getcwd also suffers from the same limitation
494da23a 1109 DWORD len = RX_GetCurrentDirectory(MAX_PATH, &result[0]);
11fdf7f2
TL
1110 if (len == 0) {
1111 auto lastError = GetLastError();
1112 return IOErrorFromWindowsError("Failed to get current working directory",
494da23a 1113 lastError);
7c673cae
FG
1114 }
1115
11fdf7f2 1116 result.resize(len);
494da23a 1117 std::string res = FN_TO_RX(result);
7c673cae 1118
494da23a 1119 res.swap(*output_path);
1e59de90 1120 return IOStatus::OK();
7c673cae
FG
1121}
1122
1e59de90
TL
1123IOStatus WinFileSystem::GetFreeSpace(const std::string& path,
1124 const IOOptions& /*options*/,
1125 uint64_t* diskfree,
1126 IODebugContext* /*dbg*/) {
f67539c2
TL
1127 assert(diskfree != nullptr);
1128 ULARGE_INTEGER freeBytes;
1129 BOOL f = RX_GetDiskFreeSpaceEx(RX_FN(path).c_str(), &freeBytes, NULL, NULL);
1130 if (f) {
1131 *diskfree = freeBytes.QuadPart;
1e59de90 1132 return IOStatus::OK();
f67539c2
TL
1133 } else {
1134 DWORD lastError = GetLastError();
1135 return IOErrorFromWindowsError("Failed to get free space: " + path,
1136 lastError);
1137 }
1138}
1139
1e59de90
TL
1140FileOptions WinFileSystem::OptimizeForLogWrite(
1141 const FileOptions& file_options, const DBOptions& db_options) const {
1142 FileOptions optimized(file_options);
11fdf7f2 1143 // These two the same as default optimizations
7c673cae 1144 optimized.bytes_per_sync = db_options.wal_bytes_per_sync;
11fdf7f2
TL
1145 optimized.writable_file_max_buffer_size =
1146 db_options.writable_file_max_buffer_size;
1147
1148 // This adversely affects %999 on windows
7c673cae 1149 optimized.use_mmap_writes = false;
11fdf7f2
TL
1150 // Direct writes will produce a huge perf impact on
1151 // Windows. Pre-allocate space for WAL.
7c673cae 1152 optimized.use_direct_writes = false;
7c673cae
FG
1153 return optimized;
1154}
1155
1e59de90
TL
1156FileOptions WinFileSystem::OptimizeForManifestWrite(
1157 const FileOptions& options) const {
1158 FileOptions optimized(options);
7c673cae 1159 optimized.use_mmap_writes = false;
11fdf7f2
TL
1160 optimized.use_direct_reads = false;
1161 return optimized;
1162}
1163
1e59de90
TL
1164FileOptions WinFileSystem::OptimizeForManifestRead(
1165 const FileOptions& file_options) const {
1166 FileOptions optimized(file_options);
11fdf7f2
TL
1167 optimized.use_mmap_writes = false;
1168 optimized.use_direct_reads = false;
7c673cae
FG
1169 return optimized;
1170}
1171
1172// Returns true iff the named directory exists and is a directory.
1e59de90 1173bool WinFileSystem::DirExists(const std::string& dname) {
7c673cae 1174 WIN32_FILE_ATTRIBUTE_DATA attrs;
1e59de90
TL
1175 if (RX_GetFileAttributesEx(RX_FN(dname).c_str(), GetFileExInfoStandard,
1176 &attrs)) {
7c673cae
FG
1177 return 0 != (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
1178 }
1179 return false;
1180}
1181
1e59de90 1182size_t WinFileSystem::GetSectorSize(const std::string& fname) {
11fdf7f2
TL
1183 size_t sector_size = kSectorSize;
1184
11fdf7f2
TL
1185 // obtain device handle
1186 char devicename[7] = "\\\\.\\";
1e59de90
TL
1187 int erresult = 0;
1188 if (RX_PathIsRelative(RX_FN(fname).c_str())) {
1189 RX_FILESTRING rx_current_dir;
1190 rx_current_dir.resize(MAX_PATH);
1191 DWORD len = RX_GetCurrentDirectory(MAX_PATH, &rx_current_dir[0]);
1192 if (len == 0) {
1193 return sector_size;
1194 }
1195 rx_current_dir.resize(len);
1196 std::string current_dir = FN_TO_RX(rx_current_dir);
1197 erresult =
1198 strncat_s(devicename, sizeof(devicename), current_dir.c_str(), 2);
1199 } else {
1200 erresult = strncat_s(devicename, sizeof(devicename), fname.c_str(), 2);
1201 }
11fdf7f2
TL
1202
1203 if (erresult) {
1204 assert(false);
1205 return sector_size;
1206 }
1207
494da23a
TL
1208 HANDLE hDevice = CreateFile(devicename, 0, 0, nullptr, OPEN_EXISTING,
1209 FILE_ATTRIBUTE_NORMAL, nullptr);
11fdf7f2
TL
1210
1211 if (hDevice == INVALID_HANDLE_VALUE) {
1212 return sector_size;
1213 }
1214
1215 STORAGE_PROPERTY_QUERY spropertyquery;
1216 spropertyquery.PropertyId = StorageAccessAlignmentProperty;
1217 spropertyquery.QueryType = PropertyStandardQuery;
1218
1219 BYTE output_buffer[sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)];
1220 DWORD output_bytes = 0;
1221
1e59de90
TL
1222 BOOL ret = DeviceIoControl(
1223 hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &spropertyquery,
1224 sizeof(spropertyquery), output_buffer,
1225 sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR), &output_bytes, nullptr);
11fdf7f2
TL
1226
1227 if (ret) {
1e59de90
TL
1228 sector_size = ((STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR*)output_buffer)
1229 ->BytesPerLogicalSector;
11fdf7f2 1230 } else {
1e59de90
TL
1231 // many devices do not support StorageProcessAlignmentProperty. Any failure
1232 // here and we fall back to logical alignment
11fdf7f2 1233
1e59de90
TL
1234 DISK_GEOMETRY_EX geometry = {0};
1235 ret = DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, nullptr, 0,
1236 &geometry, sizeof(geometry), &output_bytes, nullptr);
11fdf7f2
TL
1237 if (ret) {
1238 sector_size = geometry.Geometry.BytesPerSector;
1239 }
1240 }
1241
1242 if (hDevice != INVALID_HANDLE_VALUE) {
1243 CloseHandle(hDevice);
1244 }
1245
1246 return sector_size;
1247}
1248
7c673cae
FG
1249////////////////////////////////////////////////////////////////////////
1250// WinEnvThreads
1251
494da23a
TL
1252WinEnvThreads::WinEnvThreads(Env* hosted_env)
1253 : hosted_env_(hosted_env), thread_pools_(Env::Priority::TOTAL) {
7c673cae
FG
1254 for (int pool_id = 0; pool_id < Env::Priority::TOTAL; ++pool_id) {
1255 thread_pools_[pool_id].SetThreadPriority(
1e59de90 1256 static_cast<Env::Priority>(pool_id));
7c673cae
FG
1257 // This allows later initializing the thread-local-env of each thread.
1258 thread_pools_[pool_id].SetHostEnv(hosted_env);
1259 }
1260}
1261
1262WinEnvThreads::~WinEnvThreads() {
7c673cae
FG
1263 WaitForJoin();
1264
1265 for (auto& thpool : thread_pools_) {
1266 thpool.JoinAllThreads();
1267 }
1268}
1269
1e59de90 1270void WinEnvThreads::Schedule(void (*function)(void*), void* arg,
494da23a 1271 Env::Priority pri, void* tag,
1e59de90 1272 void (*unschedFunction)(void* arg)) {
11fdf7f2 1273 assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
7c673cae
FG
1274 thread_pools_[pri].Schedule(function, arg, tag, unschedFunction);
1275}
1276
1277int WinEnvThreads::UnSchedule(void* arg, Env::Priority pri) {
1278 return thread_pools_[pri].UnSchedule(arg);
1279}
1280
1281namespace {
1282
1e59de90
TL
1283struct StartThreadState {
1284 void (*user_function)(void*);
1285 void* arg;
1286};
7c673cae 1287
1e59de90
TL
1288void* StartThreadWrapper(void* arg) {
1289 std::unique_ptr<StartThreadState> state(
7c673cae 1290 reinterpret_cast<StartThreadState*>(arg));
1e59de90
TL
1291 state->user_function(state->arg);
1292 return nullptr;
7c673cae
FG
1293}
1294
1e59de90
TL
1295} // namespace
1296
1297void WinEnvThreads::StartThread(void (*function)(void* arg), void* arg) {
7c673cae
FG
1298 std::unique_ptr<StartThreadState> state(new StartThreadState);
1299 state->user_function = function;
1300 state->arg = arg;
1301 try {
1e59de90 1302 Thread th(&StartThreadWrapper, state.get());
7c673cae
FG
1303 state.release();
1304
1305 std::lock_guard<std::mutex> lg(mu_);
1306 threads_to_join_.push_back(std::move(th));
1307
1308 } catch (const std::system_error& ex) {
1309 WinthreadCall("start thread", ex.code());
1310 }
1311}
1312
1313void WinEnvThreads::WaitForJoin() {
1314 for (auto& th : threads_to_join_) {
1315 th.join();
1316 }
1317 threads_to_join_.clear();
1318}
1319
1320unsigned int WinEnvThreads::GetThreadPoolQueueLen(Env::Priority pri) const {
11fdf7f2 1321 assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
7c673cae
FG
1322 return thread_pools_[pri].GetQueueLen();
1323}
1324
1e59de90
TL
1325int WinEnvThreads::ReserveThreads(int threads_to_reserved, Env::Priority pri) {
1326 assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1327 return thread_pools_[pri].ReserveThreads(threads_to_reserved);
1328}
1329
1330int WinEnvThreads::ReleaseThreads(int threads_to_released, Env::Priority pri) {
1331 assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1332 return thread_pools_[pri].ReleaseThreads(threads_to_released);
1333}
1334
7c673cae
FG
1335uint64_t WinEnvThreads::gettid() {
1336 uint64_t thread_id = GetCurrentThreadId();
1337 return thread_id;
1338}
1339
1340uint64_t WinEnvThreads::GetThreadID() const { return gettid(); }
1341
7c673cae 1342void WinEnvThreads::SetBackgroundThreads(int num, Env::Priority pri) {
11fdf7f2 1343 assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
7c673cae
FG
1344 thread_pools_[pri].SetBackgroundThreads(num);
1345}
1346
11fdf7f2
TL
1347int WinEnvThreads::GetBackgroundThreads(Env::Priority pri) {
1348 assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
1349 return thread_pools_[pri].GetBackgroundThreads();
1350}
1351
7c673cae 1352void WinEnvThreads::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) {
11fdf7f2 1353 assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH);
7c673cae
FG
1354 thread_pools_[pri].IncBackgroundThreadsIfNeeded(num);
1355}
1356
1357/////////////////////////////////////////////////////////////////////////
1358// WinEnv
1359
1e59de90
TL
1360WinEnv::WinEnv()
1361 : CompositeEnv(WinFileSystem::Default(), WinClock::Default()),
1362 winenv_io_(this),
1363 winenv_threads_(this) {
7c673cae
FG
1364 // Protected member of the base class
1365 thread_status_updater_ = CreateThreadStatusUpdater();
1366}
1367
7c673cae
FG
1368WinEnv::~WinEnv() {
1369 // All threads must be joined before the deletion of
1370 // thread_status_updater_.
1371 delete thread_status_updater_;
1372}
1373
494da23a 1374Status WinEnv::GetThreadList(std::vector<ThreadStatus>* thread_list) {
7c673cae
FG
1375 assert(thread_status_updater_);
1376 return thread_status_updater_->GetThreadList(thread_list);
1377}
1378
7c673cae
FG
1379Status WinEnv::GetHostName(char* name, uint64_t len) {
1380 return winenv_io_.GetHostName(name, len);
1381}
1382
1e59de90
TL
1383void WinEnv::Schedule(void (*function)(void*), void* arg, Env::Priority pri,
1384 void* tag, void (*unschedFunction)(void* arg)) {
7c673cae
FG
1385 return winenv_threads_.Schedule(function, arg, pri, tag, unschedFunction);
1386}
1387
1388int WinEnv::UnSchedule(void* arg, Env::Priority pri) {
1389 return winenv_threads_.UnSchedule(arg, pri);
1390}
1391
1e59de90 1392void WinEnv::StartThread(void (*function)(void* arg), void* arg) {
7c673cae
FG
1393 return winenv_threads_.StartThread(function, arg);
1394}
1395
1e59de90 1396void WinEnv::WaitForJoin() { return winenv_threads_.WaitForJoin(); }
7c673cae 1397
1e59de90 1398unsigned int WinEnv::GetThreadPoolQueueLen(Env::Priority pri) const {
7c673cae
FG
1399 return winenv_threads_.GetThreadPoolQueueLen(pri);
1400}
1e59de90
TL
1401int WinEnv::ReserveThreads(int threads_to_reserved, Env::Priority pri) {
1402 return winenv_threads_.ReserveThreads(threads_to_reserved, pri);
7c673cae
FG
1403}
1404
1e59de90
TL
1405int WinEnv::ReleaseThreads(int threads_to_released, Env::Priority pri) {
1406 return winenv_threads_.ReleaseThreads(threads_to_released, pri);
f67539c2
TL
1407}
1408
1e59de90 1409uint64_t WinEnv::GetThreadID() const { return winenv_threads_.GetThreadID(); }
7c673cae
FG
1410
1411// Allow increasing the number of worker threads.
1e59de90 1412void WinEnv::SetBackgroundThreads(int num, Env::Priority pri) {
7c673cae
FG
1413 return winenv_threads_.SetBackgroundThreads(num, pri);
1414}
1415
11fdf7f2
TL
1416int WinEnv::GetBackgroundThreads(Env::Priority pri) {
1417 return winenv_threads_.GetBackgroundThreads(pri);
1418}
1419
1e59de90 1420void WinEnv::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) {
7c673cae
FG
1421 return winenv_threads_.IncBackgroundThreadsIfNeeded(num, pri);
1422}
1423
7c673cae
FG
1424} // namespace port
1425
1e59de90
TL
1426std::shared_ptr<FileSystem> FileSystem::Default() {
1427 return port::WinFileSystem::Default();
7c673cae
FG
1428}
1429
1e59de90
TL
1430const std::shared_ptr<SystemClock>& SystemClock::Default() {
1431 STATIC_AVOID_DESTRUCTION(std::shared_ptr<SystemClock>, clock)
1432 (std::make_shared<port::WinClock>());
1433 return clock;
1434}
f67539c2 1435} // namespace ROCKSDB_NAMESPACE
20effc67
TL
1436
1437#endif