| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 | //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.//  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).//// 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.#pragma once#include <stdint.h>#include <mutex>#include <string>#include "rocksdb/status.h"#include "rocksdb/env.h"#include "util/aligned_buffer.h"#include <windows.h>namespace ROCKSDB_NAMESPACE {namespace port {std::string GetWindowsErrSz(DWORD err);inline Status IOErrorFromWindowsError(const std::string& context, DWORD err) {  return ((err == ERROR_HANDLE_DISK_FULL) || (err == ERROR_DISK_FULL))             ? Status::NoSpace(context, GetWindowsErrSz(err))             : ((err == ERROR_FILE_NOT_FOUND) || (err == ERROR_PATH_NOT_FOUND))                   ? Status::PathNotFound(context, GetWindowsErrSz(err))                   : Status::IOError(context, GetWindowsErrSz(err));}inline Status IOErrorFromLastWindowsError(const std::string& context) {  return IOErrorFromWindowsError(context, GetLastError());}inline Status IOError(const std::string& context, int err_number) {  return (err_number == ENOSPC)             ? Status::NoSpace(context, strerror(err_number))             : (err_number == ENOENT)                   ? Status::PathNotFound(context, strerror(err_number))                   : Status::IOError(context, strerror(err_number));}class WinFileData;Status pwrite(const WinFileData* file_data, const Slice& data,  uint64_t offset, size_t& bytes_written);Status pread(const WinFileData* file_data, char* src, size_t num_bytes,  uint64_t offset, size_t& bytes_read);Status fallocate(const std::string& filename, HANDLE hFile, uint64_t to_size);Status ftruncate(const std::string& filename, HANDLE hFile, uint64_t toSize);size_t GetUniqueIdFromFile(HANDLE hFile, char* id, size_t max_size);class WinFileData { protected:  const std::string filename_;  HANDLE hFile_;  // If true, the I/O issued would be direct I/O which the buffer  // will need to be aligned (not sure there is a guarantee that the buffer  // passed in is aligned).  const bool use_direct_io_; public:  // We want this class be usable both for inheritance (prive  // or protected) and for containment so __ctor and __dtor public  WinFileData(const std::string& filename, HANDLE hFile, bool direct_io)      : filename_(filename), hFile_(hFile), use_direct_io_(direct_io) {}  virtual ~WinFileData() { this->CloseFile(); }  bool CloseFile() {    bool result = true;    if (hFile_ != NULL && hFile_ != INVALID_HANDLE_VALUE) {      result = ::CloseHandle(hFile_);      assert(result);      hFile_ = NULL;    }    return result;  }  const std::string& GetName() const { return filename_; }  HANDLE GetFileHandle() const { return hFile_; }  bool use_direct_io() const { return use_direct_io_; }  WinFileData(const WinFileData&) = delete;  WinFileData& operator=(const WinFileData&) = delete;};class WinSequentialFile : protected WinFileData, public SequentialFile {  // Override for behavior change when creating a custom env  virtual Status PositionedReadInternal(char* src, size_t numBytes,    uint64_t offset, size_t& bytes_read) const;public:  WinSequentialFile(const std::string& fname, HANDLE f,    const EnvOptions& options);  ~WinSequentialFile();  WinSequentialFile(const WinSequentialFile&) = delete;  WinSequentialFile& operator=(const WinSequentialFile&) = delete;  virtual Status Read(size_t n, Slice* result, char* scratch) override;  virtual Status PositionedRead(uint64_t offset, size_t n, Slice* result,    char* scratch) override;  virtual Status Skip(uint64_t n) override;  virtual Status InvalidateCache(size_t offset, size_t length) override;  virtual bool use_direct_io() const override { return WinFileData::use_direct_io(); }};// mmap() based random-accessclass WinMmapReadableFile : private WinFileData, public RandomAccessFile {  HANDLE hMap_;  const void* mapped_region_;  const size_t length_; public:  // mapped_region_[0,length-1] contains the mmapped contents of the file.  WinMmapReadableFile(const std::string& fileName, HANDLE hFile, HANDLE hMap,                      const void* mapped_region, size_t length);  ~WinMmapReadableFile();  WinMmapReadableFile(const WinMmapReadableFile&) = delete;  WinMmapReadableFile& operator=(const WinMmapReadableFile&) = delete;  virtual Status Read(uint64_t offset, size_t n, Slice* result,                      char* scratch) const override;  virtual Status InvalidateCache(size_t offset, size_t length) override;  virtual size_t GetUniqueId(char* id, size_t max_size) const override;};// We preallocate and use memcpy to append new// data to the file.  This is safe since we either properly close the// file before reading from it, or for log files, the reading code// knows enough to skip zero suffixes.class WinMmapFile : private WinFileData, public WritableFile { private:  HANDLE hMap_;  const size_t page_size_;  // We flush the mapping view in page_size  // increments. We may decide if this is a memory  // page size or SSD page size  const size_t      allocation_granularity_;  // View must start at such a granularity  size_t reserved_size_;  // Preallocated size  size_t mapping_size_;  // The max size of the mapping object  // we want to guess the final file size to minimize the remapping  size_t view_size_;  // How much memory to map into a view at a time  char* mapped_begin_;  // Must begin at the file offset that is aligned with  // allocation_granularity_  char* mapped_end_;  char* dst_;  // Where to write next  (in range [mapped_begin_,mapped_end_])  char* last_sync_;  // Where have we synced up to  uint64_t file_offset_;  // Offset of mapped_begin_ in file  // Do we have unsynced writes?  bool pending_sync_;  // Can only truncate or reserve to a sector size aligned if  // used on files that are opened with Unbuffered I/O  Status TruncateFile(uint64_t toSize);  Status UnmapCurrentRegion();  Status MapNewRegion();  virtual Status PreallocateInternal(uint64_t spaceToReserve); public:  WinMmapFile(const std::string& fname, HANDLE hFile, size_t page_size,              size_t allocation_granularity, const EnvOptions& options);  ~WinMmapFile();  WinMmapFile(const WinMmapFile&) = delete;  WinMmapFile& operator=(const WinMmapFile&) = delete;  virtual Status Append(const Slice& data) override;  // Means Close() will properly take care of truncate  // and it does not need any additional information  virtual Status Truncate(uint64_t size) override;  virtual Status Close() override;  virtual Status Flush() override;  // Flush only data  virtual Status Sync() override;  /**  * Flush data as well as metadata to stable storage.  */  virtual Status Fsync() override;  /**  * Get the size of valid data in the file. This will not match the  * size that is returned from the filesystem because we use mmap  * to extend file by map_size every time.  */  virtual uint64_t GetFileSize() override;  virtual Status InvalidateCache(size_t offset, size_t length) override;  virtual Status Allocate(uint64_t offset, uint64_t len) override;  virtual size_t GetUniqueId(char* id, size_t max_size) const override;};class WinRandomAccessImpl { protected:  WinFileData* file_base_;  size_t       alignment_;  // Override for behavior change when creating a custom env  virtual Status PositionedReadInternal(char* src, size_t numBytes,                                        uint64_t offset, size_t& bytes_read) const;  WinRandomAccessImpl(WinFileData* file_base, size_t alignment,                      const EnvOptions& options);  virtual ~WinRandomAccessImpl() {}  Status ReadImpl(uint64_t offset, size_t n, Slice* result,                  char* scratch) const;  size_t GetAlignment() const { return alignment_; } public:  WinRandomAccessImpl(const WinRandomAccessImpl&) = delete;  WinRandomAccessImpl& operator=(const WinRandomAccessImpl&) = delete;};// pread() based random-accessclass WinRandomAccessFile    : private WinFileData,      protected WinRandomAccessImpl,  // Want to be able to override                                      // PositionedReadInternal      public RandomAccessFile { public:  WinRandomAccessFile(const std::string& fname, HANDLE hFile, size_t alignment,                      const EnvOptions& options);  ~WinRandomAccessFile();  virtual Status Read(uint64_t offset, size_t n, Slice* result,                      char* scratch) const override;  virtual size_t GetUniqueId(char* id, size_t max_size) const override;  virtual bool use_direct_io() const override { return WinFileData::use_direct_io(); }  virtual Status InvalidateCache(size_t offset, size_t length) override;  virtual size_t GetRequiredBufferAlignment() const override;};// This is a sequential write class. It has been mimicked (as others) after// the original Posix class. We add support for unbuffered I/O on windows as// well// we utilize the original buffer as an alignment buffer to write directly to// file with no buffering.// No buffering requires that the provided buffer is aligned to the physical// sector size (SSD page size) and// that all SetFilePointer() operations to occur with such an alignment.// We thus always write in sector/page size increments to the drive and leave// the tail for the next write OR for Close() at which point we pad with zeros.// No padding is required for// buffered access.class WinWritableImpl { protected:  WinFileData* file_data_;  const uint64_t alignment_;  uint64_t next_write_offset_; // Needed because Windows does not support O_APPEND  uint64_t reservedsize_;  // how far we have reserved space  virtual Status PreallocateInternal(uint64_t spaceToReserve);  WinWritableImpl(WinFileData* file_data, size_t alignment);  ~WinWritableImpl() {}  uint64_t GetAlignement() const { return alignment_; }  Status AppendImpl(const Slice& data);  // Requires that the data is aligned as specified by  // GetRequiredBufferAlignment()  Status PositionedAppendImpl(const Slice& data, uint64_t offset);  Status TruncateImpl(uint64_t size);  Status CloseImpl();  Status SyncImpl();  uint64_t GetFileNextWriteOffset() {    // Double accounting now here with WritableFileWriter    // and this size will be wrong when unbuffered access is used    // but tests implement their own writable files and do not use    // WritableFileWrapper    // so we need to squeeze a square peg through    // a round hole here.    return next_write_offset_;  }  Status AllocateImpl(uint64_t offset, uint64_t len); public:  WinWritableImpl(const WinWritableImpl&) = delete;  WinWritableImpl& operator=(const WinWritableImpl&) = delete;};class WinWritableFile : private WinFileData,                        protected WinWritableImpl,                        public WritableFile { public:  WinWritableFile(const std::string& fname, HANDLE hFile, size_t alignment,                  size_t capacity, const EnvOptions& options);  ~WinWritableFile();  virtual Status Append(const Slice& data) override;  // Requires that the data is aligned as specified by  // GetRequiredBufferAlignment()  virtual Status PositionedAppend(const Slice& data, uint64_t offset) override;  // Need to implement this so the file is truncated correctly  // when buffered and unbuffered mode  virtual Status Truncate(uint64_t size) override;  virtual Status Close() override;  // write out the cached data to the OS cache  // This is now taken care of the WritableFileWriter  virtual Status Flush() override;  virtual Status Sync() override;  virtual Status Fsync() override;  virtual bool IsSyncThreadSafe() const override;  // Indicates if the class makes use of direct I/O  // Use PositionedAppend  virtual bool use_direct_io() const override;  virtual size_t GetRequiredBufferAlignment() const override;  virtual uint64_t GetFileSize() override;  virtual Status Allocate(uint64_t offset, uint64_t len) override;  virtual size_t GetUniqueId(char* id, size_t max_size) const override;};class WinRandomRWFile : private WinFileData,                        protected WinRandomAccessImpl,                        protected WinWritableImpl,                        public RandomRWFile { public:  WinRandomRWFile(const std::string& fname, HANDLE hFile, size_t alignment,                  const EnvOptions& options);  ~WinRandomRWFile() {}  // Indicates if the class makes use of direct I/O  // If false you must pass aligned buffer to Write()  virtual bool use_direct_io() const override;  // Use the returned alignment value to allocate aligned  // buffer for Write() when use_direct_io() returns true  virtual size_t GetRequiredBufferAlignment() const override;  // Write bytes in `data` at  offset `offset`, Returns Status::OK() on success.  // Pass aligned buffer when use_direct_io() returns true.  virtual Status Write(uint64_t offset, const Slice& data) override;  // Read up to `n` bytes starting from offset `offset` and store them in  // result, provided `scratch` size should be at least `n`.  // Returns Status::OK() on success.  virtual Status Read(uint64_t offset, size_t n, Slice* result,                      char* scratch) const override;  virtual Status Flush() override;  virtual Status Sync() override;  virtual Status Fsync() { return Sync(); }  virtual Status Close() override;};class WinMemoryMappedBuffer : public MemoryMappedFileBuffer {private:  HANDLE  file_handle_;  HANDLE  map_handle_;public:  WinMemoryMappedBuffer(HANDLE file_handle, HANDLE map_handle, void* base, size_t size) :    MemoryMappedFileBuffer(base, size),    file_handle_(file_handle),    map_handle_(map_handle) {}  ~WinMemoryMappedBuffer() override;};class WinDirectory : public Directory {  HANDLE handle_; public:  explicit WinDirectory(HANDLE h) noexcept : handle_(h) {    assert(handle_ != INVALID_HANDLE_VALUE);  }  ~WinDirectory() {    ::CloseHandle(handle_);  }  virtual Status Fsync() override;  size_t GetUniqueId(char* id, size_t max_size) const override;};class WinFileLock : public FileLock { public:  explicit WinFileLock(HANDLE hFile) : hFile_(hFile) {    assert(hFile != NULL);    assert(hFile != INVALID_HANDLE_VALUE);  }  ~WinFileLock(); private:  HANDLE hFile_;};}}  // namespace ROCKSDB_NAMESPACE
 |