Home | History | Annotate | Download | only in leveldatabase
      1 // Copyright (c) 2013 The LevelDB Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
      4 
      5 #include "base/debug/trace_event.h"
      6 #include "base/files/file.h"
      7 #include "base/files/file_path.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "base/win/win_util.h"
     10 #include "chromium_logger.h"
     11 #include "env_chromium_stdio.h"
     12 #include "env_chromium_win.h"
     13 
     14 using namespace leveldb;
     15 
     16 namespace leveldb_env {
     17 
     18 namespace {
     19 
     20 static std::string GetWindowsErrorMessage(DWORD err) {
     21   LPTSTR errorText(NULL);
     22   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
     23                     FORMAT_MESSAGE_IGNORE_INSERTS,
     24                 NULL,
     25                 err,
     26                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
     27                 (LPTSTR) & errorText,
     28                 0,
     29                 NULL);
     30   if (errorText != NULL) {
     31     std::string message(base::UTF16ToUTF8(errorText));
     32     // FormatMessage adds CR/LF to messages so we remove it.
     33     base::TrimWhitespace(message, base::TRIM_TRAILING, &message);
     34     LocalFree(errorText);
     35     return message;
     36   } else {
     37     return std::string();
     38   }
     39 }
     40 
     41 class ChromiumSequentialFileWin : public SequentialFile {
     42  private:
     43   std::string filename_;
     44   HANDLE file_;
     45   const UMALogger* uma_logger_;
     46 
     47  public:
     48   ChromiumSequentialFileWin(const std::string& fname,
     49                          HANDLE f,
     50                          const UMALogger* uma_logger)
     51       : filename_(fname), file_(f), uma_logger_(uma_logger) {
     52     DCHECK(file_ != INVALID_HANDLE_VALUE);
     53   }
     54   virtual ~ChromiumSequentialFileWin() {
     55     DCHECK(file_ != INVALID_HANDLE_VALUE);
     56     CloseHandle(file_);
     57     file_ = INVALID_HANDLE_VALUE;
     58   }
     59 
     60   virtual Status Read(size_t n, Slice* result, char* scratch) {
     61     Status s;
     62     DWORD bytes_read(0);
     63     DCHECK(file_ != INVALID_HANDLE_VALUE);
     64     if (ReadFile(file_, scratch, n, &bytes_read, NULL)) {
     65       *result = Slice(scratch, bytes_read);
     66     } else {
     67       DWORD err = GetLastError();
     68       s = MakeIOErrorWin(
     69           filename_, GetWindowsErrorMessage(err), kSequentialFileRead, err);
     70       uma_logger_->RecordErrorAt(kSequentialFileRead);
     71       if (bytes_read > 0)
     72         *result = Slice(scratch, bytes_read);
     73     }
     74     return s;
     75   }
     76 
     77   virtual Status Skip(uint64_t n) {
     78     LARGE_INTEGER li;
     79     li.QuadPart = n;
     80     if (SetFilePointer(file_, li.LowPart, &li.HighPart, FILE_CURRENT) ==
     81         INVALID_SET_FILE_POINTER) {
     82       DWORD err = GetLastError();
     83       uma_logger_->RecordErrorAt(kSequentialFileSkip);
     84       return MakeIOErrorWin(
     85           filename_, GetWindowsErrorMessage(err), kSequentialFileSkip, err);
     86     }
     87     return Status::OK();
     88   }
     89 };
     90 
     91 class ChromiumRandomAccessFileWin : public RandomAccessFile {
     92  private:
     93   std::string filename_;
     94   mutable ::base::File file_;
     95   const UMALogger* uma_logger_;
     96 
     97  public:
     98   ChromiumRandomAccessFileWin(const std::string& fname,
     99                               ::base::File file,
    100                               const UMALogger* uma_logger)
    101       : filename_(fname), file_(file.Pass()), uma_logger_(uma_logger) {}
    102   virtual ~ChromiumRandomAccessFileWin() {}
    103 
    104   virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch)
    105       const {
    106     Status s;
    107     int r = file_.Read(offset, scratch, n);
    108     *result = Slice(scratch, (r < 0) ? 0 : r);
    109     if (r < 0) {
    110       // An error: return a non-ok status
    111       s = MakeIOError(
    112           filename_, "Could not perform read", kRandomAccessFileRead);
    113       uma_logger_->RecordErrorAt(kRandomAccessFileRead);
    114     }
    115     return s;
    116   }
    117 };
    118 
    119 }  // unnamed namespace
    120 
    121 Status MakeIOErrorWin(Slice filename,
    122                         const std::string& message,
    123                         MethodID method,
    124                         DWORD error) {
    125   char buf[512];
    126   if (snprintf(buf,
    127                sizeof(buf),
    128                "%s (ChromeMethodErrno: %d::%s::%u)",
    129                message.c_str(),
    130                method,
    131                MethodIDToString(method),
    132                error) >= 0) {
    133     return Status::IOError(filename, buf);
    134   } else {
    135     return Status::IOError(filename, "<unknown>");
    136   }
    137 }
    138 
    139 ChromiumWritableFileWin::ChromiumWritableFileWin(
    140     const std::string& fname,
    141     HANDLE f,
    142     const UMALogger* uma_logger,
    143     WriteTracker* tracker,
    144     bool make_backup)
    145     : filename_(fname),
    146       file_(f),
    147       uma_logger_(uma_logger),
    148       tracker_(tracker),
    149       file_type_(kOther),
    150       make_backup_(make_backup) {
    151   DCHECK(f != INVALID_HANDLE_VALUE);
    152   base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
    153   if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
    154     file_type_ = kManifest;
    155   else if (ChromiumEnv::HasTableExtension(path))
    156     file_type_ = kTable;
    157   if (file_type_ != kManifest)
    158     tracker_->DidCreateNewFile(filename_);
    159   parent_dir_ = FilePathToString(ChromiumEnv::CreateFilePath(fname).DirName());
    160 }
    161 
    162 ChromiumWritableFileWin::~ChromiumWritableFileWin() {
    163   if (file_ != INVALID_HANDLE_VALUE) {
    164     // Ignoring any potential errors
    165 
    166     CloseHandle(file_);
    167     file_ = INVALID_HANDLE_VALUE;
    168   }
    169 }
    170 
    171 Status ChromiumWritableFileWin::SyncParent() {
    172   // On Windows no need to sync parent directory. It's metadata will be
    173   // updated via the creation of the new file, without an explicit sync.
    174   return Status();
    175 }
    176 
    177 Status ChromiumWritableFileWin::Append(const Slice& data) {
    178   if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
    179     Status s = SyncParent();
    180     if (!s.ok())
    181       return s;
    182     tracker_->DidSyncDir(filename_);
    183   }
    184 
    185   DWORD written(0);
    186   if (!WriteFile(file_, data.data(), data.size(), &written, NULL)) {
    187     DWORD err = GetLastError();
    188     uma_logger_->RecordOSError(kWritableFileAppend,
    189                                base::File::OSErrorToFileError(err));
    190     return MakeIOErrorWin(
    191         filename_, GetWindowsErrorMessage(err), kWritableFileAppend, err);
    192   }
    193   return Status::OK();
    194 }
    195 
    196 Status ChromiumWritableFileWin::Close() {
    197   Status result;
    198   DCHECK(file_ != INVALID_HANDLE_VALUE);
    199   if (!CloseHandle(file_)) {
    200     DWORD err = GetLastError();
    201     result = MakeIOErrorWin(
    202         filename_, GetWindowsErrorMessage(err), kWritableFileClose, err);
    203     uma_logger_->RecordErrorAt(kWritableFileClose);
    204   }
    205   file_ = INVALID_HANDLE_VALUE;
    206   return result;
    207 }
    208 
    209 Status ChromiumWritableFileWin::Flush() {
    210   // Windows doesn't use POSIX file streams, so there are no file stream buffers
    211   // to flush - this is a no-op.
    212   return Status();
    213 }
    214 
    215 Status ChromiumWritableFileWin::Sync() {
    216   TRACE_EVENT0("leveldb", "ChromiumEnvWin::Sync");
    217   Status result;
    218   DWORD error = ERROR_SUCCESS;
    219 
    220   DCHECK(file_ != INVALID_HANDLE_VALUE);
    221   if (!FlushFileBuffers(file_))
    222     error = GetLastError();
    223   if (error != ERROR_SUCCESS) {
    224     result = MakeIOErrorWin(
    225         filename_, GetWindowsErrorMessage(error), kWritableFileSync, error);
    226     uma_logger_->RecordErrorAt(kWritableFileSync);
    227   } else if (make_backup_ && file_type_ == kTable) {
    228     bool success = ChromiumEnv::MakeBackup(filename_);
    229     uma_logger_->RecordBackupResult(success);
    230   }
    231   return result;
    232 }
    233 
    234 ChromiumEnvWin::ChromiumEnvWin() {}
    235 
    236 ChromiumEnvWin::~ChromiumEnvWin() {}
    237 
    238 Status ChromiumEnvWin::NewSequentialFile(const std::string& fname,
    239                                            SequentialFile** result) {
    240   HANDLE f = CreateFile(base::UTF8ToUTF16(fname).c_str(),
    241                         GENERIC_READ,
    242                         FILE_SHARE_READ,
    243                         NULL,
    244                         OPEN_EXISTING,
    245                         FILE_ATTRIBUTE_NORMAL,
    246                         NULL);
    247   if (f == INVALID_HANDLE_VALUE) {
    248     *result = NULL;
    249     DWORD err = GetLastError();
    250     RecordOSError(kNewSequentialFile, err);
    251     return MakeIOErrorWin(
    252         fname, GetWindowsErrorMessage(err), kNewSequentialFile, err);
    253   } else {
    254     *result = new ChromiumSequentialFileWin(fname, f, this);
    255     return Status::OK();
    256   }
    257 }
    258 
    259 void ChromiumEnvWin::RecordOpenFilesLimit(const std::string& type) {
    260   // The Windows POSIX implementation (which this class doesn't use)
    261   // has an open file limit, but when using the Win32 API this is limited by
    262   // available memory, so no value to report.
    263 }
    264 
    265 Status ChromiumEnvWin::NewRandomAccessFile(const std::string& fname,
    266                                            RandomAccessFile** result) {
    267   int flags = ::base::File::FLAG_READ | ::base::File::FLAG_OPEN;
    268   ::base::File file(ChromiumEnv::CreateFilePath(fname), flags);
    269   if (file.IsValid()) {
    270     *result = new ChromiumRandomAccessFileWin(fname, file.Pass(), this);
    271     RecordOpenFilesLimit("Success");
    272     return Status::OK();
    273   }
    274   ::base::File::Error error_code = file.error_details();
    275   if (error_code == ::base::File::FILE_ERROR_TOO_MANY_OPENED)
    276     RecordOpenFilesLimit("TooManyOpened");
    277   else
    278     RecordOpenFilesLimit("OtherError");
    279   *result = NULL;
    280   RecordOSError(kNewRandomAccessFile, error_code);
    281   return MakeIOErrorWin(fname,
    282                         FileErrorString(error_code),
    283                         kNewRandomAccessFile,
    284                         error_code);
    285 }
    286 
    287 Status ChromiumEnvWin::NewWritableFile(const std::string& fname,
    288                                          WritableFile** result) {
    289   *result = NULL;
    290   HANDLE f = CreateFile(base::UTF8ToUTF16(fname).c_str(),
    291                         GENERIC_WRITE,
    292                         FILE_SHARE_READ,
    293                         NULL,
    294                         CREATE_ALWAYS,
    295                         FILE_ATTRIBUTE_NORMAL,
    296                         NULL);
    297   if (f == INVALID_HANDLE_VALUE) {
    298     DWORD err = GetLastError();
    299     RecordErrorAt(kNewWritableFile);
    300     return MakeIOErrorWin(
    301         fname, GetWindowsErrorMessage(err), kNewWritableFile, err);
    302   } else {
    303     *result = new ChromiumWritableFileWin(fname, f, this, this, make_backup_);
    304     return Status::OK();
    305   }
    306 }
    307 
    308 base::File::Error ChromiumEnvWin::GetDirectoryEntries(
    309     const base::FilePath& dir_param,
    310     std::vector<base::FilePath>* result) const {
    311   result->clear();
    312   base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
    313   WIN32_FIND_DATA find_data;
    314   HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
    315   if (find_handle == INVALID_HANDLE_VALUE) {
    316     DWORD last_error = GetLastError();
    317     if (last_error == ERROR_FILE_NOT_FOUND)
    318       return base::File::FILE_OK;
    319     return base::File::OSErrorToFileError(last_error);
    320   }
    321   do {
    322     base::FilePath filepath(find_data.cFileName);
    323     base::FilePath::StringType basename = filepath.BaseName().value();
    324     if (basename == FILE_PATH_LITERAL(".") ||
    325         basename == FILE_PATH_LITERAL(".."))
    326       continue;
    327     result->push_back(filepath.BaseName());
    328   } while (FindNextFile(find_handle, &find_data));
    329   DWORD last_error = GetLastError();
    330   base::File::Error return_value = base::File::FILE_OK;
    331   if (last_error != ERROR_NO_MORE_FILES)
    332     return_value = base::File::OSErrorToFileError(last_error);
    333   FindClose(find_handle);
    334   return return_value;
    335 }
    336 
    337 Status ChromiumEnvWin::NewLogger(const std::string& fname, Logger** result) {
    338   FILE* f = _wfopen(base::UTF8ToUTF16(fname).c_str(), L"w");
    339   if (f == NULL) {
    340     *result = NULL;
    341     int saved_errno = errno;
    342     RecordOSError(kNewLogger, saved_errno);
    343     return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
    344   } else {
    345     *result = new ChromiumLogger(f);
    346     return Status::OK();
    347   }
    348 }
    349 
    350 void ChromiumEnvWin::RecordOSError(MethodID method, DWORD error) const {
    351   RecordErrorAt(method);
    352   GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
    353 }
    354 
    355 }  // namespace leveldb_env
    356