OLD | NEW |
| (Empty) |
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_path.h" | |
7 #include "base/strings/utf_string_conversions.h" | |
8 #include "base/win/win_util.h" | |
9 #include "chromium_logger.h" | |
10 #include "env_chromium_stdio.h" | |
11 #include "env_chromium_win.h" | |
12 | |
13 using namespace leveldb; | |
14 | |
15 namespace leveldb_env { | |
16 | |
17 namespace { | |
18 | |
19 static std::string GetWindowsErrorMessage(DWORD err) { | |
20 LPTSTR errorText(NULL); | |
21 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
22 FORMAT_MESSAGE_IGNORE_INSERTS, | |
23 NULL, | |
24 err, | |
25 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
26 (LPTSTR) & errorText, | |
27 0, | |
28 NULL); | |
29 if (errorText != NULL) { | |
30 std::string message(base::UTF16ToUTF8(errorText)); | |
31 // FormatMessage adds CR/LF to messages so we remove it. | |
32 TrimWhitespace(message, TRIM_TRAILING, &message); | |
33 LocalFree(errorText); | |
34 return message; | |
35 } else { | |
36 return std::string(); | |
37 } | |
38 } | |
39 | |
40 class ChromiumSequentialFileWin : public SequentialFile { | |
41 private: | |
42 std::string filename_; | |
43 HANDLE file_; | |
44 const UMALogger* uma_logger_; | |
45 | |
46 public: | |
47 ChromiumSequentialFileWin(const std::string& fname, | |
48 HANDLE f, | |
49 const UMALogger* uma_logger) | |
50 : filename_(fname), file_(f), uma_logger_(uma_logger) { | |
51 DCHECK(file_ != INVALID_HANDLE_VALUE); | |
52 } | |
53 virtual ~ChromiumSequentialFileWin() { | |
54 DCHECK(file_ != INVALID_HANDLE_VALUE); | |
55 CloseHandle(file_); | |
56 file_ = INVALID_HANDLE_VALUE; | |
57 } | |
58 | |
59 virtual Status Read(size_t n, Slice* result, char* scratch) { | |
60 Status s; | |
61 DWORD bytes_read(0); | |
62 DCHECK(file_ != INVALID_HANDLE_VALUE); | |
63 if (ReadFile(file_, scratch, n, &bytes_read, NULL)) { | |
64 *result = Slice(scratch, bytes_read); | |
65 } else { | |
66 DWORD err = GetLastError(); | |
67 s = MakeIOErrorWin( | |
68 filename_, GetWindowsErrorMessage(err), kSequentialFileRead, err); | |
69 uma_logger_->RecordErrorAt(kSequentialFileRead); | |
70 if (bytes_read > 0) | |
71 *result = Slice(scratch, bytes_read); | |
72 } | |
73 return s; | |
74 } | |
75 | |
76 virtual Status Skip(uint64_t n) { | |
77 LARGE_INTEGER li; | |
78 li.QuadPart = n; | |
79 if (SetFilePointer(file_, li.LowPart, &li.HighPart, FILE_CURRENT) == | |
80 INVALID_SET_FILE_POINTER) { | |
81 DWORD err = GetLastError(); | |
82 uma_logger_->RecordErrorAt(kSequentialFileSkip); | |
83 return MakeIOErrorWin( | |
84 filename_, GetWindowsErrorMessage(err), kSequentialFileSkip, err); | |
85 } | |
86 return Status::OK(); | |
87 } | |
88 }; | |
89 | |
90 class ChromiumRandomAccessFileWin : public RandomAccessFile { | |
91 private: | |
92 std::string filename_; | |
93 ::base::PlatformFile file_; | |
94 const UMALogger* uma_logger_; | |
95 | |
96 public: | |
97 ChromiumRandomAccessFileWin(const std::string& fname, | |
98 ::base::PlatformFile file, | |
99 const UMALogger* uma_logger) | |
100 : filename_(fname), file_(file), uma_logger_(uma_logger) {} | |
101 virtual ~ChromiumRandomAccessFileWin() { ::base::ClosePlatformFile(file_); } | |
102 | |
103 virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) | |
104 const { | |
105 Status s; | |
106 int r = ::base::ReadPlatformFile(file_, offset, scratch, n); | |
107 *result = Slice(scratch, (r < 0) ? 0 : r); | |
108 if (r < 0) { | |
109 // An error: return a non-ok status | |
110 s = MakeIOError( | |
111 filename_, "Could not perform read", kRandomAccessFileRead); | |
112 uma_logger_->RecordErrorAt(kRandomAccessFileRead); | |
113 } | |
114 return s; | |
115 } | |
116 }; | |
117 | |
118 } // unnamed namespace | |
119 | |
120 Status MakeIOErrorWin(Slice filename, | |
121 const std::string& message, | |
122 MethodID method, | |
123 DWORD error) { | |
124 char buf[512]; | |
125 if (snprintf(buf, | |
126 sizeof(buf), | |
127 "%s (ChromeMethodErrno: %d::%s::%u)", | |
128 message.c_str(), | |
129 method, | |
130 MethodIDToString(method), | |
131 error) >= 0) { | |
132 return Status::IOError(filename, buf); | |
133 } else { | |
134 return Status::IOError(filename, "<unknown>"); | |
135 } | |
136 } | |
137 | |
138 ChromiumWritableFileWin::ChromiumWritableFileWin( | |
139 const std::string& fname, | |
140 HANDLE f, | |
141 const UMALogger* uma_logger, | |
142 WriteTracker* tracker, | |
143 bool make_backup) | |
144 : filename_(fname), | |
145 file_(f), | |
146 uma_logger_(uma_logger), | |
147 tracker_(tracker), | |
148 file_type_(kOther), | |
149 make_backup_(make_backup) { | |
150 DCHECK(f != INVALID_HANDLE_VALUE); | |
151 base::FilePath path = base::FilePath::FromUTF8Unsafe(fname); | |
152 if (FilePathToString(path.BaseName()).find("MANIFEST") == 0) | |
153 file_type_ = kManifest; | |
154 else if (ChromiumEnv::HasTableExtension(path)) | |
155 file_type_ = kTable; | |
156 if (file_type_ != kManifest) | |
157 tracker_->DidCreateNewFile(filename_); | |
158 parent_dir_ = FilePathToString(ChromiumEnv::CreateFilePath(fname).DirName()); | |
159 } | |
160 | |
161 ChromiumWritableFileWin::~ChromiumWritableFileWin() { | |
162 if (file_ != INVALID_HANDLE_VALUE) { | |
163 // Ignoring any potential errors | |
164 | |
165 CloseHandle(file_); | |
166 file_ = INVALID_HANDLE_VALUE; | |
167 } | |
168 } | |
169 | |
170 Status ChromiumWritableFileWin::SyncParent() { | |
171 // On Windows no need to sync parent directory. It's metadata will be | |
172 // updated via the creation of the new file, without an explicit sync. | |
173 return Status(); | |
174 } | |
175 | |
176 Status ChromiumWritableFileWin::Append(const Slice& data) { | |
177 if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) { | |
178 Status s = SyncParent(); | |
179 if (!s.ok()) | |
180 return s; | |
181 tracker_->DidSyncDir(filename_); | |
182 } | |
183 | |
184 DWORD written(0); | |
185 if (!WriteFile(file_, data.data(), data.size(), &written, NULL)) { | |
186 DWORD err = GetLastError(); | |
187 uma_logger_->RecordOSError(kWritableFileAppend, | |
188 base::LastErrorToPlatformFileError(err)); | |
189 return MakeIOErrorWin( | |
190 filename_, GetWindowsErrorMessage(err), kWritableFileAppend, err); | |
191 } | |
192 return Status::OK(); | |
193 } | |
194 | |
195 Status ChromiumWritableFileWin::Close() { | |
196 Status result; | |
197 DCHECK(file_ != INVALID_HANDLE_VALUE); | |
198 if (!CloseHandle(file_)) { | |
199 DWORD err = GetLastError(); | |
200 result = MakeIOErrorWin( | |
201 filename_, GetWindowsErrorMessage(err), kWritableFileClose, err); | |
202 uma_logger_->RecordErrorAt(kWritableFileClose); | |
203 } | |
204 file_ = INVALID_HANDLE_VALUE; | |
205 return result; | |
206 } | |
207 | |
208 Status ChromiumWritableFileWin::Flush() { | |
209 Status result; | |
210 if (!FlushFileBuffers(file_)) { | |
211 DWORD err = GetLastError(); | |
212 result = MakeIOErrorWin( | |
213 filename_, GetWindowsErrorMessage(err), kWritableFileFlush, err); | |
214 uma_logger_->RecordOSError(kWritableFileFlush, | |
215 base::LastErrorToPlatformFileError(err)); | |
216 } | |
217 return result; | |
218 } | |
219 | |
220 Status ChromiumWritableFileWin::Sync() { | |
221 TRACE_EVENT0("leveldb", "ChromiumEnvWin::Sync"); | |
222 Status result; | |
223 DWORD error = ERROR_SUCCESS; | |
224 | |
225 DCHECK(file_ != INVALID_HANDLE_VALUE); | |
226 if (!FlushFileBuffers(file_)) | |
227 error = GetLastError(); | |
228 if (error != ERROR_SUCCESS) { | |
229 result = MakeIOErrorWin( | |
230 filename_, GetWindowsErrorMessage(error), kWritableFileSync, error); | |
231 uma_logger_->RecordErrorAt(kWritableFileSync); | |
232 } else if (make_backup_ && file_type_ == kTable) { | |
233 bool success = ChromiumEnv::MakeBackup(filename_); | |
234 uma_logger_->RecordBackupResult(success); | |
235 } | |
236 return result; | |
237 } | |
238 | |
239 ChromiumEnvWin::ChromiumEnvWin() {} | |
240 | |
241 ChromiumEnvWin::~ChromiumEnvWin() {} | |
242 | |
243 Status ChromiumEnvWin::NewSequentialFile(const std::string& fname, | |
244 SequentialFile** result) { | |
245 HANDLE f = CreateFile(base::UTF8ToUTF16(fname).c_str(), | |
246 GENERIC_READ, | |
247 FILE_SHARE_READ, | |
248 NULL, | |
249 OPEN_EXISTING, | |
250 FILE_ATTRIBUTE_NORMAL, | |
251 NULL); | |
252 if (f == INVALID_HANDLE_VALUE) { | |
253 *result = NULL; | |
254 DWORD err = GetLastError(); | |
255 RecordOSError(kNewSequentialFile, err); | |
256 return MakeIOErrorWin( | |
257 fname, GetWindowsErrorMessage(err), kNewSequentialFile, err); | |
258 } else { | |
259 *result = new ChromiumSequentialFileWin(fname, f, this); | |
260 return Status::OK(); | |
261 } | |
262 } | |
263 | |
264 void ChromiumEnvWin::RecordOpenFilesLimit(const std::string& type) { | |
265 // The Windows POSIX implementation (which this class doesn't use) | |
266 // has an open file limit, but when using the Win32 API this is limited by | |
267 // available memory, so no value to report. | |
268 } | |
269 | |
270 Status ChromiumEnvWin::NewRandomAccessFile(const std::string& fname, | |
271 RandomAccessFile** result) { | |
272 int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN; | |
273 bool created; | |
274 ::base::PlatformFileError error_code; | |
275 ::base::PlatformFile file = ::base::CreatePlatformFile( | |
276 ChromiumEnv::CreateFilePath(fname), flags, &created, &error_code); | |
277 if (error_code == ::base::PLATFORM_FILE_OK) { | |
278 *result = new ChromiumRandomAccessFileWin(fname, file, this); | |
279 RecordOpenFilesLimit("Success"); | |
280 return Status::OK(); | |
281 } | |
282 if (error_code == ::base::PLATFORM_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 PlatformFileErrorString(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::PlatformFileError 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::PLATFORM_FILE_OK; | |
326 return base::LastErrorToPlatformFileError(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::PlatformFileError return_value = base::PLATFORM_FILE_OK; | |
338 if (last_error != ERROR_NO_MORE_FILES) | |
339 return_value = base::LastErrorToPlatformFileError(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 | |
OLD | NEW |