1 // Copyright (c) 2011 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.
10 #include "base/at_exit.h"
11 #include "base/debug/trace_event.h"
12 #include "base/file_util.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_path.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/platform_file.h"
20 #include "base/posix/eintr_wrapper.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/synchronization/lock.h"
23 #include "base/sys_info.h"
24 #include "base/threading/platform_thread.h"
25 #include "base/threading/thread.h"
26 #include "chromium_logger.h"
27 #include "env_chromium.h"
28 #include "leveldb/env.h"
29 #include "leveldb/slice.h"
30 #include "port/port.h"
31 #include "third_party/re2/re2/re2.h"
32 #include "util/logging.h"
36 #include "base/win/win_util.h"
42 #include <sys/resource.h>
46 using namespace leveldb
;
48 namespace leveldb_env
{
52 const base::FilePath::CharType backup_table_extension
[] =
53 FILE_PATH_LITERAL(".bak");
54 const base::FilePath::CharType table_extension
[] = FILE_PATH_LITERAL(".ldb");
56 #if (defined(OS_POSIX) && !defined(OS_LINUX)) || defined(OS_WIN)
57 // The following are glibc-specific
59 size_t fread_unlocked(void *ptr
, size_t size
, size_t n
, FILE *file
) {
60 return fread(ptr
, size
, n
, file
);
63 size_t fwrite_unlocked(const void *ptr
, size_t size
, size_t n
, FILE *file
) {
64 return fwrite(ptr
, size
, n
, file
);
67 int fflush_unlocked(FILE *file
) {
71 #if !defined(OS_ANDROID)
72 int fdatasync(int fildes
) {
74 return _commit(fildes
);
76 return HANDLE_EINTR(fsync(fildes
));
83 // Wide-char safe fopen wrapper.
84 FILE* fopen_internal(const char* fname
, const char* mode
) {
86 return _wfopen(UTF8ToUTF16(fname
).c_str(), ASCIIToUTF16(mode
).c_str());
88 return fopen(fname
, mode
);
92 base::FilePath
CreateFilePath(const std::string
& file_path
) {
94 return base::FilePath(UTF8ToUTF16(file_path
));
96 return base::FilePath(file_path
);
100 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix
[]
101 = FILE_PATH_LITERAL("leveldb-test-");
103 const char* PlatformFileErrorString(const ::base::PlatformFileError
& error
) {
105 case ::base::PLATFORM_FILE_ERROR_FAILED
:
106 return "No further details.";
107 case ::base::PLATFORM_FILE_ERROR_IN_USE
:
108 return "File currently in use.";
109 case ::base::PLATFORM_FILE_ERROR_EXISTS
:
110 return "File already exists.";
111 case ::base::PLATFORM_FILE_ERROR_NOT_FOUND
:
112 return "File not found.";
113 case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED
:
114 return "Access denied.";
115 case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED
:
116 return "Too many files open.";
117 case ::base::PLATFORM_FILE_ERROR_NO_MEMORY
:
118 return "Out of memory.";
119 case ::base::PLATFORM_FILE_ERROR_NO_SPACE
:
120 return "No space left on drive.";
121 case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
:
122 return "Not a directory.";
123 case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION
:
124 return "Invalid operation.";
125 case ::base::PLATFORM_FILE_ERROR_SECURITY
:
126 return "Security error.";
127 case ::base::PLATFORM_FILE_ERROR_ABORT
:
128 return "File operation aborted.";
129 case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE
:
130 return "The supplied path was not a file.";
131 case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY
:
132 return "The file was not empty.";
133 case ::base::PLATFORM_FILE_ERROR_INVALID_URL
:
134 return "Invalid URL.";
135 case ::base::PLATFORM_FILE_ERROR_IO
:
136 return "OS or hardware error.";
137 case ::base::PLATFORM_FILE_OK
:
139 case ::base::PLATFORM_FILE_ERROR_MAX
:
143 return "Unknown error.";
146 class ChromiumSequentialFile
: public SequentialFile
{
148 std::string filename_
;
150 const UMALogger
* uma_logger_
;
153 ChromiumSequentialFile(const std::string
& fname
, FILE* f
,
154 const UMALogger
* uma_logger
)
155 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) { }
156 virtual ~ChromiumSequentialFile() { fclose(file_
); }
158 virtual Status
Read(size_t n
, Slice
* result
, char* scratch
) {
160 size_t r
= fread_unlocked(scratch
, 1, n
, file_
);
161 *result
= Slice(scratch
, r
);
164 // We leave status as ok if we hit the end of the file
166 // A partial read with an error: return a non-ok status
167 s
= MakeIOError(filename_
, strerror(errno
), kSequentialFileRead
, errno
);
168 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
174 virtual Status
Skip(uint64_t n
) {
175 if (fseek(file_
, n
, SEEK_CUR
)) {
176 int saved_errno
= errno
;
177 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
179 filename_
, strerror(saved_errno
), kSequentialFileSkip
, saved_errno
);
185 class ChromiumRandomAccessFile
: public RandomAccessFile
{
187 std::string filename_
;
188 ::base::PlatformFile file_
;
189 const UMALogger
* uma_logger_
;
192 ChromiumRandomAccessFile(const std::string
& fname
, ::base::PlatformFile file
,
193 const UMALogger
* uma_logger
)
194 : filename_(fname
), file_(file
), uma_logger_(uma_logger
) { }
195 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_
); }
197 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
,
198 char* scratch
) const {
200 int r
= ::base::ReadPlatformFile(file_
, offset
, scratch
, n
);
201 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
203 // An error: return a non-ok status
205 filename_
, "Could not perform read", kRandomAccessFileRead
);
206 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
212 class ChromiumFileLock
: public FileLock
{
214 ::base::PlatformFile file_
;
220 Retrier(MethodID method
, RetrierProvider
* provider
)
221 : start_(base::TimeTicks::Now()),
222 limit_(start_
+ base::TimeDelta::FromMilliseconds(
223 provider
->MaxRetryTimeMillis())),
225 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
228 last_error_(base::PLATFORM_FILE_OK
),
229 provider_(provider
) {}
232 provider_
->GetRetryTimeHistogram(method_
)->AddTime(last_
- start_
);
233 if (last_error_
!= base::PLATFORM_FILE_OK
) {
234 DCHECK(last_error_
< 0);
235 provider_
->GetRecoveredFromErrorHistogram(method_
)->Add(-last_error_
);
239 bool ShouldKeepTrying(base::PlatformFileError last_error
) {
240 DCHECK_NE(last_error
, base::PLATFORM_FILE_OK
);
241 last_error_
= last_error
;
242 if (last_
< limit_
) {
243 base::PlatformThread::Sleep(time_to_sleep_
);
244 last_
= base::TimeTicks::Now();
252 base::TimeTicks start_
;
253 base::TimeTicks limit_
;
254 base::TimeTicks last_
;
255 base::TimeDelta time_to_sleep_
;
258 base::PlatformFileError last_error_
;
259 RetrierProvider
* provider_
;
262 class IDBEnv
: public ChromiumEnv
{
264 IDBEnv() : ChromiumEnv() {
265 name_
= "LevelDBEnv.IDB";
270 ::base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
272 ::base::LazyInstance
<ChromiumEnv
>::Leaky default_env
=
273 LAZY_INSTANCE_INITIALIZER
;
275 } // unnamed namespace
277 const char* MethodIDToString(MethodID method
) {
279 case kSequentialFileRead
:
280 return "SequentialFileRead";
281 case kSequentialFileSkip
:
282 return "SequentialFileSkip";
283 case kRandomAccessFileRead
:
284 return "RandomAccessFileRead";
285 case kWritableFileAppend
:
286 return "WritableFileAppend";
287 case kWritableFileClose
:
288 return "WritableFileClose";
289 case kWritableFileFlush
:
290 return "WritableFileFlush";
291 case kWritableFileSync
:
292 return "WritableFileSync";
293 case kNewSequentialFile
:
294 return "NewSequentialFile";
295 case kNewRandomAccessFile
:
296 return "NewRandomAccessFile";
297 case kNewWritableFile
:
298 return "NewWritableFile";
306 return "GetFileSize";
313 case kGetTestDirectory
:
314 return "GetTestDirectory";
320 return "GetChildren";
323 return "kNumEntries";
329 Status
MakeIOError(Slice filename
,
336 "%s (ChromeMethodErrno: %d::%s::%d)",
339 MethodIDToString(method
),
341 return Status::IOError(filename
, buf
);
344 Status
MakeIOError(Slice filename
,
347 base::PlatformFileError error
) {
352 "%s (ChromeMethodPFE: %d::%s::%d)",
355 MethodIDToString(method
),
357 return Status::IOError(filename
, buf
);
360 Status
MakeIOError(Slice filename
, const char* message
, MethodID method
) {
364 "%s (ChromeMethodOnly: %d::%s)",
367 MethodIDToString(method
));
368 return Status::IOError(filename
, buf
);
371 ErrorParsingResult
ParseMethodAndError(const char* string
,
372 MethodID
* method_param
,
375 if (RE2::PartialMatch(string
, "ChromeMethodOnly: (\\d+)", &method
)) {
376 *method_param
= static_cast<MethodID
>(method
);
379 if (RE2::PartialMatch(
380 string
, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
, error
)) {
382 *method_param
= static_cast<MethodID
>(method
);
383 return METHOD_AND_PFE
;
385 if (RE2::PartialMatch(
386 string
, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method
, error
)) {
387 *method_param
= static_cast<MethodID
>(method
);
388 return METHOD_AND_ERRNO
;
393 bool IndicatesDiskFull(leveldb::Status status
) {
396 leveldb_env::MethodID method
;
398 leveldb_env::ErrorParsingResult result
= leveldb_env::ParseMethodAndError(
399 status
.ToString().c_str(), &method
, &error
);
400 return (result
== leveldb_env::METHOD_AND_PFE
&&
401 static_cast<base::PlatformFileError
>(error
) ==
402 base::PLATFORM_FILE_ERROR_NO_SPACE
) ||
403 (result
== leveldb_env::METHOD_AND_ERRNO
&& error
== ENOSPC
);
406 bool IsIOError(leveldb::Status status
) {
407 leveldb_env::MethodID method
;
409 leveldb_env::ErrorParsingResult result
= leveldb_env::ParseMethodAndError(
410 status
.ToString().c_str(), &method
, &error
);
411 return result
!= leveldb_env::NONE
;
414 std::string
FilePathToString(const base::FilePath
& file_path
) {
416 return UTF16ToUTF8(file_path
.value());
418 return file_path
.value();
422 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
424 const UMALogger
* uma_logger
,
425 WriteTracker
* tracker
,
429 uma_logger_(uma_logger
),
432 make_backup_(make_backup
) {
433 base::FilePath path
= base::FilePath::FromUTF8Unsafe(fname
);
434 if (FilePathToString(path
.BaseName()).find("MANIFEST") == 0)
435 file_type_
= kManifest
;
436 else if (path
.MatchesExtension(table_extension
))
438 if (file_type_
!= kManifest
)
439 tracker_
->DidCreateNewFile(filename_
);
440 parent_dir_
= FilePathToString(CreateFilePath(fname
).DirName());
443 ChromiumWritableFile::~ChromiumWritableFile() {
445 // Ignoring any potential errors
450 Status
ChromiumWritableFile::SyncParent() {
453 TRACE_EVENT0("leveldb", "SyncParent");
456 HANDLE_EINTR(open(parent_dir_
.c_str(), O_RDONLY
));
458 int saved_errno
= errno
;
460 parent_dir_
, strerror(saved_errno
), kSyncParent
, saved_errno
);
462 if (HANDLE_EINTR(fsync(parent_fd
)) != 0) {
463 int saved_errno
= errno
;
465 parent_dir_
, strerror(saved_errno
), kSyncParent
, saved_errno
);
467 HANDLE_EINTR(close(parent_fd
));
472 Status
ChromiumWritableFile::Append(const Slice
& data
) {
473 if (file_type_
== kManifest
&& tracker_
->DoesDirNeedSync(filename_
)) {
474 Status s
= SyncParent();
477 tracker_
->DidSyncDir(filename_
);
480 size_t r
= fwrite_unlocked(data
.data(), 1, data
.size(), file_
);
481 if (r
!= data
.size()) {
482 int saved_errno
= errno
;
483 uma_logger_
->RecordOSError(kWritableFileAppend
, saved_errno
);
485 filename_
, strerror(saved_errno
), kWritableFileAppend
, saved_errno
);
490 Status
ChromiumWritableFile::Close() {
492 if (fclose(file_
) != 0) {
493 result
= MakeIOError(filename_
, strerror(errno
), kWritableFileClose
, errno
);
494 uma_logger_
->RecordErrorAt(kWritableFileClose
);
500 Status
ChromiumWritableFile::Flush() {
502 if (HANDLE_EINTR(fflush_unlocked(file_
))) {
503 int saved_errno
= errno
;
504 result
= MakeIOError(
505 filename_
, strerror(saved_errno
), kWritableFileFlush
, saved_errno
);
506 uma_logger_
->RecordOSError(kWritableFileFlush
, saved_errno
);
511 static bool MakeBackup(const std::string
& fname
) {
512 base::FilePath original_table_name
= CreateFilePath(fname
);
513 base::FilePath backup_table_name
=
514 original_table_name
.ReplaceExtension(backup_table_extension
);
515 return base::CopyFile(original_table_name
, backup_table_name
);
518 Status
ChromiumWritableFile::Sync() {
519 TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
523 if (HANDLE_EINTR(fflush_unlocked(file_
)))
525 // Sync even if fflush gave an error; perhaps the data actually got out,
526 // even though something went wrong.
527 if (fdatasync(fileno(file_
)) && !error
)
529 // Report the first error we found.
531 result
= MakeIOError(filename_
, strerror(error
), kWritableFileSync
, error
);
532 uma_logger_
->RecordErrorAt(kWritableFileSync
);
533 } else if (make_backup_
&& file_type_
== kTable
) {
534 bool success
= MakeBackup(filename_
);
535 uma_logger_
->RecordBackupResult(success
);
540 ChromiumEnv::ChromiumEnv()
541 : name_("LevelDBEnv"),
544 started_bgthread_(false),
545 kMaxRetryTimeMillis(1000) {
548 ChromiumEnv::~ChromiumEnv() {
549 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
550 // ensure that behavior isn't accidentally changed, but there's an instance in
551 // a unit test that is deleted.
554 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
555 SequentialFile
** result
) {
556 FILE* f
= fopen_internal(fname
.c_str(), "rb");
559 int saved_errno
= errno
;
560 RecordOSError(kNewSequentialFile
, saved_errno
);
562 fname
, strerror(saved_errno
), kNewSequentialFile
, saved_errno
);
564 *result
= new ChromiumSequentialFile(fname
, f
, this);
569 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
570 #if defined(OS_POSIX)
571 struct rlimit nofile
;
572 if (getrlimit(RLIMIT_NOFILE
, &nofile
))
574 GetMaxFDHistogram(type
)->Add(nofile
.rlim_cur
);
578 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
579 RandomAccessFile
** result
) {
580 int flags
= ::base::PLATFORM_FILE_READ
| ::base::PLATFORM_FILE_OPEN
;
582 ::base::PlatformFileError error_code
;
583 ::base::PlatformFile file
= ::base::CreatePlatformFile(
584 CreateFilePath(fname
), flags
, &created
, &error_code
);
585 if (error_code
== ::base::PLATFORM_FILE_OK
) {
586 *result
= new ChromiumRandomAccessFile(fname
, file
, this);
587 RecordOpenFilesLimit("Success");
590 if (error_code
== ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED
)
591 RecordOpenFilesLimit("TooManyOpened");
593 RecordOpenFilesLimit("OtherError");
595 RecordOSError(kNewRandomAccessFile
, error_code
);
596 return MakeIOError(fname
,
597 PlatformFileErrorString(error_code
),
598 kNewRandomAccessFile
,
602 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
603 WritableFile
** result
) {
605 FILE* f
= fopen_internal(fname
.c_str(), "wb");
607 int saved_errno
= errno
;
608 RecordErrorAt(kNewWritableFile
);
610 fname
, strerror(saved_errno
), kNewWritableFile
, saved_errno
);
612 *result
= new ChromiumWritableFile(fname
, f
, this, this, make_backup_
);
617 bool ChromiumEnv::FileExists(const std::string
& fname
) {
618 return ::base::PathExists(CreateFilePath(fname
));
621 base::FilePath
ChromiumEnv::RestoreFromBackup(const base::FilePath
& base_name
) {
622 base::FilePath table_name
=
623 base_name
.AddExtension(table_extension
);
624 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
626 std::string
uma_name(name_
);
627 uma_name
.append(".TableRestore");
628 base::BooleanHistogram::FactoryGet(
629 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
633 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
634 std::vector
<std::string
>* result
) {
635 std::set
<base::FilePath
> tables_found
;
636 std::set
<base::FilePath
> backups_found
;
637 for (std::vector
<std::string
>::iterator it
= result
->begin();
640 base::FilePath current
= CreateFilePath(*it
);
641 if (current
.MatchesExtension(table_extension
))
642 tables_found
.insert(current
.RemoveExtension());
643 if (current
.MatchesExtension(backup_table_extension
))
644 backups_found
.insert(current
.RemoveExtension());
646 std::set
<base::FilePath
> backups_only
;
647 std::set_difference(backups_found
.begin(),
649 tables_found
.begin(),
651 std::inserter(backups_only
, backups_only
.begin()));
652 if (backups_only
.size()) {
653 std::string
uma_name(name_
);
654 uma_name
.append(".MissingFiles");
655 int num_missing_files
=
656 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
657 base::Histogram::FactoryGet(uma_name
,
661 base::Histogram::kUmaTargetedHistogramFlag
)
662 ->Add(num_missing_files
);
664 base::FilePath dir_filepath
= base::FilePath::FromUTF8Unsafe(dir
);
665 for (std::set
<base::FilePath
>::iterator it
= backups_only
.begin();
666 it
!= backups_only
.end();
668 base::FilePath restored_table_name
=
669 RestoreFromBackup(dir_filepath
.Append(*it
));
670 result
->push_back(FilePathToString(restored_table_name
.BaseName()));
676 static base::PlatformFileError
GetDirectoryEntries(
677 const base::FilePath
& dir_param
,
678 std::vector
<base::FilePath
>* result
) {
680 base::FilePath dir_filepath
= dir_param
.Append(FILE_PATH_LITERAL("*"));
681 WIN32_FIND_DATA find_data
;
682 HANDLE find_handle
= FindFirstFile(dir_filepath
.value().c_str(), &find_data
);
683 if (find_handle
== INVALID_HANDLE_VALUE
) {
684 DWORD last_error
= GetLastError();
685 if (last_error
== ERROR_FILE_NOT_FOUND
)
686 return base::PLATFORM_FILE_OK
;
687 return base::LastErrorToPlatformFileError(last_error
);
690 base::FilePath
filepath(find_data
.cFileName
);
691 base::FilePath::StringType basename
= filepath
.BaseName().value();
692 if (basename
== FILE_PATH_LITERAL(".") ||
693 basename
== FILE_PATH_LITERAL(".."))
695 result
->push_back(filepath
.BaseName());
696 } while (FindNextFile(find_handle
, &find_data
));
697 DWORD last_error
= GetLastError();
698 base::PlatformFileError return_value
= base::PLATFORM_FILE_OK
;
699 if (last_error
!= ERROR_NO_MORE_FILES
)
700 return_value
= base::LastErrorToPlatformFileError(last_error
);
701 FindClose(find_handle
);
705 static base::PlatformFileError
GetDirectoryEntries(
706 const base::FilePath
& dir_filepath
,
707 std::vector
<base::FilePath
>* result
) {
708 const std::string dir_string
= FilePathToString(dir_filepath
);
710 DIR* dir
= opendir(dir_string
.c_str());
712 return base::ErrnoToPlatformFileError(errno
);
713 struct dirent dent_buf
;
716 while ((readdir_result
= readdir_r(dir
, &dent_buf
, &dent
)) == 0 && dent
)
717 result
->push_back(CreateFilePath(dent
->d_name
));
718 int saved_errno
= errno
;
720 if (readdir_result
!= 0)
721 return base::ErrnoToPlatformFileError(saved_errno
);
722 return base::PLATFORM_FILE_OK
;
727 Status
ChromiumEnv::GetChildren(const std::string
& dir_string
,
728 std::vector
<std::string
>* result
) {
729 std::vector
<base::FilePath
> entries
;
730 base::PlatformFileError error
=
731 GetDirectoryEntries(CreateFilePath(dir_string
), &entries
);
732 if (error
!= base::PLATFORM_FILE_OK
) {
733 RecordOSError(kGetChildren
, error
);
735 dir_string
, "Could not open/read directory", kGetChildren
, error
);
737 for (std::vector
<base::FilePath
>::iterator it
= entries
.begin();
740 result
->push_back(FilePathToString(*it
));
744 RestoreIfNecessary(dir_string
, result
);
748 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
750 base::FilePath fname_filepath
= CreateFilePath(fname
);
751 // TODO(jorlow): Should we assert this is a file?
752 if (!::base::DeleteFile(fname_filepath
, false)) {
753 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
754 RecordErrorAt(kDeleteFile
);
756 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
757 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
763 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
765 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
766 Retrier
retrier(kCreateDir
, this);
768 if (::file_util::CreateDirectoryAndGetError(CreateFilePath(name
), &error
))
770 } while (retrier
.ShouldKeepTrying(error
));
771 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
772 RecordOSError(kCreateDir
, error
);
776 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
778 // TODO(jorlow): Should we assert this is a directory?
779 if (!::base::DeleteFile(CreateFilePath(name
), false)) {
780 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
781 RecordErrorAt(kDeleteDir
);
786 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
789 if (!::file_util::GetFileSize(CreateFilePath(fname
), &signed_size
)) {
791 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
792 RecordErrorAt(kGetFileSize
);
794 *size
= static_cast<uint64_t>(signed_size
);
799 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
801 base::FilePath src_file_path
= CreateFilePath(src
);
802 if (!::base::PathExists(src_file_path
))
804 base::FilePath destination
= CreateFilePath(dst
);
806 Retrier
retrier(kRenameFile
, this);
807 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
809 if (base::ReplaceFile(src_file_path
, destination
, &error
))
811 } while (retrier
.ShouldKeepTrying(error
));
813 DCHECK(error
!= base::PLATFORM_FILE_OK
);
814 RecordOSError(kRenameFile
, error
);
818 "Could not rename file: %s",
819 PlatformFileErrorString(error
));
820 return MakeIOError(src
, buf
, kRenameFile
, error
);
823 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
826 int flags
= ::base::PLATFORM_FILE_OPEN_ALWAYS
|
827 ::base::PLATFORM_FILE_READ
|
828 ::base::PLATFORM_FILE_WRITE
;
830 ::base::PlatformFileError error_code
;
831 ::base::PlatformFile file
;
832 Retrier
retrier(kLockFile
, this);
834 file
= ::base::CreatePlatformFile(
835 CreateFilePath(fname
), flags
, &created
, &error_code
);
836 } while (error_code
!= ::base::PLATFORM_FILE_OK
&&
837 retrier
.ShouldKeepTrying(error_code
));
839 if (error_code
== ::base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
840 ::base::FilePath parent
= CreateFilePath(fname
).DirName();
841 ::base::FilePath last_parent
;
842 int num_missing_ancestors
= 0;
844 if (base::DirectoryExists(parent
))
846 ++num_missing_ancestors
;
847 last_parent
= parent
;
848 parent
= parent
.DirName();
849 } while (parent
!= last_parent
);
850 RecordLockFileAncestors(num_missing_ancestors
);
853 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
854 result
= MakeIOError(
855 fname
, PlatformFileErrorString(error_code
), kLockFile
, error_code
);
856 RecordOSError(kLockFile
, error_code
);
860 if (!locks_
.Insert(fname
)) {
861 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
862 ::base::ClosePlatformFile(file
);
866 Retrier lock_retrier
= Retrier(kLockFile
, this);
868 error_code
= ::base::LockPlatformFile(file
);
869 } while (error_code
!= ::base::PLATFORM_FILE_OK
&&
870 retrier
.ShouldKeepTrying(error_code
));
872 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
873 ::base::ClosePlatformFile(file
);
874 locks_
.Remove(fname
);
875 result
= MakeIOError(
876 fname
, PlatformFileErrorString(error_code
), kLockFile
, error_code
);
877 RecordOSError(kLockFile
, error_code
);
881 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
882 my_lock
->file_
= file
;
883 my_lock
->name_
= fname
;
888 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
889 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
892 ::base::PlatformFileError error_code
=
893 ::base::UnlockPlatformFile(my_lock
->file_
);
894 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
896 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
897 RecordOSError(kUnlockFile
, error_code
);
898 ::base::ClosePlatformFile(my_lock
->file_
);
899 } else if (!::base::ClosePlatformFile(my_lock
->file_
)) {
901 MakeIOError(my_lock
->name_
, "Could not close lock file.", kUnlockFile
);
902 RecordErrorAt(kUnlockFile
);
904 bool removed
= locks_
.Remove(my_lock
->name_
);
910 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
912 if (test_directory_
.empty()) {
913 if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
916 RecordErrorAt(kGetTestDirectory
);
918 "Could not create temp directory.", "", kGetTestDirectory
);
921 *path
= FilePathToString(test_directory_
);
926 Status
ChromiumEnv::NewLogger(const std::string
& fname
, Logger
** result
) {
927 FILE* f
= fopen_internal(fname
.c_str(), "w");
930 int saved_errno
= errno
;
931 RecordOSError(kNewLogger
, saved_errno
);
932 return MakeIOError(fname
, strerror(saved_errno
), kNewLogger
, saved_errno
);
934 *result
= new ChromiumLogger(f
);
939 uint64_t ChromiumEnv::NowMicros() {
940 return ::base::TimeTicks::Now().ToInternalValue();
943 void ChromiumEnv::SleepForMicroseconds(int micros
) {
944 // Round up to the next millisecond.
945 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros
));
948 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
949 GetMethodIOErrorHistogram()->Add(method
);
952 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
953 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
956 void ChromiumEnv::RecordOSError(MethodID method
,
957 base::PlatformFileError error
) const {
959 RecordErrorAt(method
);
960 GetOSErrorHistogram(method
, -base::PLATFORM_FILE_ERROR_MAX
)->Add(-error
);
963 void ChromiumEnv::RecordOSError(MethodID method
, int error
) const {
965 RecordErrorAt(method
);
966 GetOSErrorHistogram(method
, ERANGE
+ 1)->Add(error
);
969 void ChromiumEnv::RecordBackupResult(bool result
) const {
970 std::string
uma_name(name_
);
971 uma_name
.append(".TableBackup");
972 base::BooleanHistogram::FactoryGet(
973 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
976 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
978 std::string
uma_name(name_
);
979 // TODO(dgrogan): This is probably not the best way to concatenate strings.
980 uma_name
.append(".IOError.").append(MethodIDToString(method
));
981 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
982 base::Histogram::kUmaTargetedHistogramFlag
);
985 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
986 std::string
uma_name(name_
);
987 uma_name
.append(".IOError");
988 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
989 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
992 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
993 const std::string
& type
) const {
994 std::string
uma_name(name_
);
995 uma_name
.append(".MaxFDs.").append(type
);
996 // These numbers make each bucket twice as large as the previous bucket.
997 const int kFirstEntry
= 1;
998 const int kLastEntry
= 65536;
999 const int kNumBuckets
= 18;
1000 return base::Histogram::FactoryGet(
1001 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
1002 base::Histogram::kUmaTargetedHistogramFlag
);
1005 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
1006 std::string
uma_name(name_
);
1007 uma_name
.append(".LockFileAncestorsNotFound");
1009 const int kMax
= 10;
1010 const int kNumBuckets
= 11;
1011 return base::LinearHistogram::FactoryGet(
1012 uma_name
, kMin
, kMax
, kNumBuckets
,
1013 base::Histogram::kUmaTargetedHistogramFlag
);
1016 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
1017 std::string
uma_name(name_
);
1018 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1019 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
1021 const int kBucketSizeMillis
= 25;
1022 // Add 2, 1 for each of the buckets <1 and >max.
1023 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
1024 return base::Histogram::FactoryTimeGet(
1025 uma_name
, base::TimeDelta::FromMilliseconds(1),
1026 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
1028 base::Histogram::kUmaTargetedHistogramFlag
);
1031 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
1032 MethodID method
) const {
1033 std::string
uma_name(name_
);
1034 uma_name
.append(".RetryRecoveredFromErrorIn")
1035 .append(MethodIDToString(method
));
1036 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
1037 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1040 class Thread
: public ::base::PlatformThread::Delegate
{
1042 Thread(void (*function
)(void* arg
), void* arg
)
1043 : function_(function
), arg_(arg
) {
1044 ::base::PlatformThreadHandle handle
;
1045 bool success
= ::base::PlatformThread::Create(0, this, &handle
);
1048 virtual ~Thread() {}
1049 virtual void ThreadMain() {
1055 void (*function_
)(void* arg
);
1059 void ChromiumEnv::Schedule(void (*function
)(void*), void* arg
) {
1062 // Start background thread if necessary
1063 if (!started_bgthread_
) {
1064 started_bgthread_
= true;
1065 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1068 // If the queue is currently empty, the background thread may currently be
1070 if (queue_
.empty()) {
1074 // Add to priority queue
1075 queue_
.push_back(BGItem());
1076 queue_
.back().function
= function
;
1077 queue_
.back().arg
= arg
;
1082 void ChromiumEnv::BGThread() {
1083 base::PlatformThread::SetName(name_
.c_str());
1086 // Wait until there is an item that is ready to run
1088 while (queue_
.empty()) {
1092 void (*function
)(void*) = queue_
.front().function
;
1093 void* arg
= queue_
.front().arg
;
1097 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1102 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1103 new Thread(function
, arg
); // Will self-delete.
1106 static std::string
GetDirName(const std::string
& filename
) {
1107 base::FilePath file
= base::FilePath::FromUTF8Unsafe(filename
);
1108 return FilePathToString(file
.DirName());
1111 void ChromiumEnv::DidCreateNewFile(const std::string
& filename
) {
1112 base::AutoLock
auto_lock(map_lock_
);
1113 needs_sync_map_
[GetDirName(filename
)] = true;
1116 bool ChromiumEnv::DoesDirNeedSync(const std::string
& filename
) {
1117 base::AutoLock
auto_lock(map_lock_
);
1118 return needs_sync_map_
.find(GetDirName(filename
)) != needs_sync_map_
.end();
1121 void ChromiumEnv::DidSyncDir(const std::string
& filename
) {
1122 base::AutoLock
auto_lock(map_lock_
);
1123 needs_sync_map_
.erase(GetDirName(filename
));
1126 } // namespace leveldb_env
1131 return leveldb_env::idb_env
.Pointer();
1134 Env
* Env::Default() {
1135 return leveldb_env::default_env
.Pointer();
1138 } // namespace leveldb