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