Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blobb8d56a2405376651bf7af5b981d14f22659c9cbf
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.
5 #include "third_party/leveldatabase/env_chromium.h"
7 #if defined(OS_POSIX)
8 #include <dirent.h>
9 #include <sys/types.h>
10 #endif
12 #include "base/debug/trace_event.h"
13 #include "base/files/file_util.h"
14 #include "base/lazy_instance.h"
15 #include "base/memory/shared_memory.h"
16 #include "base/metrics/histogram.h"
17 #include "base/process/process_metrics.h"
18 #include "base/stl_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "third_party/leveldatabase/chromium_logger.h"
21 #include "third_party/re2/re2/re2.h"
23 using leveldb::FileLock;
24 using leveldb::Slice;
25 using leveldb::Status;
27 namespace leveldb_env {
29 namespace {
31 const base::FilePath::CharType backup_table_extension[] =
32 FILE_PATH_LITERAL(".bak");
33 const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
35 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
36 = FILE_PATH_LITERAL("leveldb-test-");
38 static base::File::Error LastFileError() {
39 #if defined(OS_WIN)
40 return base::File::OSErrorToFileError(GetLastError());
41 #else
42 return base::File::OSErrorToFileError(errno);
43 #endif
46 // Making direct platform in lieu of using base::FileEnumerator because the
47 // latter can fail quietly without return an error result.
48 static base::File::Error GetDirectoryEntries(
49 const base::FilePath& dir_param,
50 std::vector<base::FilePath>* result) {
51 result->clear();
52 #if defined(OS_WIN)
53 base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
54 WIN32_FIND_DATA find_data;
55 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
56 if (find_handle == INVALID_HANDLE_VALUE) {
57 DWORD last_error = GetLastError();
58 if (last_error == ERROR_FILE_NOT_FOUND)
59 return base::File::FILE_OK;
60 return base::File::OSErrorToFileError(last_error);
62 do {
63 base::FilePath filepath(find_data.cFileName);
64 base::FilePath::StringType basename = filepath.BaseName().value();
65 if (basename == FILE_PATH_LITERAL(".") ||
66 basename == FILE_PATH_LITERAL(".."))
67 continue;
68 result->push_back(filepath.BaseName());
69 } while (FindNextFile(find_handle, &find_data));
70 DWORD last_error = GetLastError();
71 base::File::Error return_value = base::File::FILE_OK;
72 if (last_error != ERROR_NO_MORE_FILES)
73 return_value = base::File::OSErrorToFileError(last_error);
74 FindClose(find_handle);
75 return return_value;
76 #else
77 const std::string dir_string = FilePathToString(dir_param);
78 DIR* dir = opendir(dir_string.c_str());
79 if (!dir)
80 return base::File::OSErrorToFileError(errno);
81 struct dirent dent_buf;
82 struct dirent* dent;
83 int readdir_result;
84 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
85 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
86 continue;
87 result->push_back(ChromiumEnv::CreateFilePath(dent->d_name));
89 int saved_errno = errno;
90 closedir(dir);
91 if (readdir_result != 0)
92 return base::File::OSErrorToFileError(saved_errno);
93 return base::File::FILE_OK;
94 #endif
97 class ChromiumFileLock : public FileLock {
98 public:
99 ::base::File file_;
100 std::string name_;
103 class Retrier {
104 public:
105 Retrier(MethodID method, RetrierProvider* provider)
106 : start_(base::TimeTicks::Now()),
107 limit_(start_ + base::TimeDelta::FromMilliseconds(
108 provider->MaxRetryTimeMillis())),
109 last_(start_),
110 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
111 success_(true),
112 method_(method),
113 last_error_(base::File::FILE_OK),
114 provider_(provider) {}
115 ~Retrier() {
116 if (success_) {
117 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
118 if (last_error_ != base::File::FILE_OK) {
119 DCHECK_LT(last_error_, 0);
120 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
124 bool ShouldKeepTrying(base::File::Error last_error) {
125 DCHECK_NE(last_error, base::File::FILE_OK);
126 last_error_ = last_error;
127 if (last_ < limit_) {
128 base::PlatformThread::Sleep(time_to_sleep_);
129 last_ = base::TimeTicks::Now();
130 return true;
132 success_ = false;
133 return false;
136 private:
137 base::TimeTicks start_;
138 base::TimeTicks limit_;
139 base::TimeTicks last_;
140 base::TimeDelta time_to_sleep_;
141 bool success_;
142 MethodID method_;
143 base::File::Error last_error_;
144 RetrierProvider* provider_;
147 class ChromiumSequentialFile : public leveldb::SequentialFile {
148 public:
149 ChromiumSequentialFile(const std::string& fname,
150 base::File* f,
151 const UMALogger* uma_logger)
152 : filename_(fname), file_(f), uma_logger_(uma_logger) {}
153 virtual ~ChromiumSequentialFile() {}
155 Status Read(size_t n, Slice* result, char* scratch) override {
156 int bytes_read = file_->ReadAtCurrentPosNoBestEffort(scratch, n);
157 if (bytes_read == -1) {
158 base::File::Error error = LastFileError();
159 uma_logger_->RecordErrorAt(kSequentialFileRead);
160 return MakeIOError(filename_, base::File::ErrorToString(error),
161 kSequentialFileRead, error);
162 } else {
163 *result = Slice(scratch, bytes_read);
164 return Status::OK();
168 Status Skip(uint64_t n) override {
169 if (file_->Seek(base::File::FROM_CURRENT, n) == -1) {
170 base::File::Error error = LastFileError();
171 uma_logger_->RecordErrorAt(kSequentialFileSkip);
172 return MakeIOError(filename_, base::File::ErrorToString(error),
173 kSequentialFileSkip, error);
174 } else {
175 return Status::OK();
179 private:
180 std::string filename_;
181 scoped_ptr<base::File> file_;
182 const UMALogger* uma_logger_;
185 class ChromiumRandomAccessFile : public leveldb::RandomAccessFile {
186 public:
187 ChromiumRandomAccessFile(const std::string& fname,
188 ::base::File file,
189 const UMALogger* uma_logger)
190 : filename_(fname), file_(file.Pass()), uma_logger_(uma_logger) {}
191 virtual ~ChromiumRandomAccessFile() {}
193 Status Read(uint64_t offset,
194 size_t n,
195 Slice* result,
196 char* scratch) const override {
197 Status s;
198 int r = file_.Read(offset, scratch, n);
199 *result = Slice(scratch, (r < 0) ? 0 : r);
200 if (r < 0) {
201 // An error: return a non-ok status
202 s = MakeIOError(filename_, "Could not perform read",
203 kRandomAccessFileRead);
204 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
206 return s;
209 private:
210 std::string filename_;
211 mutable ::base::File file_;
212 const UMALogger* uma_logger_;
215 class ChromiumWritableFile : public leveldb::WritableFile {
216 public:
217 ChromiumWritableFile(const std::string& fname,
218 base::File* f,
219 const UMALogger* uma_logger,
220 WriteTracker* tracker,
221 bool make_backup);
222 virtual ~ChromiumWritableFile() {}
223 leveldb::Status Append(const leveldb::Slice& data) override;
224 leveldb::Status Close() override;
225 leveldb::Status Flush() override;
226 leveldb::Status Sync() override;
228 private:
229 enum Type { kManifest, kTable, kOther };
230 leveldb::Status SyncParent();
232 std::string filename_;
233 scoped_ptr<base::File> file_;
234 const UMALogger* uma_logger_;
235 WriteTracker* tracker_;
236 Type file_type_;
237 std::string parent_dir_;
238 bool make_backup_;
241 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
242 base::File* f,
243 const UMALogger* uma_logger,
244 WriteTracker* tracker,
245 bool make_backup)
246 : filename_(fname),
247 file_(f),
248 uma_logger_(uma_logger),
249 tracker_(tracker),
250 file_type_(kOther),
251 make_backup_(make_backup) {
252 base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
253 if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
254 file_type_ = kManifest;
255 else if (ChromiumEnv::HasTableExtension(path))
256 file_type_ = kTable;
257 if (file_type_ != kManifest)
258 tracker_->DidCreateNewFile(filename_);
259 parent_dir_ = FilePathToString(ChromiumEnv::CreateFilePath(fname).DirName());
262 Status ChromiumWritableFile::SyncParent() {
263 TRACE_EVENT0("leveldb", "SyncParent");
264 #if defined(OS_POSIX)
265 base::FilePath path = base::FilePath::FromUTF8Unsafe(parent_dir_);
266 base::File f(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
267 if (!f.IsValid()) {
268 return MakeIOError(parent_dir_, "Unable to open directory", kSyncParent,
269 f.error_details());
271 if (!f.Flush()) {
272 base::File::Error error = LastFileError();
273 return MakeIOError(parent_dir_, base::File::ErrorToString(error),
274 kSyncParent, error);
276 #endif
277 return Status::OK();
280 Status ChromiumWritableFile::Append(const Slice& data) {
281 if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
282 Status s = SyncParent();
283 if (!s.ok())
284 return s;
285 tracker_->DidSyncDir(filename_);
288 int bytes_written = file_->WriteAtCurrentPos(data.data(), data.size());
289 if (bytes_written != data.size()) {
290 base::File::Error error = LastFileError();
291 uma_logger_->RecordOSError(kWritableFileAppend, error);
292 return MakeIOError(filename_, base::File::ErrorToString(error),
293 kWritableFileAppend, error);
296 return Status::OK();
299 Status ChromiumWritableFile::Close() {
300 file_->Close();
301 return Status::OK();
304 Status ChromiumWritableFile::Flush() {
305 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
306 // flush.
307 return Status::OK();
310 Status ChromiumWritableFile::Sync() {
311 TRACE_EVENT0("leveldb", "WritableFile::Sync");
313 if (!file_->Flush()) {
314 base::File::Error error = LastFileError();
315 uma_logger_->RecordErrorAt(kWritableFileSync);
316 return MakeIOError(filename_, base::File::ErrorToString(error),
317 kWritableFileSync, error);
320 if (make_backup_ && file_type_ == kTable)
321 uma_logger_->RecordBackupResult(ChromiumEnv::MakeBackup(filename_));
323 return Status::OK();
326 class IDBEnv : public ChromiumEnv {
327 public:
328 IDBEnv() : ChromiumEnv() {
329 name_ = "LevelDBEnv.IDB";
330 make_backup_ = true;
334 ::base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
336 ::base::LazyInstance<ChromiumEnv>::Leaky default_env =
337 LAZY_INSTANCE_INITIALIZER;
339 } // unnamed namespace
341 const char* MethodIDToString(MethodID method) {
342 switch (method) {
343 case kSequentialFileRead:
344 return "SequentialFileRead";
345 case kSequentialFileSkip:
346 return "SequentialFileSkip";
347 case kRandomAccessFileRead:
348 return "RandomAccessFileRead";
349 case kWritableFileAppend:
350 return "WritableFileAppend";
351 case kWritableFileClose:
352 return "WritableFileClose";
353 case kWritableFileFlush:
354 return "WritableFileFlush";
355 case kWritableFileSync:
356 return "WritableFileSync";
357 case kNewSequentialFile:
358 return "NewSequentialFile";
359 case kNewRandomAccessFile:
360 return "NewRandomAccessFile";
361 case kNewWritableFile:
362 return "NewWritableFile";
363 case kDeleteFile:
364 return "DeleteFile";
365 case kCreateDir:
366 return "CreateDir";
367 case kDeleteDir:
368 return "DeleteDir";
369 case kGetFileSize:
370 return "GetFileSize";
371 case kRenameFile:
372 return "RenameFile";
373 case kLockFile:
374 return "LockFile";
375 case kUnlockFile:
376 return "UnlockFile";
377 case kGetTestDirectory:
378 return "GetTestDirectory";
379 case kNewLogger:
380 return "NewLogger";
381 case kSyncParent:
382 return "SyncParent";
383 case kGetChildren:
384 return "GetChildren";
385 case kNumEntries:
386 NOTREACHED();
387 return "kNumEntries";
389 NOTREACHED();
390 return "Unknown";
393 Status MakeIOError(Slice filename,
394 const std::string& message,
395 MethodID method,
396 base::File::Error error) {
397 DCHECK_LT(error, 0);
398 char buf[512];
399 snprintf(buf, sizeof(buf), "%s (ChromeMethodPFE: %d::%s::%d)",
400 message.c_str(), method, MethodIDToString(method), -error);
401 return Status::IOError(filename, buf);
404 Status MakeIOError(Slice filename,
405 const std::string& message,
406 MethodID method) {
407 char buf[512];
408 snprintf(buf, sizeof(buf), "%s (ChromeMethodOnly: %d::%s)", message.c_str(),
409 method, MethodIDToString(method));
410 return Status::IOError(filename, buf);
413 ErrorParsingResult ParseMethodAndError(const char* string,
414 MethodID* method_param,
415 int* error) {
416 int method;
417 if (RE2::PartialMatch(string, "ChromeMethodOnly: (\\d+)", &method)) {
418 *method_param = static_cast<MethodID>(method);
419 return METHOD_ONLY;
421 if (RE2::PartialMatch(
422 string, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method, error)) {
423 *error = -*error;
424 *method_param = static_cast<MethodID>(method);
425 return METHOD_AND_PFE;
427 if (RE2::PartialMatch(
428 string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) {
429 *method_param = static_cast<MethodID>(method);
430 return METHOD_AND_ERRNO;
432 return NONE;
435 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
436 // change the order because indices into this array have been recorded in uma
437 // histograms.
438 const char* patterns[] = {
439 "missing files",
440 "log record too small",
441 "corrupted internal key",
442 "partial record",
443 "missing start of fragmented record",
444 "error in middle of record",
445 "unknown record type",
446 "truncated record at end",
447 "bad record length",
448 "VersionEdit",
449 "FileReader invoked with unexpected value",
450 "corrupted key",
451 "CURRENT file does not end with newline",
452 "no meta-nextfile entry",
453 "no meta-lognumber entry",
454 "no last-sequence-number entry",
455 "malformed WriteBatch",
456 "bad WriteBatch Put",
457 "bad WriteBatch Delete",
458 "unknown WriteBatch tag",
459 "WriteBatch has wrong count",
460 "bad entry in block",
461 "bad block contents",
462 "bad block handle",
463 "truncated block read",
464 "block checksum mismatch",
465 "checksum mismatch",
466 "corrupted compressed block contents",
467 "bad block type",
468 "bad magic number",
469 "file is too short",
472 // Returns 1-based index into the above array or 0 if nothing matches.
473 int GetCorruptionCode(const leveldb::Status& status) {
474 DCHECK(!status.IsIOError());
475 DCHECK(!status.ok());
476 const int kOtherError = 0;
477 int error = kOtherError;
478 const std::string& str_error = status.ToString();
479 const size_t kNumPatterns = arraysize(patterns);
480 for (size_t i = 0; i < kNumPatterns; ++i) {
481 if (str_error.find(patterns[i]) != std::string::npos) {
482 error = i + 1;
483 break;
486 return error;
489 int GetNumCorruptionCodes() {
490 // + 1 for the "other" error that is returned when a corruption message
491 // doesn't match any of the patterns.
492 return arraysize(patterns) + 1;
495 std::string GetCorruptionMessage(const leveldb::Status& status) {
496 int code = GetCorruptionCode(status);
497 if (code == 0)
498 return "Unknown corruption";
499 return patterns[code - 1];
502 bool IndicatesDiskFull(const leveldb::Status& status) {
503 if (status.ok())
504 return false;
505 leveldb_env::MethodID method;
506 int error = -1;
507 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
508 status.ToString().c_str(), &method, &error);
509 return (result == leveldb_env::METHOD_AND_PFE &&
510 static_cast<base::File::Error>(error) ==
511 base::File::FILE_ERROR_NO_SPACE) ||
512 (result == leveldb_env::METHOD_AND_ERRNO && error == ENOSPC);
515 bool IsIOError(const leveldb::Status& status) {
516 leveldb_env::MethodID method;
517 int error = -1;
518 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
519 status.ToString().c_str(), &method, &error);
520 return result != leveldb_env::NONE;
523 std::string FilePathToString(const base::FilePath& file_path) {
524 #if defined(OS_WIN)
525 return base::UTF16ToUTF8(file_path.value());
526 #else
527 return file_path.value();
528 #endif
531 base::FilePath ChromiumEnv::CreateFilePath(const std::string& file_path) {
532 return base::FilePath::FromUTF8Unsafe(file_path);
535 bool ChromiumEnv::MakeBackup(const std::string& fname) {
536 base::FilePath original_table_name = CreateFilePath(fname);
537 base::FilePath backup_table_name =
538 original_table_name.ReplaceExtension(backup_table_extension);
539 return base::CopyFile(original_table_name, backup_table_name);
542 bool ChromiumEnv::HasTableExtension(const base::FilePath& path) {
543 return path.MatchesExtension(table_extension);
546 ChromiumEnv::ChromiumEnv()
547 : name_("LevelDBEnv"),
548 make_backup_(false),
549 bgsignal_(&mu_),
550 started_bgthread_(false),
551 kMaxRetryTimeMillis(1000) {
554 ChromiumEnv::~ChromiumEnv() {
555 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
556 // ensure that behavior isn't accidentally changed, but there's an instance in
557 // a unit test that is deleted.
560 bool ChromiumEnv::FileExists(const std::string& fname) {
561 return ::base::PathExists(CreateFilePath(fname));
564 const char* ChromiumEnv::FileErrorString(::base::File::Error error) {
565 switch (error) {
566 case ::base::File::FILE_ERROR_FAILED:
567 return "No further details.";
568 case ::base::File::FILE_ERROR_IN_USE:
569 return "File currently in use.";
570 case ::base::File::FILE_ERROR_EXISTS:
571 return "File already exists.";
572 case ::base::File::FILE_ERROR_NOT_FOUND:
573 return "File not found.";
574 case ::base::File::FILE_ERROR_ACCESS_DENIED:
575 return "Access denied.";
576 case ::base::File::FILE_ERROR_TOO_MANY_OPENED:
577 return "Too many files open.";
578 case ::base::File::FILE_ERROR_NO_MEMORY:
579 return "Out of memory.";
580 case ::base::File::FILE_ERROR_NO_SPACE:
581 return "No space left on drive.";
582 case ::base::File::FILE_ERROR_NOT_A_DIRECTORY:
583 return "Not a directory.";
584 case ::base::File::FILE_ERROR_INVALID_OPERATION:
585 return "Invalid operation.";
586 case ::base::File::FILE_ERROR_SECURITY:
587 return "Security error.";
588 case ::base::File::FILE_ERROR_ABORT:
589 return "File operation aborted.";
590 case ::base::File::FILE_ERROR_NOT_A_FILE:
591 return "The supplied path was not a file.";
592 case ::base::File::FILE_ERROR_NOT_EMPTY:
593 return "The file was not empty.";
594 case ::base::File::FILE_ERROR_INVALID_URL:
595 return "Invalid URL.";
596 case ::base::File::FILE_ERROR_IO:
597 return "OS or hardware error.";
598 case ::base::File::FILE_OK:
599 return "OK.";
600 case ::base::File::FILE_ERROR_MAX:
601 NOTREACHED();
603 NOTIMPLEMENTED();
604 return "Unknown error.";
607 base::FilePath ChromiumEnv::RestoreFromBackup(const base::FilePath& base_name) {
608 base::FilePath table_name =
609 base_name.AddExtension(table_extension);
610 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
611 table_name);
612 std::string uma_name(name_);
613 uma_name.append(".TableRestore");
614 base::BooleanHistogram::FactoryGet(
615 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
616 return table_name;
619 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
620 std::vector<std::string>* result) {
621 std::set<base::FilePath> tables_found;
622 std::set<base::FilePath> backups_found;
623 for (std::vector<std::string>::iterator it = result->begin();
624 it != result->end();
625 ++it) {
626 base::FilePath current = CreateFilePath(*it);
627 if (current.MatchesExtension(table_extension))
628 tables_found.insert(current.RemoveExtension());
629 if (current.MatchesExtension(backup_table_extension))
630 backups_found.insert(current.RemoveExtension());
632 std::set<base::FilePath> backups_only =
633 base::STLSetDifference<std::set<base::FilePath> >(backups_found,
634 tables_found);
636 if (backups_only.size()) {
637 std::string uma_name(name_);
638 uma_name.append(".MissingFiles");
639 int num_missing_files =
640 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
641 base::Histogram::FactoryGet(uma_name,
642 1 /*min*/,
643 100 /*max*/,
644 8 /*num_buckets*/,
645 base::Histogram::kUmaTargetedHistogramFlag)
646 ->Add(num_missing_files);
648 base::FilePath dir_filepath = base::FilePath::FromUTF8Unsafe(dir);
649 for (std::set<base::FilePath>::iterator it = backups_only.begin();
650 it != backups_only.end();
651 ++it) {
652 base::FilePath restored_table_name =
653 RestoreFromBackup(dir_filepath.Append(*it));
654 result->push_back(FilePathToString(restored_table_name.BaseName()));
658 Status ChromiumEnv::GetChildren(const std::string& dir_string,
659 std::vector<std::string>* result) {
660 std::vector<base::FilePath> entries;
661 base::File::Error error =
662 GetDirectoryEntries(CreateFilePath(dir_string), &entries);
663 if (error != base::File::FILE_OK) {
664 RecordOSError(kGetChildren, error);
665 return MakeIOError(
666 dir_string, "Could not open/read directory", kGetChildren, error);
669 result->clear();
670 for (const auto& entry : entries)
671 result->push_back(FilePathToString(entry));
673 if (make_backup_)
674 RestoreIfNecessary(dir_string, result);
676 return Status::OK();
679 Status ChromiumEnv::DeleteFile(const std::string& fname) {
680 Status result;
681 base::FilePath fname_filepath = CreateFilePath(fname);
682 // TODO(jorlow): Should we assert this is a file?
683 if (!::base::DeleteFile(fname_filepath, false)) {
684 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
685 RecordErrorAt(kDeleteFile);
687 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
688 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
689 false);
691 return result;
694 Status ChromiumEnv::CreateDir(const std::string& name) {
695 Status result;
696 base::File::Error error = base::File::FILE_OK;
697 Retrier retrier(kCreateDir, this);
698 do {
699 if (base::CreateDirectoryAndGetError(CreateFilePath(name), &error))
700 return result;
701 } while (retrier.ShouldKeepTrying(error));
702 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
703 RecordOSError(kCreateDir, error);
704 return result;
707 Status ChromiumEnv::DeleteDir(const std::string& name) {
708 Status result;
709 // TODO(jorlow): Should we assert this is a directory?
710 if (!::base::DeleteFile(CreateFilePath(name), false)) {
711 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
712 RecordErrorAt(kDeleteDir);
714 return result;
717 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
718 Status s;
719 int64_t signed_size;
720 if (!::base::GetFileSize(CreateFilePath(fname), &signed_size)) {
721 *size = 0;
722 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
723 RecordErrorAt(kGetFileSize);
724 } else {
725 *size = static_cast<uint64_t>(signed_size);
727 return s;
730 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
731 Status result;
732 base::FilePath src_file_path = CreateFilePath(src);
733 if (!::base::PathExists(src_file_path))
734 return result;
735 base::FilePath destination = CreateFilePath(dst);
737 Retrier retrier(kRenameFile, this);
738 base::File::Error error = base::File::FILE_OK;
739 do {
740 if (base::ReplaceFile(src_file_path, destination, &error))
741 return result;
742 } while (retrier.ShouldKeepTrying(error));
744 DCHECK(error != base::File::FILE_OK);
745 RecordOSError(kRenameFile, error);
746 char buf[100];
747 snprintf(buf,
748 sizeof(buf),
749 "Could not rename file: %s",
750 FileErrorString(error));
751 return MakeIOError(src, buf, kRenameFile, error);
754 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
755 *lock = NULL;
756 Status result;
757 int flags = ::base::File::FLAG_OPEN_ALWAYS |
758 ::base::File::FLAG_READ |
759 ::base::File::FLAG_WRITE;
760 ::base::File::Error error_code;
761 ::base::File file;
762 Retrier retrier(kLockFile, this);
763 do {
764 file.Initialize(CreateFilePath(fname), flags);
765 if (!file.IsValid())
766 error_code = file.error_details();
767 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
769 if (!file.IsValid()) {
770 if (error_code == ::base::File::FILE_ERROR_NOT_FOUND) {
771 ::base::FilePath parent = CreateFilePath(fname).DirName();
772 ::base::FilePath last_parent;
773 int num_missing_ancestors = 0;
774 do {
775 if (base::DirectoryExists(parent))
776 break;
777 ++num_missing_ancestors;
778 last_parent = parent;
779 parent = parent.DirName();
780 } while (parent != last_parent);
781 RecordLockFileAncestors(num_missing_ancestors);
784 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
785 error_code);
786 RecordOSError(kLockFile, error_code);
787 return result;
790 if (!locks_.Insert(fname)) {
791 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
792 return result;
795 Retrier lock_retrier = Retrier(kLockFile, this);
796 do {
797 error_code = file.Lock();
798 } while (error_code != ::base::File::FILE_OK &&
799 retrier.ShouldKeepTrying(error_code));
801 if (error_code != ::base::File::FILE_OK) {
802 locks_.Remove(fname);
803 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
804 error_code);
805 RecordOSError(kLockFile, error_code);
806 return result;
809 ChromiumFileLock* my_lock = new ChromiumFileLock;
810 my_lock->file_ = file.Pass();
811 my_lock->name_ = fname;
812 *lock = my_lock;
813 return result;
816 Status ChromiumEnv::UnlockFile(FileLock* lock) {
817 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
818 Status result;
820 ::base::File::Error error_code = my_lock->file_.Unlock();
821 if (error_code != ::base::File::FILE_OK) {
822 result =
823 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
824 RecordOSError(kUnlockFile, error_code);
826 bool removed = locks_.Remove(my_lock->name_);
827 DCHECK(removed);
828 delete my_lock;
829 return result;
832 Status ChromiumEnv::GetTestDirectory(std::string* path) {
833 mu_.Acquire();
834 if (test_directory_.empty()) {
835 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
836 &test_directory_)) {
837 mu_.Release();
838 RecordErrorAt(kGetTestDirectory);
839 return MakeIOError(
840 "Could not create temp directory.", "", kGetTestDirectory);
843 *path = FilePathToString(test_directory_);
844 mu_.Release();
845 return Status::OK();
848 Status ChromiumEnv::NewLogger(const std::string& fname,
849 leveldb::Logger** result) {
850 base::FilePath path = CreateFilePath(fname);
851 scoped_ptr<base::File> f(new base::File(
852 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
853 if (!f->IsValid()) {
854 *result = NULL;
855 RecordOSError(kNewLogger, f->error_details());
856 return MakeIOError(fname, "Unable to create log file", kNewLogger,
857 f->error_details());
858 } else {
859 *result = new leveldb::ChromiumLogger(f.release());
860 return Status::OK();
864 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
865 leveldb::SequentialFile** result) {
866 base::FilePath path = CreateFilePath(fname);
867 scoped_ptr<base::File> f(
868 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
869 if (!f->IsValid()) {
870 *result = NULL;
871 RecordOSError(kNewSequentialFile, f->error_details());
872 return MakeIOError(fname, "Unable to create sequential file",
873 kNewSequentialFile, f->error_details());
874 } else {
875 *result = new ChromiumSequentialFile(fname, f.release(), this);
876 return Status::OK();
880 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
881 #if defined(OS_POSIX)
882 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
883 #elif defined(OS_WIN)
884 // Windows is only limited by available memory
885 #else
886 #error "Need to determine limit to open files for this OS"
887 #endif
890 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
891 leveldb::RandomAccessFile** result) {
892 int flags = ::base::File::FLAG_READ | ::base::File::FLAG_OPEN;
893 ::base::File file(ChromiumEnv::CreateFilePath(fname), flags);
894 if (file.IsValid()) {
895 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
896 RecordOpenFilesLimit("Success");
897 return Status::OK();
899 ::base::File::Error error_code = file.error_details();
900 if (error_code == ::base::File::FILE_ERROR_TOO_MANY_OPENED)
901 RecordOpenFilesLimit("TooManyOpened");
902 else
903 RecordOpenFilesLimit("OtherError");
904 *result = NULL;
905 RecordOSError(kNewRandomAccessFile, error_code);
906 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
907 error_code);
910 Status ChromiumEnv::NewWritableFile(const std::string& fname,
911 leveldb::WritableFile** result) {
912 *result = NULL;
913 base::FilePath path = CreateFilePath(fname);
914 scoped_ptr<base::File> f(new base::File(
915 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
916 if (!f->IsValid()) {
917 RecordErrorAt(kNewWritableFile);
918 return MakeIOError(fname, "Unable to create writable file",
919 kNewWritableFile, f->error_details());
920 } else {
921 *result =
922 new ChromiumWritableFile(fname, f.release(), this, this, make_backup_);
923 return Status::OK();
927 uint64_t ChromiumEnv::NowMicros() {
928 return ::base::TimeTicks::Now().ToInternalValue();
931 void ChromiumEnv::SleepForMicroseconds(int micros) {
932 // Round up to the next millisecond.
933 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
936 void ChromiumEnv::RecordErrorAt(MethodID method) const {
937 GetMethodIOErrorHistogram()->Add(method);
940 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
941 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
944 void ChromiumEnv::RecordOSError(MethodID method,
945 base::File::Error error) const {
946 DCHECK_LT(error, 0);
947 RecordErrorAt(method);
948 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
951 void ChromiumEnv::RecordBackupResult(bool result) const {
952 std::string uma_name(name_);
953 uma_name.append(".TableBackup");
954 base::BooleanHistogram::FactoryGet(
955 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
958 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
959 int limit) const {
960 std::string uma_name(name_);
961 // TODO(dgrogan): This is probably not the best way to concatenate strings.
962 uma_name.append(".IOError.").append(MethodIDToString(method));
963 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
964 base::Histogram::kUmaTargetedHistogramFlag);
967 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
968 std::string uma_name(name_);
969 uma_name.append(".IOError");
970 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
971 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
974 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
975 const std::string& type) const {
976 std::string uma_name(name_);
977 uma_name.append(".MaxFDs.").append(type);
978 // These numbers make each bucket twice as large as the previous bucket.
979 const int kFirstEntry = 1;
980 const int kLastEntry = 65536;
981 const int kNumBuckets = 18;
982 return base::Histogram::FactoryGet(
983 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
984 base::Histogram::kUmaTargetedHistogramFlag);
987 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
988 std::string uma_name(name_);
989 uma_name.append(".LockFileAncestorsNotFound");
990 const int kMin = 1;
991 const int kMax = 10;
992 const int kNumBuckets = 11;
993 return base::LinearHistogram::FactoryGet(
994 uma_name, kMin, kMax, kNumBuckets,
995 base::Histogram::kUmaTargetedHistogramFlag);
998 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
999 std::string uma_name(name_);
1000 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1001 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
1003 const int kBucketSizeMillis = 25;
1004 // Add 2, 1 for each of the buckets <1 and >max.
1005 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
1006 return base::Histogram::FactoryTimeGet(
1007 uma_name, base::TimeDelta::FromMilliseconds(1),
1008 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
1009 kNumBuckets,
1010 base::Histogram::kUmaTargetedHistogramFlag);
1013 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
1014 MethodID method) const {
1015 std::string uma_name(name_);
1016 uma_name.append(".RetryRecoveredFromErrorIn")
1017 .append(MethodIDToString(method));
1018 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1019 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1022 class Thread : public ::base::PlatformThread::Delegate {
1023 public:
1024 Thread(void (*function)(void* arg), void* arg)
1025 : function_(function), arg_(arg) {
1026 ::base::PlatformThreadHandle handle;
1027 bool success = ::base::PlatformThread::Create(0, this, &handle);
1028 DCHECK(success);
1030 virtual ~Thread() {}
1031 void ThreadMain() override {
1032 (*function_)(arg_);
1033 delete this;
1036 private:
1037 void (*function_)(void* arg);
1038 void* arg_;
1041 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1042 mu_.Acquire();
1044 // Start background thread if necessary
1045 if (!started_bgthread_) {
1046 started_bgthread_ = true;
1047 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1050 // If the queue is currently empty, the background thread may currently be
1051 // waiting.
1052 if (queue_.empty()) {
1053 bgsignal_.Signal();
1056 // Add to priority queue
1057 queue_.push_back(BGItem());
1058 queue_.back().function = function;
1059 queue_.back().arg = arg;
1061 mu_.Release();
1064 void ChromiumEnv::BGThread() {
1065 base::PlatformThread::SetName(name_.c_str());
1067 while (true) {
1068 // Wait until there is an item that is ready to run
1069 mu_.Acquire();
1070 while (queue_.empty()) {
1071 bgsignal_.Wait();
1074 void (*function)(void*) = queue_.front().function;
1075 void* arg = queue_.front().arg;
1076 queue_.pop_front();
1078 mu_.Release();
1079 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1080 (*function)(arg);
1084 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1085 new Thread(function, arg); // Will self-delete.
1088 static std::string GetDirName(const std::string& filename) {
1089 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
1090 return FilePathToString(file.DirName());
1093 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
1094 base::AutoLock auto_lock(directory_sync_lock_);
1095 directories_needing_sync_.insert(GetDirName(filename));
1098 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
1099 base::AutoLock auto_lock(directory_sync_lock_);
1100 return ContainsKey(directories_needing_sync_, GetDirName(filename));
1103 void ChromiumEnv::DidSyncDir(const std::string& filename) {
1104 base::AutoLock auto_lock(directory_sync_lock_);
1105 directories_needing_sync_.erase(GetDirName(filename));
1108 } // namespace leveldb_env
1110 namespace leveldb {
1112 Env* IDBEnv() {
1113 return leveldb_env::idb_env.Pointer();
1116 Env* Env::Default() {
1117 return leveldb_env::default_env.Pointer();
1120 } // namespace leveldb