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.
11 #include "base/at_exit.h"
12 #include "base/debug/trace_event.h"
13 #include "base/file_util.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/files/file_path.h"
16 #include "base/lazy_instance.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/metrics/histogram.h"
20 #include "base/platform_file.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/synchronization/lock.h"
24 #include "base/sys_info.h"
25 #include "base/threading/platform_thread.h"
26 #include "base/threading/thread.h"
27 #include "chromium_logger.h"
28 #include "env_chromium.h"
29 #include "leveldb/env.h"
30 #include "leveldb/slice.h"
31 #include "port/port.h"
32 #include "third_party/re2/re2/re2.h"
33 #include "util/logging.h"
37 #include "base/win/win_util.h"
43 #include <sys/resource.h>
47 using namespace leveldb
;
49 namespace leveldb_env
{
53 const base::FilePath::CharType backup_table_extension
[] =
54 FILE_PATH_LITERAL(".bak");
55 const base::FilePath::CharType table_extension
[] = FILE_PATH_LITERAL(".ldb");
57 #if (defined(OS_POSIX) && !defined(OS_LINUX)) || defined(OS_WIN)
58 // The following are glibc-specific
60 size_t fread_unlocked(void *ptr
, size_t size
, size_t n
, FILE *file
) {
61 return fread(ptr
, size
, n
, file
);
64 size_t fwrite_unlocked(const void *ptr
, size_t size
, size_t n
, FILE *file
) {
65 return fwrite(ptr
, size
, n
, file
);
68 int fflush_unlocked(FILE *file
) {
72 #if !defined(OS_ANDROID)
73 int fdatasync(int fildes
) {
75 return _commit(fildes
);
77 return HANDLE_EINTR(fsync(fildes
));
84 // Wide-char safe fopen wrapper.
85 FILE* fopen_internal(const char* fname
, const char* mode
) {
87 return _wfopen(base::UTF8ToUTF16(fname
).c_str(),
88 base::ASCIIToUTF16(mode
).c_str());
90 return fopen(fname
, mode
);
94 base::FilePath
CreateFilePath(const std::string
& file_path
) {
96 return base::FilePath(base::UTF8ToUTF16(file_path
));
98 return base::FilePath(file_path
);
102 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix
[]
103 = FILE_PATH_LITERAL("leveldb-test-");
105 const char* PlatformFileErrorString(const ::base::PlatformFileError
& error
) {
107 case ::base::PLATFORM_FILE_ERROR_FAILED
:
108 return "No further details.";
109 case ::base::PLATFORM_FILE_ERROR_IN_USE
:
110 return "File currently in use.";
111 case ::base::PLATFORM_FILE_ERROR_EXISTS
:
112 return "File already exists.";
113 case ::base::PLATFORM_FILE_ERROR_NOT_FOUND
:
114 return "File not found.";
115 case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED
:
116 return "Access denied.";
117 case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED
:
118 return "Too many files open.";
119 case ::base::PLATFORM_FILE_ERROR_NO_MEMORY
:
120 return "Out of memory.";
121 case ::base::PLATFORM_FILE_ERROR_NO_SPACE
:
122 return "No space left on drive.";
123 case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
:
124 return "Not a directory.";
125 case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION
:
126 return "Invalid operation.";
127 case ::base::PLATFORM_FILE_ERROR_SECURITY
:
128 return "Security error.";
129 case ::base::PLATFORM_FILE_ERROR_ABORT
:
130 return "File operation aborted.";
131 case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE
:
132 return "The supplied path was not a file.";
133 case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY
:
134 return "The file was not empty.";
135 case ::base::PLATFORM_FILE_ERROR_INVALID_URL
:
136 return "Invalid URL.";
137 case ::base::PLATFORM_FILE_ERROR_IO
:
138 return "OS or hardware error.";
139 case ::base::PLATFORM_FILE_OK
:
141 case ::base::PLATFORM_FILE_ERROR_MAX
:
145 return "Unknown error.";
148 class ChromiumSequentialFile
: public SequentialFile
{
150 std::string filename_
;
152 const UMALogger
* uma_logger_
;
155 ChromiumSequentialFile(const std::string
& fname
, FILE* f
,
156 const UMALogger
* uma_logger
)
157 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) { }
158 virtual ~ChromiumSequentialFile() { fclose(file_
); }
160 virtual Status
Read(size_t n
, Slice
* result
, char* scratch
) {
162 size_t r
= fread_unlocked(scratch
, 1, n
, file_
);
163 *result
= Slice(scratch
, r
);
166 // We leave status as ok if we hit the end of the file
168 // A partial read with an error: return a non-ok status
169 s
= MakeIOError(filename_
, strerror(errno
), kSequentialFileRead
, errno
);
170 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
176 virtual Status
Skip(uint64_t n
) {
177 if (fseek(file_
, n
, SEEK_CUR
)) {
178 int saved_errno
= errno
;
179 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
181 filename_
, strerror(saved_errno
), kSequentialFileSkip
, saved_errno
);
187 class ChromiumRandomAccessFile
: public RandomAccessFile
{
189 std::string filename_
;
190 ::base::PlatformFile file_
;
191 const UMALogger
* uma_logger_
;
194 ChromiumRandomAccessFile(const std::string
& fname
, ::base::PlatformFile file
,
195 const UMALogger
* uma_logger
)
196 : filename_(fname
), file_(file
), uma_logger_(uma_logger
) { }
197 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_
); }
199 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
,
200 char* scratch
) const {
202 int r
= ::base::ReadPlatformFile(file_
, offset
, scratch
, n
);
203 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
205 // An error: return a non-ok status
207 filename_
, "Could not perform read", kRandomAccessFileRead
);
208 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
214 class ChromiumFileLock
: public FileLock
{
216 ::base::PlatformFile file_
;
222 Retrier(MethodID method
, RetrierProvider
* provider
)
223 : start_(base::TimeTicks::Now()),
224 limit_(start_
+ base::TimeDelta::FromMilliseconds(
225 provider
->MaxRetryTimeMillis())),
227 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
230 last_error_(base::PLATFORM_FILE_OK
),
231 provider_(provider
) {}
234 provider_
->GetRetryTimeHistogram(method_
)->AddTime(last_
- start_
);
235 if (last_error_
!= base::PLATFORM_FILE_OK
) {
236 DCHECK(last_error_
< 0);
237 provider_
->GetRecoveredFromErrorHistogram(method_
)->Add(-last_error_
);
241 bool ShouldKeepTrying(base::PlatformFileError last_error
) {
242 DCHECK_NE(last_error
, base::PLATFORM_FILE_OK
);
243 last_error_
= last_error
;
244 if (last_
< limit_
) {
245 base::PlatformThread::Sleep(time_to_sleep_
);
246 last_
= base::TimeTicks::Now();
254 base::TimeTicks start_
;
255 base::TimeTicks limit_
;
256 base::TimeTicks last_
;
257 base::TimeDelta time_to_sleep_
;
260 base::PlatformFileError last_error_
;
261 RetrierProvider
* provider_
;
264 class IDBEnv
: public ChromiumEnv
{
266 IDBEnv() : ChromiumEnv() {
267 name_
= "LevelDBEnv.IDB";
272 ::base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
274 ::base::LazyInstance
<ChromiumEnv
>::Leaky default_env
=
275 LAZY_INSTANCE_INITIALIZER
;
277 } // unnamed namespace
279 const char* MethodIDToString(MethodID method
) {
281 case kSequentialFileRead
:
282 return "SequentialFileRead";
283 case kSequentialFileSkip
:
284 return "SequentialFileSkip";
285 case kRandomAccessFileRead
:
286 return "RandomAccessFileRead";
287 case kWritableFileAppend
:
288 return "WritableFileAppend";
289 case kWritableFileClose
:
290 return "WritableFileClose";
291 case kWritableFileFlush
:
292 return "WritableFileFlush";
293 case kWritableFileSync
:
294 return "WritableFileSync";
295 case kNewSequentialFile
:
296 return "NewSequentialFile";
297 case kNewRandomAccessFile
:
298 return "NewRandomAccessFile";
299 case kNewWritableFile
:
300 return "NewWritableFile";
308 return "GetFileSize";
315 case kGetTestDirectory
:
316 return "GetTestDirectory";
322 return "GetChildren";
325 return "kNumEntries";
331 Status
MakeIOError(Slice filename
,
338 "%s (ChromeMethodErrno: %d::%s::%d)",
341 MethodIDToString(method
),
343 return Status::IOError(filename
, buf
);
346 Status
MakeIOError(Slice filename
,
349 base::PlatformFileError error
) {
354 "%s (ChromeMethodPFE: %d::%s::%d)",
357 MethodIDToString(method
),
359 return Status::IOError(filename
, buf
);
362 Status
MakeIOError(Slice filename
, const char* message
, MethodID method
) {
366 "%s (ChromeMethodOnly: %d::%s)",
369 MethodIDToString(method
));
370 return Status::IOError(filename
, buf
);
373 ErrorParsingResult
ParseMethodAndError(const char* string
,
374 MethodID
* method_param
,
377 if (RE2::PartialMatch(string
, "ChromeMethodOnly: (\\d+)", &method
)) {
378 *method_param
= static_cast<MethodID
>(method
);
381 if (RE2::PartialMatch(
382 string
, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
, error
)) {
384 *method_param
= static_cast<MethodID
>(method
);
385 return METHOD_AND_PFE
;
387 if (RE2::PartialMatch(
388 string
, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method
, error
)) {
389 *method_param
= static_cast<MethodID
>(method
);
390 return METHOD_AND_ERRNO
;
395 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
396 // change the order because indices into this array have been recorded in uma
398 const char* patterns
[] = {
400 "log record too small",
401 "corrupted internal key",
403 "missing start of fragmented record",
404 "error in middle of record",
405 "unknown record type",
406 "truncated record at end",
409 "FileReader invoked with unexpected value",
411 "CURRENT file does not end with newline",
412 "no meta-nextfile entry",
413 "no meta-lognumber entry",
414 "no last-sequence-number entry",
415 "malformed WriteBatch",
416 "bad WriteBatch Put",
417 "bad WriteBatch Delete",
418 "unknown WriteBatch tag",
419 "WriteBatch has wrong count",
420 "bad entry in block",
421 "bad block contents",
423 "truncated block read",
424 "block checksum mismatch",
426 "corrupted compressed block contents",
432 // Returns 1-based index into the above array or 0 if nothing matches.
433 int GetCorruptionCode(const leveldb::Status
& status
) {
434 DCHECK(!status
.IsIOError());
435 DCHECK(!status
.ok());
436 const int kOtherError
= 0;
437 int error
= kOtherError
;
438 const std::string
& str_error
= status
.ToString();
439 const size_t kNumPatterns
= arraysize(patterns
);
440 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
441 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
449 int GetNumCorruptionCodes() {
450 // + 1 for the "other" error that is returned when a corruption message
451 // doesn't match any of the patterns.
452 return arraysize(patterns
) + 1;
455 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
456 int code
= GetCorruptionCode(status
);
458 return "Unknown corruption";
459 return patterns
[code
- 1];
462 bool IndicatesDiskFull(const leveldb::Status
& status
) {
465 leveldb_env::MethodID method
;
467 leveldb_env::ErrorParsingResult result
= leveldb_env::ParseMethodAndError(
468 status
.ToString().c_str(), &method
, &error
);
469 return (result
== leveldb_env::METHOD_AND_PFE
&&
470 static_cast<base::PlatformFileError
>(error
) ==
471 base::PLATFORM_FILE_ERROR_NO_SPACE
) ||
472 (result
== leveldb_env::METHOD_AND_ERRNO
&& error
== ENOSPC
);
475 bool IsIOError(const leveldb::Status
& status
) {
476 leveldb_env::MethodID method
;
478 leveldb_env::ErrorParsingResult result
= leveldb_env::ParseMethodAndError(
479 status
.ToString().c_str(), &method
, &error
);
480 return result
!= leveldb_env::NONE
;
483 bool IsCorruption(const leveldb::Status
& status
) {
484 // LevelDB returns InvalidArgument when an sst file is truncated but there is
485 // no IsInvalidArgument() accessor defined.
486 return status
.IsCorruption() || (!status
.ok() && !IsIOError(status
));
489 std::string
FilePathToString(const base::FilePath
& file_path
) {
491 return base::UTF16ToUTF8(file_path
.value());
493 return file_path
.value();
497 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
499 const UMALogger
* uma_logger
,
500 WriteTracker
* tracker
,
504 uma_logger_(uma_logger
),
507 make_backup_(make_backup
) {
508 base::FilePath path
= base::FilePath::FromUTF8Unsafe(fname
);
509 if (FilePathToString(path
.BaseName()).find("MANIFEST") == 0)
510 file_type_
= kManifest
;
511 else if (path
.MatchesExtension(table_extension
))
513 if (file_type_
!= kManifest
)
514 tracker_
->DidCreateNewFile(filename_
);
515 parent_dir_
= FilePathToString(CreateFilePath(fname
).DirName());
518 ChromiumWritableFile::~ChromiumWritableFile() {
520 // Ignoring any potential errors
525 Status
ChromiumWritableFile::SyncParent() {
528 TRACE_EVENT0("leveldb", "SyncParent");
531 HANDLE_EINTR(open(parent_dir_
.c_str(), O_RDONLY
));
533 int saved_errno
= errno
;
535 parent_dir_
, strerror(saved_errno
), kSyncParent
, saved_errno
);
537 if (HANDLE_EINTR(fsync(parent_fd
)) != 0) {
538 int saved_errno
= errno
;
540 parent_dir_
, strerror(saved_errno
), kSyncParent
, saved_errno
);
547 Status
ChromiumWritableFile::Append(const Slice
& data
) {
548 if (file_type_
== kManifest
&& tracker_
->DoesDirNeedSync(filename_
)) {
549 Status s
= SyncParent();
552 tracker_
->DidSyncDir(filename_
);
555 size_t r
= fwrite_unlocked(data
.data(), 1, data
.size(), file_
);
556 if (r
!= data
.size()) {
557 int saved_errno
= errno
;
558 uma_logger_
->RecordOSError(kWritableFileAppend
, saved_errno
);
560 filename_
, strerror(saved_errno
), kWritableFileAppend
, saved_errno
);
565 Status
ChromiumWritableFile::Close() {
567 if (fclose(file_
) != 0) {
568 result
= MakeIOError(filename_
, strerror(errno
), kWritableFileClose
, errno
);
569 uma_logger_
->RecordErrorAt(kWritableFileClose
);
575 Status
ChromiumWritableFile::Flush() {
577 if (HANDLE_EINTR(fflush_unlocked(file_
))) {
578 int saved_errno
= errno
;
579 result
= MakeIOError(
580 filename_
, strerror(saved_errno
), kWritableFileFlush
, saved_errno
);
581 uma_logger_
->RecordOSError(kWritableFileFlush
, saved_errno
);
586 static bool MakeBackup(const std::string
& fname
) {
587 base::FilePath original_table_name
= CreateFilePath(fname
);
588 base::FilePath backup_table_name
=
589 original_table_name
.ReplaceExtension(backup_table_extension
);
590 return base::CopyFile(original_table_name
, backup_table_name
);
593 Status
ChromiumWritableFile::Sync() {
594 TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
598 if (HANDLE_EINTR(fflush_unlocked(file_
)))
600 // Sync even if fflush gave an error; perhaps the data actually got out,
601 // even though something went wrong.
602 if (fdatasync(fileno(file_
)) && !error
)
604 // Report the first error we found.
606 result
= MakeIOError(filename_
, strerror(error
), kWritableFileSync
, error
);
607 uma_logger_
->RecordErrorAt(kWritableFileSync
);
608 } else if (make_backup_
&& file_type_
== kTable
) {
609 bool success
= MakeBackup(filename_
);
610 uma_logger_
->RecordBackupResult(success
);
615 ChromiumEnv::ChromiumEnv()
616 : name_("LevelDBEnv"),
619 started_bgthread_(false),
620 kMaxRetryTimeMillis(1000) {
623 ChromiumEnv::~ChromiumEnv() {
624 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
625 // ensure that behavior isn't accidentally changed, but there's an instance in
626 // a unit test that is deleted.
629 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
630 SequentialFile
** result
) {
631 FILE* f
= fopen_internal(fname
.c_str(), "rb");
634 int saved_errno
= errno
;
635 RecordOSError(kNewSequentialFile
, saved_errno
);
637 fname
, strerror(saved_errno
), kNewSequentialFile
, saved_errno
);
639 *result
= new ChromiumSequentialFile(fname
, f
, this);
644 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
645 #if defined(OS_POSIX)
646 struct rlimit nofile
;
647 if (getrlimit(RLIMIT_NOFILE
, &nofile
))
649 GetMaxFDHistogram(type
)->Add(nofile
.rlim_cur
);
653 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
654 RandomAccessFile
** result
) {
655 int flags
= ::base::PLATFORM_FILE_READ
| ::base::PLATFORM_FILE_OPEN
;
657 ::base::PlatformFileError error_code
;
658 ::base::PlatformFile file
= ::base::CreatePlatformFile(
659 CreateFilePath(fname
), flags
, &created
, &error_code
);
660 if (error_code
== ::base::PLATFORM_FILE_OK
) {
661 *result
= new ChromiumRandomAccessFile(fname
, file
, this);
662 RecordOpenFilesLimit("Success");
665 if (error_code
== ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED
)
666 RecordOpenFilesLimit("TooManyOpened");
668 RecordOpenFilesLimit("OtherError");
670 RecordOSError(kNewRandomAccessFile
, error_code
);
671 return MakeIOError(fname
,
672 PlatformFileErrorString(error_code
),
673 kNewRandomAccessFile
,
677 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
678 WritableFile
** result
) {
680 FILE* f
= fopen_internal(fname
.c_str(), "wb");
682 int saved_errno
= errno
;
683 RecordErrorAt(kNewWritableFile
);
685 fname
, strerror(saved_errno
), kNewWritableFile
, saved_errno
);
687 *result
= new ChromiumWritableFile(fname
, f
, this, this, make_backup_
);
692 bool ChromiumEnv::FileExists(const std::string
& fname
) {
693 return ::base::PathExists(CreateFilePath(fname
));
696 base::FilePath
ChromiumEnv::RestoreFromBackup(const base::FilePath
& base_name
) {
697 base::FilePath table_name
=
698 base_name
.AddExtension(table_extension
);
699 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
701 std::string
uma_name(name_
);
702 uma_name
.append(".TableRestore");
703 base::BooleanHistogram::FactoryGet(
704 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
708 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
709 std::vector
<std::string
>* result
) {
710 std::set
<base::FilePath
> tables_found
;
711 std::set
<base::FilePath
> backups_found
;
712 for (std::vector
<std::string
>::iterator it
= result
->begin();
715 base::FilePath current
= CreateFilePath(*it
);
716 if (current
.MatchesExtension(table_extension
))
717 tables_found
.insert(current
.RemoveExtension());
718 if (current
.MatchesExtension(backup_table_extension
))
719 backups_found
.insert(current
.RemoveExtension());
721 std::set
<base::FilePath
> backups_only
;
722 std::set_difference(backups_found
.begin(),
724 tables_found
.begin(),
726 std::inserter(backups_only
, backups_only
.begin()));
727 if (backups_only
.size()) {
728 std::string
uma_name(name_
);
729 uma_name
.append(".MissingFiles");
730 int num_missing_files
=
731 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
732 base::Histogram::FactoryGet(uma_name
,
736 base::Histogram::kUmaTargetedHistogramFlag
)
737 ->Add(num_missing_files
);
739 base::FilePath dir_filepath
= base::FilePath::FromUTF8Unsafe(dir
);
740 for (std::set
<base::FilePath
>::iterator it
= backups_only
.begin();
741 it
!= backups_only
.end();
743 base::FilePath restored_table_name
=
744 RestoreFromBackup(dir_filepath
.Append(*it
));
745 result
->push_back(FilePathToString(restored_table_name
.BaseName()));
751 static base::PlatformFileError
GetDirectoryEntries(
752 const base::FilePath
& dir_param
,
753 std::vector
<base::FilePath
>* result
) {
755 base::FilePath dir_filepath
= dir_param
.Append(FILE_PATH_LITERAL("*"));
756 WIN32_FIND_DATA find_data
;
757 HANDLE find_handle
= FindFirstFile(dir_filepath
.value().c_str(), &find_data
);
758 if (find_handle
== INVALID_HANDLE_VALUE
) {
759 DWORD last_error
= GetLastError();
760 if (last_error
== ERROR_FILE_NOT_FOUND
)
761 return base::PLATFORM_FILE_OK
;
762 return base::LastErrorToPlatformFileError(last_error
);
765 base::FilePath
filepath(find_data
.cFileName
);
766 base::FilePath::StringType basename
= filepath
.BaseName().value();
767 if (basename
== FILE_PATH_LITERAL(".") ||
768 basename
== FILE_PATH_LITERAL(".."))
770 result
->push_back(filepath
.BaseName());
771 } while (FindNextFile(find_handle
, &find_data
));
772 DWORD last_error
= GetLastError();
773 base::PlatformFileError return_value
= base::PLATFORM_FILE_OK
;
774 if (last_error
!= ERROR_NO_MORE_FILES
)
775 return_value
= base::LastErrorToPlatformFileError(last_error
);
776 FindClose(find_handle
);
780 static base::PlatformFileError
GetDirectoryEntries(
781 const base::FilePath
& dir_filepath
,
782 std::vector
<base::FilePath
>* result
) {
783 const std::string dir_string
= FilePathToString(dir_filepath
);
785 DIR* dir
= opendir(dir_string
.c_str());
787 return base::ErrnoToPlatformFileError(errno
);
788 struct dirent dent_buf
;
791 while ((readdir_result
= readdir_r(dir
, &dent_buf
, &dent
)) == 0 && dent
) {
792 if (strcmp(dent
->d_name
, ".") == 0 || strcmp(dent
->d_name
, "..") == 0)
794 result
->push_back(CreateFilePath(dent
->d_name
));
796 int saved_errno
= errno
;
798 if (readdir_result
!= 0)
799 return base::ErrnoToPlatformFileError(saved_errno
);
800 return base::PLATFORM_FILE_OK
;
805 Status
ChromiumEnv::GetChildren(const std::string
& dir_string
,
806 std::vector
<std::string
>* result
) {
807 std::vector
<base::FilePath
> entries
;
808 base::PlatformFileError error
=
809 GetDirectoryEntries(CreateFilePath(dir_string
), &entries
);
810 if (error
!= base::PLATFORM_FILE_OK
) {
811 RecordOSError(kGetChildren
, error
);
813 dir_string
, "Could not open/read directory", kGetChildren
, error
);
816 for (std::vector
<base::FilePath
>::iterator it
= entries
.begin();
819 result
->push_back(FilePathToString(*it
));
823 RestoreIfNecessary(dir_string
, result
);
827 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
829 base::FilePath fname_filepath
= CreateFilePath(fname
);
830 // TODO(jorlow): Should we assert this is a file?
831 if (!::base::DeleteFile(fname_filepath
, false)) {
832 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
833 RecordErrorAt(kDeleteFile
);
835 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
836 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
842 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
844 // TODO(rvargas): convert this code to base::File::Error.
845 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
846 Retrier
retrier(kCreateDir
, this);
848 if (base::CreateDirectoryAndGetError(
849 CreateFilePath(name
),
850 reinterpret_cast<base::File::Error
*>(&error
))) {
853 } while (retrier
.ShouldKeepTrying(error
));
854 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
855 RecordOSError(kCreateDir
, error
);
859 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
861 // TODO(jorlow): Should we assert this is a directory?
862 if (!::base::DeleteFile(CreateFilePath(name
), false)) {
863 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
864 RecordErrorAt(kDeleteDir
);
869 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
872 if (!::base::GetFileSize(CreateFilePath(fname
), &signed_size
)) {
874 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
875 RecordErrorAt(kGetFileSize
);
877 *size
= static_cast<uint64_t>(signed_size
);
882 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
884 base::FilePath src_file_path
= CreateFilePath(src
);
885 if (!::base::PathExists(src_file_path
))
887 base::FilePath destination
= CreateFilePath(dst
);
889 Retrier
retrier(kRenameFile
, this);
890 // TODO(rvargas): convert this code to base::File::Error.
891 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
893 if (base::ReplaceFile(src_file_path
, destination
,
894 reinterpret_cast<base::File::Error
*>(&error
))) {
897 } while (retrier
.ShouldKeepTrying(error
));
899 DCHECK(error
!= base::PLATFORM_FILE_OK
);
900 RecordOSError(kRenameFile
, error
);
904 "Could not rename file: %s",
905 PlatformFileErrorString(error
));
906 return MakeIOError(src
, buf
, kRenameFile
, error
);
909 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
912 int flags
= ::base::PLATFORM_FILE_OPEN_ALWAYS
|
913 ::base::PLATFORM_FILE_READ
|
914 ::base::PLATFORM_FILE_WRITE
;
916 ::base::PlatformFileError error_code
;
917 ::base::PlatformFile file
;
918 Retrier
retrier(kLockFile
, this);
920 file
= ::base::CreatePlatformFile(
921 CreateFilePath(fname
), flags
, &created
, &error_code
);
922 } while (error_code
!= ::base::PLATFORM_FILE_OK
&&
923 retrier
.ShouldKeepTrying(error_code
));
925 if (error_code
== ::base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
926 ::base::FilePath parent
= CreateFilePath(fname
).DirName();
927 ::base::FilePath last_parent
;
928 int num_missing_ancestors
= 0;
930 if (base::DirectoryExists(parent
))
932 ++num_missing_ancestors
;
933 last_parent
= parent
;
934 parent
= parent
.DirName();
935 } while (parent
!= last_parent
);
936 RecordLockFileAncestors(num_missing_ancestors
);
939 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
940 result
= MakeIOError(
941 fname
, PlatformFileErrorString(error_code
), kLockFile
, error_code
);
942 RecordOSError(kLockFile
, error_code
);
946 if (!locks_
.Insert(fname
)) {
947 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
948 ::base::ClosePlatformFile(file
);
952 Retrier lock_retrier
= Retrier(kLockFile
, this);
954 error_code
= ::base::LockPlatformFile(file
);
955 } while (error_code
!= ::base::PLATFORM_FILE_OK
&&
956 retrier
.ShouldKeepTrying(error_code
));
958 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
959 ::base::ClosePlatformFile(file
);
960 locks_
.Remove(fname
);
961 result
= MakeIOError(
962 fname
, PlatformFileErrorString(error_code
), kLockFile
, error_code
);
963 RecordOSError(kLockFile
, error_code
);
967 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
968 my_lock
->file_
= file
;
969 my_lock
->name_
= fname
;
974 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
975 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
978 ::base::PlatformFileError error_code
=
979 ::base::UnlockPlatformFile(my_lock
->file_
);
980 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
982 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
983 RecordOSError(kUnlockFile
, error_code
);
984 ::base::ClosePlatformFile(my_lock
->file_
);
985 } else if (!::base::ClosePlatformFile(my_lock
->file_
)) {
987 MakeIOError(my_lock
->name_
, "Could not close lock file.", kUnlockFile
);
988 RecordErrorAt(kUnlockFile
);
990 bool removed
= locks_
.Remove(my_lock
->name_
);
996 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
998 if (test_directory_
.empty()) {
999 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
1000 &test_directory_
)) {
1002 RecordErrorAt(kGetTestDirectory
);
1004 "Could not create temp directory.", "", kGetTestDirectory
);
1007 *path
= FilePathToString(test_directory_
);
1009 return Status::OK();
1012 Status
ChromiumEnv::NewLogger(const std::string
& fname
, Logger
** result
) {
1013 FILE* f
= fopen_internal(fname
.c_str(), "w");
1016 int saved_errno
= errno
;
1017 RecordOSError(kNewLogger
, saved_errno
);
1018 return MakeIOError(fname
, strerror(saved_errno
), kNewLogger
, saved_errno
);
1020 *result
= new ChromiumLogger(f
);
1021 return Status::OK();
1025 uint64_t ChromiumEnv::NowMicros() {
1026 return ::base::TimeTicks::Now().ToInternalValue();
1029 void ChromiumEnv::SleepForMicroseconds(int micros
) {
1030 // Round up to the next millisecond.
1031 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros
));
1034 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
1035 GetMethodIOErrorHistogram()->Add(method
);
1038 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
1039 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
1042 void ChromiumEnv::RecordOSError(MethodID method
,
1043 base::PlatformFileError error
) const {
1045 RecordErrorAt(method
);
1046 GetOSErrorHistogram(method
, -base::PLATFORM_FILE_ERROR_MAX
)->Add(-error
);
1049 void ChromiumEnv::RecordOSError(MethodID method
, int error
) const {
1051 RecordErrorAt(method
);
1052 GetOSErrorHistogram(method
, ERANGE
+ 1)->Add(error
);
1055 void ChromiumEnv::RecordBackupResult(bool result
) const {
1056 std::string
uma_name(name_
);
1057 uma_name
.append(".TableBackup");
1058 base::BooleanHistogram::FactoryGet(
1059 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
1062 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
1064 std::string
uma_name(name_
);
1065 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1066 uma_name
.append(".IOError.").append(MethodIDToString(method
));
1067 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
1068 base::Histogram::kUmaTargetedHistogramFlag
);
1071 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
1072 std::string
uma_name(name_
);
1073 uma_name
.append(".IOError");
1074 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
1075 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1078 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
1079 const std::string
& type
) const {
1080 std::string
uma_name(name_
);
1081 uma_name
.append(".MaxFDs.").append(type
);
1082 // These numbers make each bucket twice as large as the previous bucket.
1083 const int kFirstEntry
= 1;
1084 const int kLastEntry
= 65536;
1085 const int kNumBuckets
= 18;
1086 return base::Histogram::FactoryGet(
1087 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
1088 base::Histogram::kUmaTargetedHistogramFlag
);
1091 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
1092 std::string
uma_name(name_
);
1093 uma_name
.append(".LockFileAncestorsNotFound");
1095 const int kMax
= 10;
1096 const int kNumBuckets
= 11;
1097 return base::LinearHistogram::FactoryGet(
1098 uma_name
, kMin
, kMax
, kNumBuckets
,
1099 base::Histogram::kUmaTargetedHistogramFlag
);
1102 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
1103 std::string
uma_name(name_
);
1104 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1105 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
1107 const int kBucketSizeMillis
= 25;
1108 // Add 2, 1 for each of the buckets <1 and >max.
1109 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
1110 return base::Histogram::FactoryTimeGet(
1111 uma_name
, base::TimeDelta::FromMilliseconds(1),
1112 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
1114 base::Histogram::kUmaTargetedHistogramFlag
);
1117 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
1118 MethodID method
) const {
1119 std::string
uma_name(name_
);
1120 uma_name
.append(".RetryRecoveredFromErrorIn")
1121 .append(MethodIDToString(method
));
1122 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
1123 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1126 class Thread
: public ::base::PlatformThread::Delegate
{
1128 Thread(void (*function
)(void* arg
), void* arg
)
1129 : function_(function
), arg_(arg
) {
1130 ::base::PlatformThreadHandle handle
;
1131 bool success
= ::base::PlatformThread::Create(0, this, &handle
);
1134 virtual ~Thread() {}
1135 virtual void ThreadMain() {
1141 void (*function_
)(void* arg
);
1145 void ChromiumEnv::Schedule(void (*function
)(void*), void* arg
) {
1148 // Start background thread if necessary
1149 if (!started_bgthread_
) {
1150 started_bgthread_
= true;
1151 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1154 // If the queue is currently empty, the background thread may currently be
1156 if (queue_
.empty()) {
1160 // Add to priority queue
1161 queue_
.push_back(BGItem());
1162 queue_
.back().function
= function
;
1163 queue_
.back().arg
= arg
;
1168 void ChromiumEnv::BGThread() {
1169 base::PlatformThread::SetName(name_
.c_str());
1172 // Wait until there is an item that is ready to run
1174 while (queue_
.empty()) {
1178 void (*function
)(void*) = queue_
.front().function
;
1179 void* arg
= queue_
.front().arg
;
1183 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1188 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1189 new Thread(function
, arg
); // Will self-delete.
1192 static std::string
GetDirName(const std::string
& filename
) {
1193 base::FilePath file
= base::FilePath::FromUTF8Unsafe(filename
);
1194 return FilePathToString(file
.DirName());
1197 void ChromiumEnv::DidCreateNewFile(const std::string
& filename
) {
1198 base::AutoLock
auto_lock(map_lock_
);
1199 needs_sync_map_
[GetDirName(filename
)] = true;
1202 bool ChromiumEnv::DoesDirNeedSync(const std::string
& filename
) {
1203 base::AutoLock
auto_lock(map_lock_
);
1204 return needs_sync_map_
.find(GetDirName(filename
)) != needs_sync_map_
.end();
1207 void ChromiumEnv::DidSyncDir(const std::string
& filename
) {
1208 base::AutoLock
auto_lock(map_lock_
);
1209 needs_sync_map_
.erase(GetDirName(filename
));
1212 } // namespace leveldb_env
1217 return leveldb_env::idb_env
.Pointer();
1220 Env
* Env::Default() {
1221 return leveldb_env::default_env
.Pointer();
1224 } // namespace leveldb