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 "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 | |
OLD | NEW |