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 "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"
14 using namespace leveldb
;
16 namespace leveldb_env
{
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
,
26 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
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
);
41 class ChromiumSequentialFileWin
: public SequentialFile
{
43 std::string filename_
;
45 const UMALogger
* uma_logger_
;
48 ChromiumSequentialFileWin(const std::string
& fname
,
50 const UMALogger
* uma_logger
)
51 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) {
52 DCHECK(file_
!= INVALID_HANDLE_VALUE
);
54 virtual ~ChromiumSequentialFileWin() {
55 DCHECK(file_
!= INVALID_HANDLE_VALUE
);
57 file_
= INVALID_HANDLE_VALUE
;
60 virtual Status
Read(size_t n
, Slice
* result
, char* scratch
) {
63 DCHECK(file_
!= INVALID_HANDLE_VALUE
);
64 if (ReadFile(file_
, scratch
, n
, &bytes_read
, NULL
)) {
65 *result
= Slice(scratch
, bytes_read
);
67 DWORD err
= GetLastError();
69 filename_
, GetWindowsErrorMessage(err
), kSequentialFileRead
, err
);
70 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
72 *result
= Slice(scratch
, bytes_read
);
77 virtual Status
Skip(uint64_t 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
);
91 class ChromiumRandomAccessFileWin
: public RandomAccessFile
{
93 std::string filename_
;
94 mutable ::base::File file_
;
95 const UMALogger
* uma_logger_
;
98 ChromiumRandomAccessFileWin(const std::string
& fname
,
100 const UMALogger
* uma_logger
)
101 : filename_(fname
), file_(file
.Pass()), uma_logger_(uma_logger
) {}
102 virtual ~ChromiumRandomAccessFileWin() {}
104 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
, char* scratch
)
107 int r
= file_
.Read(offset
, scratch
, n
);
108 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
110 // An error: return a non-ok status
112 filename_
, "Could not perform read", kRandomAccessFileRead
);
113 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
119 } // unnamed namespace
121 Status
MakeIOErrorWin(Slice filename
,
122 const std::string
& message
,
128 "%s (ChromeMethodErrno: %d::%s::%u)",
131 MethodIDToString(method
),
133 return Status::IOError(filename
, buf
);
135 return Status::IOError(filename
, "<unknown>");
139 ChromiumWritableFileWin::ChromiumWritableFileWin(
140 const std::string
& fname
,
142 const UMALogger
* uma_logger
,
143 WriteTracker
* tracker
,
147 uma_logger_(uma_logger
),
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
))
157 if (file_type_
!= kManifest
)
158 tracker_
->DidCreateNewFile(filename_
);
159 parent_dir_
= FilePathToString(ChromiumEnv::CreateFilePath(fname
).DirName());
162 ChromiumWritableFileWin::~ChromiumWritableFileWin() {
163 if (file_
!= INVALID_HANDLE_VALUE
) {
164 // Ignoring any potential errors
167 file_
= INVALID_HANDLE_VALUE
;
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.
177 Status
ChromiumWritableFileWin::Append(const Slice
& data
) {
178 if (file_type_
== kManifest
&& tracker_
->DoesDirNeedSync(filename_
)) {
179 Status s
= SyncParent();
182 tracker_
->DidSyncDir(filename_
);
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
);
196 Status
ChromiumWritableFileWin::Close() {
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
);
205 file_
= INVALID_HANDLE_VALUE
;
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.
215 Status
ChromiumWritableFileWin::Sync() {
216 TRACE_EVENT0("leveldb", "ChromiumEnvWin::Sync");
218 DWORD error
= ERROR_SUCCESS
;
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
);
234 ChromiumEnvWin::ChromiumEnvWin() {}
236 ChromiumEnvWin::~ChromiumEnvWin() {}
238 Status
ChromiumEnvWin::NewSequentialFile(const std::string
& fname
,
239 SequentialFile
** result
) {
240 HANDLE f
= CreateFile(base::UTF8ToUTF16(fname
).c_str(),
245 FILE_ATTRIBUTE_NORMAL
,
247 if (f
== INVALID_HANDLE_VALUE
) {
249 DWORD err
= GetLastError();
250 RecordOSError(kNewSequentialFile
, err
);
251 return MakeIOErrorWin(
252 fname
, GetWindowsErrorMessage(err
), kNewSequentialFile
, err
);
254 *result
= new ChromiumSequentialFileWin(fname
, f
, this);
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.
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");
274 ::base::File::Error error_code
= file
.error_details();
275 if (error_code
== ::base::File::FILE_ERROR_TOO_MANY_OPENED
)
276 RecordOpenFilesLimit("TooManyOpened");
278 RecordOpenFilesLimit("OtherError");
280 RecordOSError(kNewRandomAccessFile
, error_code
);
281 return MakeIOErrorWin(fname
,
282 FileErrorString(error_code
),
283 kNewRandomAccessFile
,
287 Status
ChromiumEnvWin::NewWritableFile(const std::string
& fname
,
288 WritableFile
** result
) {
290 HANDLE f
= CreateFile(base::UTF8ToUTF16(fname
).c_str(),
295 FILE_ATTRIBUTE_NORMAL
,
297 if (f
== INVALID_HANDLE_VALUE
) {
298 DWORD err
= GetLastError();
299 RecordErrorAt(kNewWritableFile
);
300 return MakeIOErrorWin(
301 fname
, GetWindowsErrorMessage(err
), kNewWritableFile
, err
);
303 *result
= new ChromiumWritableFileWin(fname
, f
, this, this, make_backup_
);
308 base::File::Error
ChromiumEnvWin::GetDirectoryEntries(
309 const base::FilePath
& dir_param
,
310 std::vector
<base::FilePath
>* result
) const {
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
);
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(".."))
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
);
337 Status
ChromiumEnvWin::NewLogger(const std::string
& fname
, Logger
** result
) {
338 FILE* f
= _wfopen(base::UTF8ToUTF16(fname
).c_str(), L
"w");
341 int saved_errno
= errno
;
342 RecordOSError(kNewLogger
, saved_errno
);
343 return MakeIOError(fname
, strerror(saved_errno
), kNewLogger
, saved_errno
);
345 *result
= new ChromiumLogger(f
);
350 void ChromiumEnvWin::RecordOSError(MethodID method
, DWORD error
) const {
351 RecordErrorAt(method
);
352 GetOSErrorHistogram(method
, ERANGE
+ 1)->Add(error
);
355 } // namespace leveldb_env