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.
5 #include "third_party/leveldatabase/env_chromium_win.h"
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"
15 using leveldb::ChromiumLogger
;
16 using leveldb::Logger
;
17 using leveldb::RandomAccessFile
;
18 using leveldb::SequentialFile
;
20 using leveldb::Status
;
21 using leveldb::WritableFile
;
23 namespace leveldb_env
{
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
,
33 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
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
);
48 class ChromiumSequentialFileWin
: public SequentialFile
{
50 std::string filename_
;
52 const UMALogger
* uma_logger_
;
55 ChromiumSequentialFileWin(const std::string
& fname
,
57 const UMALogger
* uma_logger
)
58 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) {
59 DCHECK(file_
!= INVALID_HANDLE_VALUE
);
61 virtual ~ChromiumSequentialFileWin() {
62 DCHECK(file_
!= INVALID_HANDLE_VALUE
);
64 file_
= INVALID_HANDLE_VALUE
;
67 virtual Status
Read(size_t n
, Slice
* result
, char* scratch
) {
70 DCHECK(file_
!= INVALID_HANDLE_VALUE
);
71 if (ReadFile(file_
, scratch
, n
, &bytes_read
, NULL
)) {
72 *result
= Slice(scratch
, bytes_read
);
74 DWORD err
= GetLastError();
76 filename_
, GetWindowsErrorMessage(err
), kSequentialFileRead
, err
);
77 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
79 *result
= Slice(scratch
, bytes_read
);
84 virtual Status
Skip(uint64_t 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
);
98 class ChromiumRandomAccessFileWin
: public RandomAccessFile
{
100 std::string filename_
;
101 mutable ::base::File file_
;
102 const UMALogger
* uma_logger_
;
105 ChromiumRandomAccessFileWin(const std::string
& fname
,
107 const UMALogger
* uma_logger
)
108 : filename_(fname
), file_(file
.Pass()), uma_logger_(uma_logger
) {}
109 virtual ~ChromiumRandomAccessFileWin() {}
111 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
, char* scratch
)
114 int r
= file_
.Read(offset
, scratch
, n
);
115 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
117 // An error: return a non-ok status
119 filename_
, "Could not perform read", kRandomAccessFileRead
);
120 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
126 } // unnamed namespace
128 Status
MakeIOErrorWin(Slice filename
,
129 const std::string
& message
,
135 "%s (ChromeMethodErrno: %d::%s::%u)",
138 MethodIDToString(method
),
140 return Status::IOError(filename
, buf
);
142 return Status::IOError(filename
, "<unknown>");
146 ChromiumWritableFileWin::ChromiumWritableFileWin(
147 const std::string
& fname
,
149 const UMALogger
* uma_logger
,
150 WriteTracker
* tracker
,
154 uma_logger_(uma_logger
),
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
))
164 if (file_type_
!= kManifest
)
165 tracker_
->DidCreateNewFile(filename_
);
166 parent_dir_
= FilePathToString(ChromiumEnv::CreateFilePath(fname
).DirName());
169 ChromiumWritableFileWin::~ChromiumWritableFileWin() {
170 if (file_
!= INVALID_HANDLE_VALUE
) {
171 // Ignoring any potential errors
174 file_
= INVALID_HANDLE_VALUE
;
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.
184 Status
ChromiumWritableFileWin::Append(const Slice
& data
) {
185 if (file_type_
== kManifest
&& tracker_
->DoesDirNeedSync(filename_
)) {
186 Status s
= SyncParent();
189 tracker_
->DidSyncDir(filename_
);
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
);
203 Status
ChromiumWritableFileWin::Close() {
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
);
212 file_
= INVALID_HANDLE_VALUE
;
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.
222 Status
ChromiumWritableFileWin::Sync() {
223 TRACE_EVENT0("leveldb", "ChromiumEnvWin::Sync");
225 DWORD error
= ERROR_SUCCESS
;
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
);
241 ChromiumEnvWin::ChromiumEnvWin() {}
243 ChromiumEnvWin::~ChromiumEnvWin() {}
245 Status
ChromiumEnvWin::NewSequentialFile(const std::string
& fname
,
246 SequentialFile
** result
) {
247 HANDLE f
= CreateFile(base::UTF8ToUTF16(fname
).c_str(),
252 FILE_ATTRIBUTE_NORMAL
,
254 if (f
== INVALID_HANDLE_VALUE
) {
256 DWORD err
= GetLastError();
257 RecordOSError(kNewSequentialFile
, err
);
258 return MakeIOErrorWin(
259 fname
, GetWindowsErrorMessage(err
), kNewSequentialFile
, err
);
261 *result
= new ChromiumSequentialFileWin(fname
, f
, this);
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.
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");
281 ::base::File::Error error_code
= file
.error_details();
282 if (error_code
== ::base::File::FILE_ERROR_TOO_MANY_OPENED
)
283 RecordOpenFilesLimit("TooManyOpened");
285 RecordOpenFilesLimit("OtherError");
287 RecordOSError(kNewRandomAccessFile
, error_code
);
288 return MakeIOErrorWin(fname
,
289 FileErrorString(error_code
),
290 kNewRandomAccessFile
,
294 Status
ChromiumEnvWin::NewWritableFile(const std::string
& fname
,
295 WritableFile
** result
) {
297 HANDLE f
= CreateFile(base::UTF8ToUTF16(fname
).c_str(),
302 FILE_ATTRIBUTE_NORMAL
,
304 if (f
== INVALID_HANDLE_VALUE
) {
305 DWORD err
= GetLastError();
306 RecordErrorAt(kNewWritableFile
);
307 return MakeIOErrorWin(
308 fname
, GetWindowsErrorMessage(err
), kNewWritableFile
, err
);
310 *result
= new ChromiumWritableFileWin(fname
, f
, this, this, make_backup_
);
315 base::File::Error
ChromiumEnvWin::GetDirectoryEntries(
316 const base::FilePath
& dir_param
,
317 std::vector
<base::FilePath
>* result
) const {
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
);
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(".."))
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
);
344 Status
ChromiumEnvWin::NewLogger(const std::string
& fname
, Logger
** result
) {
345 FILE* f
= _wfopen(base::UTF8ToUTF16(fname
).c_str(), L
"w");
348 int saved_errno
= errno
;
349 RecordOSError(kNewLogger
, saved_errno
);
350 return MakeIOError(fname
, strerror(saved_errno
), kNewLogger
, saved_errno
);
352 *result
= new ChromiumLogger(f
);
357 void ChromiumEnvWin::RecordOSError(MethodID method
, DWORD error
) const {
358 RecordErrorAt(method
);
359 GetOSErrorHistogram(method
, ERANGE
+ 1)->Add(error
);
362 } // namespace leveldb_env