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