Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
bloba12edf486182b04c2d58490300a67a9d72c541f0
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 base::FilePath;
24 using leveldb::FileLock;
25 using leveldb::Slice;
26 using leveldb::Status;
28 namespace leveldb_env {
30 namespace {
32 const FilePath::CharType backup_table_extension[] = FILE_PATH_LITERAL(".bak");
33 const FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
35 static const 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(const FilePath& dir_param,
49 std::vector<FilePath>* result) {
50 result->clear();
51 #if defined(OS_WIN)
52 FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
53 WIN32_FIND_DATA find_data;
54 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
55 if (find_handle == INVALID_HANDLE_VALUE) {
56 DWORD last_error = GetLastError();
57 if (last_error == ERROR_FILE_NOT_FOUND)
58 return base::File::FILE_OK;
59 return base::File::OSErrorToFileError(last_error);
61 do {
62 FilePath filepath(find_data.cFileName);
63 FilePath::StringType basename = filepath.BaseName().value();
64 if (basename == FILE_PATH_LITERAL(".") ||
65 basename == FILE_PATH_LITERAL(".."))
66 continue;
67 result->push_back(filepath.BaseName());
68 } while (FindNextFile(find_handle, &find_data));
69 DWORD last_error = GetLastError();
70 base::File::Error return_value = base::File::FILE_OK;
71 if (last_error != ERROR_NO_MORE_FILES)
72 return_value = base::File::OSErrorToFileError(last_error);
73 FindClose(find_handle);
74 return return_value;
75 #else
76 const std::string dir_string = dir_param.AsUTF8Unsafe();
77 DIR* dir = opendir(dir_string.c_str());
78 if (!dir)
79 return base::File::OSErrorToFileError(errno);
80 struct dirent dent_buf;
81 struct dirent* dent;
82 int readdir_result;
83 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
84 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
85 continue;
86 result->push_back(FilePath::FromUTF8Unsafe(dent->d_name));
88 int saved_errno = errno;
89 closedir(dir);
90 if (readdir_result != 0)
91 return base::File::OSErrorToFileError(saved_errno);
92 return base::File::FILE_OK;
93 #endif
96 class ChromiumFileLock : public FileLock {
97 public:
98 base::File file_;
99 std::string name_;
102 class Retrier {
103 public:
104 Retrier(MethodID method, RetrierProvider* provider)
105 : start_(base::TimeTicks::Now()),
106 limit_(start_ + base::TimeDelta::FromMilliseconds(
107 provider->MaxRetryTimeMillis())),
108 last_(start_),
109 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
110 success_(true),
111 method_(method),
112 last_error_(base::File::FILE_OK),
113 provider_(provider) {}
114 ~Retrier() {
115 if (success_) {
116 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
117 if (last_error_ != base::File::FILE_OK) {
118 DCHECK_LT(last_error_, 0);
119 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
123 bool ShouldKeepTrying(base::File::Error last_error) {
124 DCHECK_NE(last_error, base::File::FILE_OK);
125 last_error_ = last_error;
126 if (last_ < limit_) {
127 base::PlatformThread::Sleep(time_to_sleep_);
128 last_ = base::TimeTicks::Now();
129 return true;
131 success_ = false;
132 return false;
135 private:
136 base::TimeTicks start_;
137 base::TimeTicks limit_;
138 base::TimeTicks last_;
139 base::TimeDelta time_to_sleep_;
140 bool success_;
141 MethodID method_;
142 base::File::Error last_error_;
143 RetrierProvider* provider_;
146 class ChromiumSequentialFile : public leveldb::SequentialFile {
147 public:
148 ChromiumSequentialFile(const std::string& fname,
149 base::File* f,
150 const UMALogger* uma_logger)
151 : filename_(fname), file_(f), uma_logger_(uma_logger) {}
152 virtual ~ChromiumSequentialFile() {}
154 Status Read(size_t n, Slice* result, char* scratch) override {
155 int bytes_read = file_->ReadAtCurrentPosNoBestEffort(scratch, n);
156 if (bytes_read == -1) {
157 base::File::Error error = LastFileError();
158 uma_logger_->RecordErrorAt(kSequentialFileRead);
159 return MakeIOError(filename_, base::File::ErrorToString(error),
160 kSequentialFileRead, error);
161 } else {
162 *result = Slice(scratch, bytes_read);
163 return Status::OK();
167 Status Skip(uint64_t n) override {
168 if (file_->Seek(base::File::FROM_CURRENT, n) == -1) {
169 base::File::Error error = LastFileError();
170 uma_logger_->RecordErrorAt(kSequentialFileSkip);
171 return MakeIOError(filename_, base::File::ErrorToString(error),
172 kSequentialFileSkip, error);
173 } else {
174 return Status::OK();
178 private:
179 std::string filename_;
180 scoped_ptr<base::File> file_;
181 const UMALogger* uma_logger_;
184 class ChromiumRandomAccessFile : public leveldb::RandomAccessFile {
185 public:
186 ChromiumRandomAccessFile(const std::string& fname,
187 base::File file,
188 const UMALogger* uma_logger)
189 : filename_(fname), file_(file.Pass()), uma_logger_(uma_logger) {}
190 virtual ~ChromiumRandomAccessFile() {}
192 Status Read(uint64_t offset,
193 size_t n,
194 Slice* result,
195 char* scratch) const override {
196 Status s;
197 int r = file_.Read(offset, scratch, n);
198 *result = Slice(scratch, (r < 0) ? 0 : r);
199 if (r < 0) {
200 // An error: return a non-ok status
201 s = MakeIOError(filename_, "Could not perform read",
202 kRandomAccessFileRead);
203 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
205 return s;
208 private:
209 std::string filename_;
210 mutable base::File file_;
211 const UMALogger* uma_logger_;
214 class ChromiumWritableFile : public leveldb::WritableFile {
215 public:
216 ChromiumWritableFile(const std::string& fname,
217 base::File* f,
218 const UMALogger* uma_logger,
219 WriteTracker* tracker,
220 bool make_backup);
221 virtual ~ChromiumWritableFile() {}
222 leveldb::Status Append(const leveldb::Slice& data) override;
223 leveldb::Status Close() override;
224 leveldb::Status Flush() override;
225 leveldb::Status Sync() override;
227 private:
228 enum Type { kManifest, kTable, kOther };
229 leveldb::Status SyncParent();
231 std::string filename_;
232 scoped_ptr<base::File> file_;
233 const UMALogger* uma_logger_;
234 WriteTracker* tracker_;
235 Type file_type_;
236 std::string parent_dir_;
237 bool make_backup_;
240 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
241 base::File* f,
242 const UMALogger* uma_logger,
243 WriteTracker* tracker,
244 bool make_backup)
245 : filename_(fname),
246 file_(f),
247 uma_logger_(uma_logger),
248 tracker_(tracker),
249 file_type_(kOther),
250 make_backup_(make_backup) {
251 FilePath path = FilePath::FromUTF8Unsafe(fname);
252 if (path.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
253 file_type_ = kManifest;
254 else if (path.MatchesExtension(table_extension))
255 file_type_ = kTable;
256 if (file_type_ != kManifest)
257 tracker_->DidCreateNewFile(filename_);
258 parent_dir_ = FilePath::FromUTF8Unsafe(fname).DirName().AsUTF8Unsafe();
261 Status ChromiumWritableFile::SyncParent() {
262 TRACE_EVENT0("leveldb", "SyncParent");
263 #if defined(OS_POSIX)
264 FilePath path = FilePath::FromUTF8Unsafe(parent_dir_);
265 base::File f(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
266 if (!f.IsValid()) {
267 return MakeIOError(parent_dir_, "Unable to open directory", kSyncParent,
268 f.error_details());
270 if (!f.Flush()) {
271 base::File::Error error = LastFileError();
272 return MakeIOError(parent_dir_, base::File::ErrorToString(error),
273 kSyncParent, error);
275 #endif
276 return Status::OK();
279 Status ChromiumWritableFile::Append(const Slice& data) {
280 if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
281 Status s = SyncParent();
282 if (!s.ok())
283 return s;
284 tracker_->DidSyncDir(filename_);
287 int bytes_written = file_->WriteAtCurrentPos(data.data(), data.size());
288 if (bytes_written != data.size()) {
289 base::File::Error error = LastFileError();
290 uma_logger_->RecordOSError(kWritableFileAppend, error);
291 return MakeIOError(filename_, base::File::ErrorToString(error),
292 kWritableFileAppend, error);
295 return Status::OK();
298 Status ChromiumWritableFile::Close() {
299 file_->Close();
300 return Status::OK();
303 Status ChromiumWritableFile::Flush() {
304 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
305 // flush.
306 return Status::OK();
309 Status ChromiumWritableFile::Sync() {
310 TRACE_EVENT0("leveldb", "WritableFile::Sync");
312 if (!file_->Flush()) {
313 base::File::Error error = LastFileError();
314 uma_logger_->RecordErrorAt(kWritableFileSync);
315 return MakeIOError(filename_, base::File::ErrorToString(error),
316 kWritableFileSync, error);
319 if (make_backup_ && file_type_ == kTable)
320 uma_logger_->RecordBackupResult(ChromiumEnv::MakeBackup(filename_));
322 return Status::OK();
325 class IDBEnv : public ChromiumEnv {
326 public:
327 IDBEnv() : ChromiumEnv() {
328 name_ = "LevelDBEnv.IDB";
329 make_backup_ = true;
333 base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
335 base::LazyInstance<ChromiumEnv>::Leaky default_env = LAZY_INSTANCE_INITIALIZER;
337 } // unnamed namespace
339 const char* MethodIDToString(MethodID method) {
340 switch (method) {
341 case kSequentialFileRead:
342 return "SequentialFileRead";
343 case kSequentialFileSkip:
344 return "SequentialFileSkip";
345 case kRandomAccessFileRead:
346 return "RandomAccessFileRead";
347 case kWritableFileAppend:
348 return "WritableFileAppend";
349 case kWritableFileClose:
350 return "WritableFileClose";
351 case kWritableFileFlush:
352 return "WritableFileFlush";
353 case kWritableFileSync:
354 return "WritableFileSync";
355 case kNewSequentialFile:
356 return "NewSequentialFile";
357 case kNewRandomAccessFile:
358 return "NewRandomAccessFile";
359 case kNewWritableFile:
360 return "NewWritableFile";
361 case kNewAppendableFile:
362 return "NewAppendableFile";
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 leveldb::Status& status,
414 MethodID* method_param,
415 int* error) {
416 const std::string status_string = status.ToString();
417 int method;
418 if (RE2::PartialMatch(status_string.c_str(), "ChromeMethodOnly: (\\d+)",
419 &method)) {
420 *method_param = static_cast<MethodID>(method);
421 return METHOD_ONLY;
423 if (RE2::PartialMatch(status_string.c_str(),
424 "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method,
425 error)) {
426 *error = -*error;
427 *method_param = static_cast<MethodID>(method);
428 return METHOD_AND_PFE;
430 if (RE2::PartialMatch(status_string.c_str(),
431 "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method,
432 error)) {
433 *method_param = static_cast<MethodID>(method);
434 return METHOD_AND_ERRNO;
436 return NONE;
439 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
440 // change the order because indices into this array have been recorded in uma
441 // histograms.
442 const char* patterns[] = {
443 "missing files",
444 "log record too small",
445 "corrupted internal key",
446 "partial record",
447 "missing start of fragmented record",
448 "error in middle of record",
449 "unknown record type",
450 "truncated record at end",
451 "bad record length",
452 "VersionEdit",
453 "FileReader invoked with unexpected value",
454 "corrupted key",
455 "CURRENT file does not end with newline",
456 "no meta-nextfile entry",
457 "no meta-lognumber entry",
458 "no last-sequence-number entry",
459 "malformed WriteBatch",
460 "bad WriteBatch Put",
461 "bad WriteBatch Delete",
462 "unknown WriteBatch tag",
463 "WriteBatch has wrong count",
464 "bad entry in block",
465 "bad block contents",
466 "bad block handle",
467 "truncated block read",
468 "block checksum mismatch",
469 "checksum mismatch",
470 "corrupted compressed block contents",
471 "bad block type",
472 "bad magic number",
473 "file is too short",
476 // Returns 1-based index into the above array or 0 if nothing matches.
477 int GetCorruptionCode(const leveldb::Status& status) {
478 DCHECK(!status.IsIOError());
479 DCHECK(!status.ok());
480 const int kOtherError = 0;
481 int error = kOtherError;
482 const std::string& str_error = status.ToString();
483 const size_t kNumPatterns = arraysize(patterns);
484 for (size_t i = 0; i < kNumPatterns; ++i) {
485 if (str_error.find(patterns[i]) != std::string::npos) {
486 error = i + 1;
487 break;
490 return error;
493 int GetNumCorruptionCodes() {
494 // + 1 for the "other" error that is returned when a corruption message
495 // doesn't match any of the patterns.
496 return arraysize(patterns) + 1;
499 std::string GetCorruptionMessage(const leveldb::Status& status) {
500 int code = GetCorruptionCode(status);
501 if (code == 0)
502 return "Unknown corruption";
503 return patterns[code - 1];
506 bool IndicatesDiskFull(const leveldb::Status& status) {
507 if (status.ok())
508 return false;
509 leveldb_env::MethodID method;
510 int error = -1;
511 leveldb_env::ErrorParsingResult result =
512 leveldb_env::ParseMethodAndError(status, &method, &error);
513 return (result == leveldb_env::METHOD_AND_PFE &&
514 static_cast<base::File::Error>(error) ==
515 base::File::FILE_ERROR_NO_SPACE) ||
516 (result == leveldb_env::METHOD_AND_ERRNO && error == ENOSPC);
519 bool ChromiumEnv::MakeBackup(const std::string& fname) {
520 FilePath original_table_name = FilePath::FromUTF8Unsafe(fname);
521 FilePath backup_table_name =
522 original_table_name.ReplaceExtension(backup_table_extension);
523 return base::CopyFile(original_table_name, backup_table_name);
526 ChromiumEnv::ChromiumEnv()
527 : name_("LevelDBEnv"),
528 make_backup_(false),
529 bgsignal_(&mu_),
530 started_bgthread_(false),
531 kMaxRetryTimeMillis(1000) {
534 ChromiumEnv::~ChromiumEnv() {
535 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
536 // ensure that behavior isn't accidentally changed, but there's an instance in
537 // a unit test that is deleted.
540 bool ChromiumEnv::FileExists(const std::string& fname) {
541 return base::PathExists(FilePath::FromUTF8Unsafe(fname));
544 const char* ChromiumEnv::FileErrorString(base::File::Error error) {
545 switch (error) {
546 case base::File::FILE_ERROR_FAILED:
547 return "No further details.";
548 case base::File::FILE_ERROR_IN_USE:
549 return "File currently in use.";
550 case base::File::FILE_ERROR_EXISTS:
551 return "File already exists.";
552 case base::File::FILE_ERROR_NOT_FOUND:
553 return "File not found.";
554 case base::File::FILE_ERROR_ACCESS_DENIED:
555 return "Access denied.";
556 case base::File::FILE_ERROR_TOO_MANY_OPENED:
557 return "Too many files open.";
558 case base::File::FILE_ERROR_NO_MEMORY:
559 return "Out of memory.";
560 case base::File::FILE_ERROR_NO_SPACE:
561 return "No space left on drive.";
562 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
563 return "Not a directory.";
564 case base::File::FILE_ERROR_INVALID_OPERATION:
565 return "Invalid operation.";
566 case base::File::FILE_ERROR_SECURITY:
567 return "Security error.";
568 case base::File::FILE_ERROR_ABORT:
569 return "File operation aborted.";
570 case base::File::FILE_ERROR_NOT_A_FILE:
571 return "The supplied path was not a file.";
572 case base::File::FILE_ERROR_NOT_EMPTY:
573 return "The file was not empty.";
574 case base::File::FILE_ERROR_INVALID_URL:
575 return "Invalid URL.";
576 case base::File::FILE_ERROR_IO:
577 return "OS or hardware error.";
578 case base::File::FILE_OK:
579 return "OK.";
580 case base::File::FILE_ERROR_MAX:
581 NOTREACHED();
583 NOTIMPLEMENTED();
584 return "Unknown error.";
587 FilePath ChromiumEnv::RestoreFromBackup(const FilePath& base_name) {
588 FilePath table_name = base_name.AddExtension(table_extension);
589 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
590 table_name);
591 std::string uma_name(name_);
592 uma_name.append(".TableRestore");
593 base::BooleanHistogram::FactoryGet(
594 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
595 return table_name;
598 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
599 std::vector<std::string>* dir_entries) {
600 std::set<FilePath> tables_found;
601 std::set<FilePath> backups_found;
602 for (const std::string& entry : *dir_entries) {
603 FilePath current = FilePath::FromUTF8Unsafe(entry);
604 if (current.MatchesExtension(table_extension))
605 tables_found.insert(current.RemoveExtension());
606 if (current.MatchesExtension(backup_table_extension))
607 backups_found.insert(current.RemoveExtension());
609 std::set<FilePath> backups_only =
610 base::STLSetDifference<std::set<FilePath>>(backups_found, tables_found);
612 if (backups_only.size()) {
613 std::string uma_name(name_);
614 uma_name.append(".MissingFiles");
615 int num_missing_files =
616 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
617 base::Histogram::FactoryGet(uma_name,
618 1 /*min*/,
619 100 /*max*/,
620 8 /*num_buckets*/,
621 base::Histogram::kUmaTargetedHistogramFlag)
622 ->Add(num_missing_files);
624 FilePath dir_path = FilePath::FromUTF8Unsafe(dir);
625 for (const FilePath& backup : backups_only) {
626 FilePath restored_table_name = RestoreFromBackup(dir_path.Append(backup));
627 dir_entries->push_back(restored_table_name.BaseName().AsUTF8Unsafe());
631 Status ChromiumEnv::GetChildren(const std::string& dir,
632 std::vector<std::string>* result) {
633 std::vector<FilePath> entries;
634 base::File::Error error =
635 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir), &entries);
636 if (error != base::File::FILE_OK) {
637 RecordOSError(kGetChildren, error);
638 return MakeIOError(dir, "Could not open/read directory", kGetChildren,
639 error);
642 result->clear();
643 for (const auto& entry : entries)
644 result->push_back(entry.BaseName().AsUTF8Unsafe());
646 if (make_backup_)
647 RestoreIfNecessary(dir, result);
649 return Status::OK();
652 Status ChromiumEnv::DeleteFile(const std::string& fname) {
653 Status result;
654 FilePath fname_filepath = FilePath::FromUTF8Unsafe(fname);
655 // TODO(jorlow): Should we assert this is a file?
656 if (!base::DeleteFile(fname_filepath, false)) {
657 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
658 RecordErrorAt(kDeleteFile);
660 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
661 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
662 false);
664 return result;
667 Status ChromiumEnv::CreateDir(const std::string& name) {
668 Status result;
669 base::File::Error error = base::File::FILE_OK;
670 Retrier retrier(kCreateDir, this);
671 do {
672 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name),
673 &error))
674 return result;
675 } while (retrier.ShouldKeepTrying(error));
676 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
677 RecordOSError(kCreateDir, error);
678 return result;
681 Status ChromiumEnv::DeleteDir(const std::string& name) {
682 Status result;
683 // TODO(jorlow): Should we assert this is a directory?
684 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name), false)) {
685 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
686 RecordErrorAt(kDeleteDir);
688 return result;
691 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
692 Status s;
693 int64_t signed_size;
694 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname), &signed_size)) {
695 *size = 0;
696 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
697 RecordErrorAt(kGetFileSize);
698 } else {
699 *size = static_cast<uint64_t>(signed_size);
701 return s;
704 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
705 Status result;
706 FilePath src_file_path = FilePath::FromUTF8Unsafe(src);
707 if (!base::PathExists(src_file_path))
708 return result;
709 FilePath destination = FilePath::FromUTF8Unsafe(dst);
711 Retrier retrier(kRenameFile, this);
712 base::File::Error error = base::File::FILE_OK;
713 do {
714 if (base::ReplaceFile(src_file_path, destination, &error))
715 return result;
716 } while (retrier.ShouldKeepTrying(error));
718 DCHECK(error != base::File::FILE_OK);
719 RecordOSError(kRenameFile, error);
720 char buf[100];
721 snprintf(buf,
722 sizeof(buf),
723 "Could not rename file: %s",
724 FileErrorString(error));
725 return MakeIOError(src, buf, kRenameFile, error);
728 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
729 *lock = NULL;
730 Status result;
731 int flags = base::File::FLAG_OPEN_ALWAYS |
732 base::File::FLAG_READ |
733 base::File::FLAG_WRITE;
734 base::File::Error error_code;
735 base::File file;
736 Retrier retrier(kLockFile, this);
737 do {
738 file.Initialize(FilePath::FromUTF8Unsafe(fname), flags);
739 if (!file.IsValid())
740 error_code = file.error_details();
741 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
743 if (!file.IsValid()) {
744 if (error_code == base::File::FILE_ERROR_NOT_FOUND) {
745 FilePath parent = FilePath::FromUTF8Unsafe(fname).DirName();
746 FilePath last_parent;
747 int num_missing_ancestors = 0;
748 do {
749 if (base::DirectoryExists(parent))
750 break;
751 ++num_missing_ancestors;
752 last_parent = parent;
753 parent = parent.DirName();
754 } while (parent != last_parent);
755 RecordLockFileAncestors(num_missing_ancestors);
758 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
759 error_code);
760 RecordOSError(kLockFile, error_code);
761 return result;
764 if (!locks_.Insert(fname)) {
765 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
766 return result;
769 Retrier lock_retrier = Retrier(kLockFile, this);
770 do {
771 error_code = file.Lock();
772 } while (error_code != base::File::FILE_OK &&
773 retrier.ShouldKeepTrying(error_code));
775 if (error_code != base::File::FILE_OK) {
776 locks_.Remove(fname);
777 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
778 error_code);
779 RecordOSError(kLockFile, error_code);
780 return result;
783 ChromiumFileLock* my_lock = new ChromiumFileLock;
784 my_lock->file_ = file.Pass();
785 my_lock->name_ = fname;
786 *lock = my_lock;
787 return result;
790 Status ChromiumEnv::UnlockFile(FileLock* lock) {
791 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
792 Status result;
794 base::File::Error error_code = my_lock->file_.Unlock();
795 if (error_code != base::File::FILE_OK) {
796 result =
797 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
798 RecordOSError(kUnlockFile, error_code);
800 bool removed = locks_.Remove(my_lock->name_);
801 DCHECK(removed);
802 delete my_lock;
803 return result;
806 Status ChromiumEnv::GetTestDirectory(std::string* path) {
807 mu_.Acquire();
808 if (test_directory_.empty()) {
809 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
810 &test_directory_)) {
811 mu_.Release();
812 RecordErrorAt(kGetTestDirectory);
813 return MakeIOError(
814 "Could not create temp directory.", "", kGetTestDirectory);
817 *path = test_directory_.AsUTF8Unsafe();
818 mu_.Release();
819 return Status::OK();
822 Status ChromiumEnv::NewLogger(const std::string& fname,
823 leveldb::Logger** result) {
824 FilePath path = FilePath::FromUTF8Unsafe(fname);
825 scoped_ptr<base::File> f(new base::File(
826 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
827 if (!f->IsValid()) {
828 *result = NULL;
829 RecordOSError(kNewLogger, f->error_details());
830 return MakeIOError(fname, "Unable to create log file", kNewLogger,
831 f->error_details());
832 } else {
833 *result = new leveldb::ChromiumLogger(f.release());
834 return Status::OK();
838 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
839 leveldb::SequentialFile** result) {
840 FilePath path = FilePath::FromUTF8Unsafe(fname);
841 scoped_ptr<base::File> f(
842 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
843 if (!f->IsValid()) {
844 *result = NULL;
845 RecordOSError(kNewSequentialFile, f->error_details());
846 return MakeIOError(fname, "Unable to create sequential file",
847 kNewSequentialFile, f->error_details());
848 } else {
849 *result = new ChromiumSequentialFile(fname, f.release(), this);
850 return Status::OK();
854 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
855 #if defined(OS_POSIX)
856 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
857 #elif defined(OS_WIN)
858 // Windows is only limited by available memory
859 #else
860 #error "Need to determine limit to open files for this OS"
861 #endif
864 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
865 leveldb::RandomAccessFile** result) {
866 int flags = base::File::FLAG_READ | base::File::FLAG_OPEN;
867 base::File file(FilePath::FromUTF8Unsafe(fname), flags);
868 if (file.IsValid()) {
869 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
870 RecordOpenFilesLimit("Success");
871 return Status::OK();
873 base::File::Error error_code = file.error_details();
874 if (error_code == base::File::FILE_ERROR_TOO_MANY_OPENED)
875 RecordOpenFilesLimit("TooManyOpened");
876 else
877 RecordOpenFilesLimit("OtherError");
878 *result = NULL;
879 RecordOSError(kNewRandomAccessFile, error_code);
880 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
881 error_code);
884 Status ChromiumEnv::NewWritableFile(const std::string& fname,
885 leveldb::WritableFile** result) {
886 *result = NULL;
887 FilePath path = FilePath::FromUTF8Unsafe(fname);
888 scoped_ptr<base::File> f(new base::File(
889 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
890 if (!f->IsValid()) {
891 RecordErrorAt(kNewWritableFile);
892 return MakeIOError(fname, "Unable to create writable file",
893 kNewWritableFile, f->error_details());
894 } else {
895 *result =
896 new ChromiumWritableFile(fname, f.release(), this, this, make_backup_);
897 return Status::OK();
901 uint64_t ChromiumEnv::NowMicros() {
902 return base::TimeTicks::Now().ToInternalValue();
905 void ChromiumEnv::SleepForMicroseconds(int micros) {
906 // Round up to the next millisecond.
907 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
910 void ChromiumEnv::RecordErrorAt(MethodID method) const {
911 GetMethodIOErrorHistogram()->Add(method);
914 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
915 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
918 void ChromiumEnv::RecordOSError(MethodID method,
919 base::File::Error error) const {
920 DCHECK_LT(error, 0);
921 RecordErrorAt(method);
922 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
925 void ChromiumEnv::RecordBackupResult(bool result) const {
926 std::string uma_name(name_);
927 uma_name.append(".TableBackup");
928 base::BooleanHistogram::FactoryGet(
929 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
932 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
933 int limit) const {
934 std::string uma_name(name_);
935 // TODO(dgrogan): This is probably not the best way to concatenate strings.
936 uma_name.append(".IOError.").append(MethodIDToString(method));
937 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
938 base::Histogram::kUmaTargetedHistogramFlag);
941 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
942 std::string uma_name(name_);
943 uma_name.append(".IOError");
944 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
945 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
948 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
949 const std::string& type) const {
950 std::string uma_name(name_);
951 uma_name.append(".MaxFDs.").append(type);
952 // These numbers make each bucket twice as large as the previous bucket.
953 const int kFirstEntry = 1;
954 const int kLastEntry = 65536;
955 const int kNumBuckets = 18;
956 return base::Histogram::FactoryGet(
957 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
958 base::Histogram::kUmaTargetedHistogramFlag);
961 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
962 std::string uma_name(name_);
963 uma_name.append(".LockFileAncestorsNotFound");
964 const int kMin = 1;
965 const int kMax = 10;
966 const int kNumBuckets = 11;
967 return base::LinearHistogram::FactoryGet(
968 uma_name, kMin, kMax, kNumBuckets,
969 base::Histogram::kUmaTargetedHistogramFlag);
972 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
973 std::string uma_name(name_);
974 // TODO(dgrogan): This is probably not the best way to concatenate strings.
975 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
977 const int kBucketSizeMillis = 25;
978 // Add 2, 1 for each of the buckets <1 and >max.
979 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
980 return base::Histogram::FactoryTimeGet(
981 uma_name, base::TimeDelta::FromMilliseconds(1),
982 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
983 kNumBuckets,
984 base::Histogram::kUmaTargetedHistogramFlag);
987 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
988 MethodID method) const {
989 std::string uma_name(name_);
990 uma_name.append(".RetryRecoveredFromErrorIn")
991 .append(MethodIDToString(method));
992 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
993 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
996 class Thread : public base::PlatformThread::Delegate {
997 public:
998 Thread(void (*function)(void* arg), void* arg)
999 : function_(function), arg_(arg) {
1000 base::PlatformThreadHandle handle;
1001 bool success = base::PlatformThread::Create(0, this, &handle);
1002 DCHECK(success);
1004 virtual ~Thread() {}
1005 void ThreadMain() override {
1006 (*function_)(arg_);
1007 delete this;
1010 private:
1011 void (*function_)(void* arg);
1012 void* arg_;
1015 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1016 mu_.Acquire();
1018 // Start background thread if necessary
1019 if (!started_bgthread_) {
1020 started_bgthread_ = true;
1021 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1024 // If the queue is currently empty, the background thread may currently be
1025 // waiting.
1026 if (queue_.empty()) {
1027 bgsignal_.Signal();
1030 // Add to priority queue
1031 queue_.push_back(BGItem());
1032 queue_.back().function = function;
1033 queue_.back().arg = arg;
1035 mu_.Release();
1038 void ChromiumEnv::BGThread() {
1039 base::PlatformThread::SetName(name_.c_str());
1041 while (true) {
1042 // Wait until there is an item that is ready to run
1043 mu_.Acquire();
1044 while (queue_.empty()) {
1045 bgsignal_.Wait();
1048 void (*function)(void*) = queue_.front().function;
1049 void* arg = queue_.front().arg;
1050 queue_.pop_front();
1052 mu_.Release();
1053 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1054 (*function)(arg);
1058 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1059 new Thread(function, arg); // Will self-delete.
1062 static std::string GetDirName(const std::string& filename) {
1063 return FilePath::FromUTF8Unsafe(filename).DirName().AsUTF8Unsafe();
1066 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
1067 base::AutoLock auto_lock(directory_sync_lock_);
1068 directories_needing_sync_.insert(GetDirName(filename));
1071 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
1072 base::AutoLock auto_lock(directory_sync_lock_);
1073 return ContainsKey(directories_needing_sync_, GetDirName(filename));
1076 void ChromiumEnv::DidSyncDir(const std::string& filename) {
1077 base::AutoLock auto_lock(directory_sync_lock_);
1078 directories_needing_sync_.erase(GetDirName(filename));
1081 } // namespace leveldb_env
1083 namespace leveldb {
1085 Env* IDBEnv() {
1086 return leveldb_env::idb_env.Pointer();
1089 Env* Env::Default() {
1090 return leveldb_env::default_env.Pointer();
1093 } // namespace leveldb