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