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 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
394 // change the order because indices into this array have been recorded in uma
396 const char* patterns[] = {
398 "log record too small",
399 "corrupted internal key",
401 "missing start of fragmented record",
402 "error in middle of record",
403 "unknown record type",
404 "truncated record at end",
407 "FileReader invoked with unexpected value",
409 "CURRENT file does not end with newline",
410 "no meta-nextfile entry",
411 "no meta-lognumber entry",
412 "no last-sequence-number entry",
413 "malformed WriteBatch",
414 "bad WriteBatch Put",
415 "bad WriteBatch Delete",
416 "unknown WriteBatch tag",
417 "WriteBatch has wrong count",
418 "bad entry in block",
419 "bad block contents",
421 "truncated block read",
422 "block checksum mismatch",
424 "corrupted compressed block contents",
430 // Returns 1-based index into the above array or 0 if nothing matches.
431 int GetCorruptionCode(const leveldb::Status& status) {
432 DCHECK(!status.IsIOError());
433 DCHECK(!status.ok());
434 const int kOtherError = 0;
435 int error = kOtherError;
436 const std::string& str_error = status.ToString();
437 const size_t kNumPatterns = arraysize(patterns);
438 for (size_t i = 0; i < kNumPatterns; ++i) {
439 if (str_error.find(patterns[i]) != std::string::npos) {
447 int GetNumCorruptionCodes() {
448 // + 1 for the "other" error that is returned when a corruption message
449 // doesn't match any of the patterns.
450 return arraysize(patterns) + 1;
453 std::string GetCorruptionMessage(const leveldb::Status& status) {
454 int code = GetCorruptionCode(status);
456 return "Unknown corruption";
457 return patterns[code - 1];
460 bool IndicatesDiskFull(const leveldb::Status& status) {
463 leveldb_env::MethodID method;
465 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
466 status.ToString().c_str(), &method, &error);
467 return (result == leveldb_env::METHOD_AND_PFE &&
468 static_cast<base::PlatformFileError>(error) ==
469 base::PLATFORM_FILE_ERROR_NO_SPACE) ||
470 (result == leveldb_env::METHOD_AND_ERRNO && error == ENOSPC);
473 bool IsIOError(const leveldb::Status& status) {
474 leveldb_env::MethodID method;
476 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
477 status.ToString().c_str(), &method, &error);
478 return result != leveldb_env::NONE;
481 bool IsCorruption(const leveldb::Status& status) {
482 // LevelDB returns InvalidArgument when an sst file is truncated but there is
483 // no IsInvalidArgument() accessor defined.
484 return status.IsCorruption() || (!status.ok() && !IsIOError(status));
487 std::string FilePathToString(const base::FilePath& file_path) {
489 return UTF16ToUTF8(file_path.value());
491 return file_path.value();
495 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
497 const UMALogger* uma_logger,
498 WriteTracker* tracker,
502 uma_logger_(uma_logger),
505 make_backup_(make_backup) {
506 base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
507 if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
508 file_type_ = kManifest;
509 else if (path.MatchesExtension(table_extension))
511 if (file_type_ != kManifest)
512 tracker_->DidCreateNewFile(filename_);
513 parent_dir_ = FilePathToString(CreateFilePath(fname).DirName());
516 ChromiumWritableFile::~ChromiumWritableFile() {
518 // Ignoring any potential errors
523 Status ChromiumWritableFile::SyncParent() {
526 TRACE_EVENT0("leveldb", "SyncParent");
529 HANDLE_EINTR(open(parent_dir_.c_str(), O_RDONLY));
531 int saved_errno = errno;
533 parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
535 if (HANDLE_EINTR(fsync(parent_fd)) != 0) {
536 int saved_errno = errno;
538 parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
540 HANDLE_EINTR(close(parent_fd));
545 Status ChromiumWritableFile::Append(const Slice& data) {
546 if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
547 Status s = SyncParent();
550 tracker_->DidSyncDir(filename_);
553 size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);
554 if (r != data.size()) {
555 int saved_errno = errno;
556 uma_logger_->RecordOSError(kWritableFileAppend, saved_errno);
558 filename_, strerror(saved_errno), kWritableFileAppend, saved_errno);
563 Status ChromiumWritableFile::Close() {
565 if (fclose(file_) != 0) {
566 result = MakeIOError(filename_, strerror(errno), kWritableFileClose, errno);
567 uma_logger_->RecordErrorAt(kWritableFileClose);
573 Status ChromiumWritableFile::Flush() {
575 if (HANDLE_EINTR(fflush_unlocked(file_))) {
576 int saved_errno = errno;
577 result = MakeIOError(
578 filename_, strerror(saved_errno), kWritableFileFlush, saved_errno);
579 uma_logger_->RecordOSError(kWritableFileFlush, saved_errno);
584 static bool MakeBackup(const std::string& fname) {
585 base::FilePath original_table_name = CreateFilePath(fname);
586 base::FilePath backup_table_name =
587 original_table_name.ReplaceExtension(backup_table_extension);
588 return base::CopyFile(original_table_name, backup_table_name);
591 Status ChromiumWritableFile::Sync() {
592 TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
596 if (HANDLE_EINTR(fflush_unlocked(file_)))
598 // Sync even if fflush gave an error; perhaps the data actually got out,
599 // even though something went wrong.
600 if (fdatasync(fileno(file_)) && !error)
602 // Report the first error we found.
604 result = MakeIOError(filename_, strerror(error), kWritableFileSync, error);
605 uma_logger_->RecordErrorAt(kWritableFileSync);
606 } else if (make_backup_ && file_type_ == kTable) {
607 bool success = MakeBackup(filename_);
608 uma_logger_->RecordBackupResult(success);
613 ChromiumEnv::ChromiumEnv()
614 : name_("LevelDBEnv"),
617 started_bgthread_(false),
618 kMaxRetryTimeMillis(1000) {
621 ChromiumEnv::~ChromiumEnv() {
622 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
623 // ensure that behavior isn't accidentally changed, but there's an instance in
624 // a unit test that is deleted.
627 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
628 SequentialFile** result) {
629 FILE* f = fopen_internal(fname.c_str(), "rb");
632 int saved_errno = errno;
633 RecordOSError(kNewSequentialFile, saved_errno);
635 fname, strerror(saved_errno), kNewSequentialFile, saved_errno);
637 *result = new ChromiumSequentialFile(fname, f, this);
642 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
643 #if defined(OS_POSIX)
644 struct rlimit nofile;
645 if (getrlimit(RLIMIT_NOFILE, &nofile))
647 GetMaxFDHistogram(type)->Add(nofile.rlim_cur);
651 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
652 RandomAccessFile** result) {
653 int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
655 ::base::PlatformFileError error_code;
656 ::base::PlatformFile file = ::base::CreatePlatformFile(
657 CreateFilePath(fname), flags, &created, &error_code);
658 if (error_code == ::base::PLATFORM_FILE_OK) {
659 *result = new ChromiumRandomAccessFile(fname, file, this);
660 RecordOpenFilesLimit("Success");
663 if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED)
664 RecordOpenFilesLimit("TooManyOpened");
666 RecordOpenFilesLimit("OtherError");
668 RecordOSError(kNewRandomAccessFile, error_code);
669 return MakeIOError(fname,
670 PlatformFileErrorString(error_code),
671 kNewRandomAccessFile,
675 Status ChromiumEnv::NewWritableFile(const std::string& fname,
676 WritableFile** result) {
678 FILE* f = fopen_internal(fname.c_str(), "wb");
680 int saved_errno = errno;
681 RecordErrorAt(kNewWritableFile);
683 fname, strerror(saved_errno), kNewWritableFile, saved_errno);
685 *result = new ChromiumWritableFile(fname, f, this, this, make_backup_);
690 bool ChromiumEnv::FileExists(const std::string& fname) {
691 return ::base::PathExists(CreateFilePath(fname));
694 base::FilePath ChromiumEnv::RestoreFromBackup(const base::FilePath& base_name) {
695 base::FilePath table_name =
696 base_name.AddExtension(table_extension);
697 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
699 std::string uma_name(name_);
700 uma_name.append(".TableRestore");
701 base::BooleanHistogram::FactoryGet(
702 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
706 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
707 std::vector<std::string>* result) {
708 std::set<base::FilePath> tables_found;
709 std::set<base::FilePath> backups_found;
710 for (std::vector<std::string>::iterator it = result->begin();
713 base::FilePath current = CreateFilePath(*it);
714 if (current.MatchesExtension(table_extension))
715 tables_found.insert(current.RemoveExtension());
716 if (current.MatchesExtension(backup_table_extension))
717 backups_found.insert(current.RemoveExtension());
719 std::set<base::FilePath> backups_only;
720 std::set_difference(backups_found.begin(),
722 tables_found.begin(),
724 std::inserter(backups_only, backups_only.begin()));
725 if (backups_only.size()) {
726 std::string uma_name(name_);
727 uma_name.append(".MissingFiles");
728 int num_missing_files =
729 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
730 base::Histogram::FactoryGet(uma_name,
734 base::Histogram::kUmaTargetedHistogramFlag)
735 ->Add(num_missing_files);
737 base::FilePath dir_filepath = base::FilePath::FromUTF8Unsafe(dir);
738 for (std::set<base::FilePath>::iterator it = backups_only.begin();
739 it != backups_only.end();
741 base::FilePath restored_table_name =
742 RestoreFromBackup(dir_filepath.Append(*it));
743 result->push_back(FilePathToString(restored_table_name.BaseName()));
749 static base::PlatformFileError GetDirectoryEntries(
750 const base::FilePath& dir_param,
751 std::vector<base::FilePath>* result) {
753 base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
754 WIN32_FIND_DATA find_data;
755 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
756 if (find_handle == INVALID_HANDLE_VALUE) {
757 DWORD last_error = GetLastError();
758 if (last_error == ERROR_FILE_NOT_FOUND)
759 return base::PLATFORM_FILE_OK;
760 return base::LastErrorToPlatformFileError(last_error);
763 base::FilePath filepath(find_data.cFileName);
764 base::FilePath::StringType basename = filepath.BaseName().value();
765 if (basename == FILE_PATH_LITERAL(".") ||
766 basename == FILE_PATH_LITERAL(".."))
768 result->push_back(filepath.BaseName());
769 } while (FindNextFile(find_handle, &find_data));
770 DWORD last_error = GetLastError();
771 base::PlatformFileError return_value = base::PLATFORM_FILE_OK;
772 if (last_error != ERROR_NO_MORE_FILES)
773 return_value = base::LastErrorToPlatformFileError(last_error);
774 FindClose(find_handle);
778 static base::PlatformFileError GetDirectoryEntries(
779 const base::FilePath& dir_filepath,
780 std::vector<base::FilePath>* result) {
781 const std::string dir_string = FilePathToString(dir_filepath);
783 DIR* dir = opendir(dir_string.c_str());
785 return base::ErrnoToPlatformFileError(errno);
786 struct dirent dent_buf;
789 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent)
790 result->push_back(CreateFilePath(dent->d_name));
791 int saved_errno = errno;
793 if (readdir_result != 0)
794 return base::ErrnoToPlatformFileError(saved_errno);
795 return base::PLATFORM_FILE_OK;
800 Status ChromiumEnv::GetChildren(const std::string& dir_string,
801 std::vector<std::string>* result) {
802 std::vector<base::FilePath> entries;
803 base::PlatformFileError error =
804 GetDirectoryEntries(CreateFilePath(dir_string), &entries);
805 if (error != base::PLATFORM_FILE_OK) {
806 RecordOSError(kGetChildren, error);
808 dir_string, "Could not open/read directory", kGetChildren, error);
810 for (std::vector<base::FilePath>::iterator it = entries.begin();
813 result->push_back(FilePathToString(*it));
817 RestoreIfNecessary(dir_string, result);
821 Status ChromiumEnv::DeleteFile(const std::string& fname) {
823 base::FilePath fname_filepath = CreateFilePath(fname);
824 // TODO(jorlow): Should we assert this is a file?
825 if (!::base::DeleteFile(fname_filepath, false)) {
826 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
827 RecordErrorAt(kDeleteFile);
829 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
830 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
836 Status ChromiumEnv::CreateDir(const std::string& name) {
838 base::PlatformFileError error = base::PLATFORM_FILE_OK;
839 Retrier retrier(kCreateDir, this);
841 if (::file_util::CreateDirectoryAndGetError(CreateFilePath(name), &error))
843 } while (retrier.ShouldKeepTrying(error));
844 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
845 RecordOSError(kCreateDir, error);
849 Status ChromiumEnv::DeleteDir(const std::string& name) {
851 // TODO(jorlow): Should we assert this is a directory?
852 if (!::base::DeleteFile(CreateFilePath(name), false)) {
853 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
854 RecordErrorAt(kDeleteDir);
859 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
862 if (!::file_util::GetFileSize(CreateFilePath(fname), &signed_size)) {
864 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
865 RecordErrorAt(kGetFileSize);
867 *size = static_cast<uint64_t>(signed_size);
872 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
874 base::FilePath src_file_path = CreateFilePath(src);
875 if (!::base::PathExists(src_file_path))
877 base::FilePath destination = CreateFilePath(dst);
879 Retrier retrier(kRenameFile, this);
880 base::PlatformFileError error = base::PLATFORM_FILE_OK;
882 if (base::ReplaceFile(src_file_path, destination, &error))
884 } while (retrier.ShouldKeepTrying(error));
886 DCHECK(error != base::PLATFORM_FILE_OK);
887 RecordOSError(kRenameFile, error);
891 "Could not rename file: %s",
892 PlatformFileErrorString(error));
893 return MakeIOError(src, buf, kRenameFile, error);
896 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
899 int flags = ::base::PLATFORM_FILE_OPEN_ALWAYS |
900 ::base::PLATFORM_FILE_READ |
901 ::base::PLATFORM_FILE_WRITE;
903 ::base::PlatformFileError error_code;
904 ::base::PlatformFile file;
905 Retrier retrier(kLockFile, this);
907 file = ::base::CreatePlatformFile(
908 CreateFilePath(fname), flags, &created, &error_code);
909 } while (error_code != ::base::PLATFORM_FILE_OK &&
910 retrier.ShouldKeepTrying(error_code));
912 if (error_code == ::base::PLATFORM_FILE_ERROR_NOT_FOUND) {
913 ::base::FilePath parent = CreateFilePath(fname).DirName();
914 ::base::FilePath last_parent;
915 int num_missing_ancestors = 0;
917 if (base::DirectoryExists(parent))
919 ++num_missing_ancestors;
920 last_parent = parent;
921 parent = parent.DirName();
922 } while (parent != last_parent);
923 RecordLockFileAncestors(num_missing_ancestors);
926 if (error_code != ::base::PLATFORM_FILE_OK) {
927 result = MakeIOError(
928 fname, PlatformFileErrorString(error_code), kLockFile, error_code);
929 RecordOSError(kLockFile, error_code);
933 if (!locks_.Insert(fname)) {
934 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
935 ::base::ClosePlatformFile(file);
939 Retrier lock_retrier = Retrier(kLockFile, this);
941 error_code = ::base::LockPlatformFile(file);
942 } while (error_code != ::base::PLATFORM_FILE_OK &&
943 retrier.ShouldKeepTrying(error_code));
945 if (error_code != ::base::PLATFORM_FILE_OK) {
946 ::base::ClosePlatformFile(file);
947 locks_.Remove(fname);
948 result = MakeIOError(
949 fname, PlatformFileErrorString(error_code), kLockFile, error_code);
950 RecordOSError(kLockFile, error_code);
954 ChromiumFileLock* my_lock = new ChromiumFileLock;
955 my_lock->file_ = file;
956 my_lock->name_ = fname;
961 Status ChromiumEnv::UnlockFile(FileLock* lock) {
962 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
965 ::base::PlatformFileError error_code =
966 ::base::UnlockPlatformFile(my_lock->file_);
967 if (error_code != ::base::PLATFORM_FILE_OK) {
969 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
970 RecordOSError(kUnlockFile, error_code);
971 ::base::ClosePlatformFile(my_lock->file_);
972 } else if (!::base::ClosePlatformFile(my_lock->file_)) {
974 MakeIOError(my_lock->name_, "Could not close lock file.", kUnlockFile);
975 RecordErrorAt(kUnlockFile);
977 bool removed = locks_.Remove(my_lock->name_);
983 Status ChromiumEnv::GetTestDirectory(std::string* path) {
985 if (test_directory_.empty()) {
986 if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
989 RecordErrorAt(kGetTestDirectory);
991 "Could not create temp directory.", "", kGetTestDirectory);
994 *path = FilePathToString(test_directory_);
999 Status ChromiumEnv::NewLogger(const std::string& fname, Logger** result) {
1000 FILE* f = fopen_internal(fname.c_str(), "w");
1003 int saved_errno = errno;
1004 RecordOSError(kNewLogger, saved_errno);
1005 return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
1007 *result = new ChromiumLogger(f);
1008 return Status::OK();
1012 uint64_t ChromiumEnv::NowMicros() {
1013 return ::base::TimeTicks::Now().ToInternalValue();
1016 void ChromiumEnv::SleepForMicroseconds(int micros) {
1017 // Round up to the next millisecond.
1018 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
1021 void ChromiumEnv::RecordErrorAt(MethodID method) const {
1022 GetMethodIOErrorHistogram()->Add(method);
1025 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
1026 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
1029 void ChromiumEnv::RecordOSError(MethodID method,
1030 base::PlatformFileError error) const {
1032 RecordErrorAt(method);
1033 GetOSErrorHistogram(method, -base::PLATFORM_FILE_ERROR_MAX)->Add(-error);
1036 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
1038 RecordErrorAt(method);
1039 GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
1042 void ChromiumEnv::RecordBackupResult(bool result) const {
1043 std::string uma_name(name_);
1044 uma_name.append(".TableBackup");
1045 base::BooleanHistogram::FactoryGet(
1046 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
1049 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
1051 std::string uma_name(name_);
1052 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1053 uma_name.append(".IOError.").append(MethodIDToString(method));
1054 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
1055 base::Histogram::kUmaTargetedHistogramFlag);
1058 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
1059 std::string uma_name(name_);
1060 uma_name.append(".IOError");
1061 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1062 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1065 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
1066 const std::string& type) const {
1067 std::string uma_name(name_);
1068 uma_name.append(".MaxFDs.").append(type);
1069 // These numbers make each bucket twice as large as the previous bucket.
1070 const int kFirstEntry = 1;
1071 const int kLastEntry = 65536;
1072 const int kNumBuckets = 18;
1073 return base::Histogram::FactoryGet(
1074 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
1075 base::Histogram::kUmaTargetedHistogramFlag);
1078 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
1079 std::string uma_name(name_);
1080 uma_name.append(".LockFileAncestorsNotFound");
1082 const int kMax = 10;
1083 const int kNumBuckets = 11;
1084 return base::LinearHistogram::FactoryGet(
1085 uma_name, kMin, kMax, kNumBuckets,
1086 base::Histogram::kUmaTargetedHistogramFlag);
1089 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
1090 std::string uma_name(name_);
1091 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1092 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
1094 const int kBucketSizeMillis = 25;
1095 // Add 2, 1 for each of the buckets <1 and >max.
1096 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
1097 return base::Histogram::FactoryTimeGet(
1098 uma_name, base::TimeDelta::FromMilliseconds(1),
1099 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
1101 base::Histogram::kUmaTargetedHistogramFlag);
1104 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
1105 MethodID method) const {
1106 std::string uma_name(name_);
1107 uma_name.append(".RetryRecoveredFromErrorIn")
1108 .append(MethodIDToString(method));
1109 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1110 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1113 class Thread : public ::base::PlatformThread::Delegate {
1115 Thread(void (*function)(void* arg), void* arg)
1116 : function_(function), arg_(arg) {
1117 ::base::PlatformThreadHandle handle;
1118 bool success = ::base::PlatformThread::Create(0, this, &handle);
1121 virtual ~Thread() {}
1122 virtual void ThreadMain() {
1128 void (*function_)(void* arg);
1132 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
1135 // Start background thread if necessary
1136 if (!started_bgthread_) {
1137 started_bgthread_ = true;
1138 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1141 // If the queue is currently empty, the background thread may currently be
1143 if (queue_.empty()) {
1147 // Add to priority queue
1148 queue_.push_back(BGItem());
1149 queue_.back().function = function;
1150 queue_.back().arg = arg;
1155 void ChromiumEnv::BGThread() {
1156 base::PlatformThread::SetName(name_.c_str());
1159 // Wait until there is an item that is ready to run
1161 while (queue_.empty()) {
1165 void (*function)(void*) = queue_.front().function;
1166 void* arg = queue_.front().arg;
1170 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1175 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1176 new Thread(function, arg); // Will self-delete.
1179 static std::string GetDirName(const std::string& filename) {
1180 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
1181 return FilePathToString(file.DirName());
1184 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
1185 base::AutoLock auto_lock(map_lock_);
1186 needs_sync_map_[GetDirName(filename)] = true;
1189 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
1190 base::AutoLock auto_lock(map_lock_);
1191 return needs_sync_map_.find(GetDirName(filename)) != needs_sync_map_.end();
1194 void ChromiumEnv::DidSyncDir(const std::string& filename) {
1195 base::AutoLock auto_lock(map_lock_);
1196 needs_sync_map_.erase(GetDirName(filename));
1199 } // namespace leveldb_env
1204 return leveldb_env::idb_env.Pointer();
1207 Env* Env::Default() {
1208 return leveldb_env::default_env.Pointer();
1211 } // namespace leveldb