Add per-user preferences support.
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob42bdb42269373db25644e415dc94d8b5e907741b
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/files/file_util.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/shared_memory.h"
15 #include "base/metrics/histogram.h"
16 #include "base/process/process_metrics.h"
17 #include "base/stl_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/trace_event/trace_event.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 base::File::Error* 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 int parsed_error;
424 if (RE2::PartialMatch(status_string.c_str(),
425 "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method,
426 &parsed_error)) {
427 *method_param = static_cast<MethodID>(method);
428 *error = static_cast<base::File::Error>(-parsed_error);
429 DCHECK_LT(*error, base::File::FILE_OK);
430 DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
431 return METHOD_AND_PFE;
433 return NONE;
436 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
437 // change the order because indices into this array have been recorded in uma
438 // histograms.
439 const char* patterns[] = {
440 "missing files",
441 "log record too small",
442 "corrupted internal key",
443 "partial record",
444 "missing start of fragmented record",
445 "error in middle of record",
446 "unknown record type",
447 "truncated record at end",
448 "bad record length",
449 "VersionEdit",
450 "FileReader invoked with unexpected value",
451 "corrupted key",
452 "CURRENT file does not end with newline",
453 "no meta-nextfile entry",
454 "no meta-lognumber entry",
455 "no last-sequence-number entry",
456 "malformed WriteBatch",
457 "bad WriteBatch Put",
458 "bad WriteBatch Delete",
459 "unknown WriteBatch tag",
460 "WriteBatch has wrong count",
461 "bad entry in block",
462 "bad block contents",
463 "bad block handle",
464 "truncated block read",
465 "block checksum mismatch",
466 "checksum mismatch",
467 "corrupted compressed block contents",
468 "bad block type",
469 "bad magic number",
470 "file is too short",
473 // Returns 1-based index into the above array or 0 if nothing matches.
474 int GetCorruptionCode(const leveldb::Status& status) {
475 DCHECK(!status.IsIOError());
476 DCHECK(!status.ok());
477 const int kOtherError = 0;
478 int error = kOtherError;
479 const std::string& str_error = status.ToString();
480 const size_t kNumPatterns = arraysize(patterns);
481 for (size_t i = 0; i < kNumPatterns; ++i) {
482 if (str_error.find(patterns[i]) != std::string::npos) {
483 error = i + 1;
484 break;
487 return error;
490 int GetNumCorruptionCodes() {
491 // + 1 for the "other" error that is returned when a corruption message
492 // doesn't match any of the patterns.
493 return arraysize(patterns) + 1;
496 std::string GetCorruptionMessage(const leveldb::Status& status) {
497 int code = GetCorruptionCode(status);
498 if (code == 0)
499 return "Unknown corruption";
500 return patterns[code - 1];
503 bool IndicatesDiskFull(const leveldb::Status& status) {
504 if (status.ok())
505 return false;
506 leveldb_env::MethodID method;
507 base::File::Error error = base::File::FILE_OK;
508 leveldb_env::ErrorParsingResult result =
509 leveldb_env::ParseMethodAndError(status, &method, &error);
510 return (result == leveldb_env::METHOD_AND_PFE &&
511 static_cast<base::File::Error>(error) ==
512 base::File::FILE_ERROR_NO_SPACE);
515 bool ChromiumEnv::MakeBackup(const std::string& fname) {
516 FilePath original_table_name = FilePath::FromUTF8Unsafe(fname);
517 FilePath backup_table_name =
518 original_table_name.ReplaceExtension(backup_table_extension);
519 return base::CopyFile(original_table_name, backup_table_name);
522 ChromiumEnv::ChromiumEnv()
523 : name_("LevelDBEnv"),
524 make_backup_(false),
525 bgsignal_(&mu_),
526 started_bgthread_(false),
527 kMaxRetryTimeMillis(1000) {
530 ChromiumEnv::~ChromiumEnv() {
531 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
532 // ensure that behavior isn't accidentally changed, but there's an instance in
533 // a unit test that is deleted.
536 bool ChromiumEnv::FileExists(const std::string& fname) {
537 return base::PathExists(FilePath::FromUTF8Unsafe(fname));
540 const char* ChromiumEnv::FileErrorString(base::File::Error error) {
541 switch (error) {
542 case base::File::FILE_ERROR_FAILED:
543 return "No further details.";
544 case base::File::FILE_ERROR_IN_USE:
545 return "File currently in use.";
546 case base::File::FILE_ERROR_EXISTS:
547 return "File already exists.";
548 case base::File::FILE_ERROR_NOT_FOUND:
549 return "File not found.";
550 case base::File::FILE_ERROR_ACCESS_DENIED:
551 return "Access denied.";
552 case base::File::FILE_ERROR_TOO_MANY_OPENED:
553 return "Too many files open.";
554 case base::File::FILE_ERROR_NO_MEMORY:
555 return "Out of memory.";
556 case base::File::FILE_ERROR_NO_SPACE:
557 return "No space left on drive.";
558 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
559 return "Not a directory.";
560 case base::File::FILE_ERROR_INVALID_OPERATION:
561 return "Invalid operation.";
562 case base::File::FILE_ERROR_SECURITY:
563 return "Security error.";
564 case base::File::FILE_ERROR_ABORT:
565 return "File operation aborted.";
566 case base::File::FILE_ERROR_NOT_A_FILE:
567 return "The supplied path was not a file.";
568 case base::File::FILE_ERROR_NOT_EMPTY:
569 return "The file was not empty.";
570 case base::File::FILE_ERROR_INVALID_URL:
571 return "Invalid URL.";
572 case base::File::FILE_ERROR_IO:
573 return "OS or hardware error.";
574 case base::File::FILE_OK:
575 return "OK.";
576 case base::File::FILE_ERROR_MAX:
577 NOTREACHED();
579 NOTIMPLEMENTED();
580 return "Unknown error.";
583 FilePath ChromiumEnv::RestoreFromBackup(const FilePath& base_name) {
584 FilePath table_name = base_name.AddExtension(table_extension);
585 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
586 table_name);
587 std::string uma_name(name_);
588 uma_name.append(".TableRestore");
589 base::BooleanHistogram::FactoryGet(
590 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
591 return table_name;
594 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
595 std::vector<std::string>* dir_entries) {
596 std::set<FilePath> tables_found;
597 std::set<FilePath> backups_found;
598 for (const std::string& entry : *dir_entries) {
599 FilePath current = FilePath::FromUTF8Unsafe(entry);
600 if (current.MatchesExtension(table_extension))
601 tables_found.insert(current.RemoveExtension());
602 if (current.MatchesExtension(backup_table_extension))
603 backups_found.insert(current.RemoveExtension());
605 std::set<FilePath> backups_only =
606 base::STLSetDifference<std::set<FilePath>>(backups_found, tables_found);
608 if (backups_only.size()) {
609 std::string uma_name(name_);
610 uma_name.append(".MissingFiles");
611 int num_missing_files =
612 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
613 base::Histogram::FactoryGet(uma_name,
614 1 /*min*/,
615 100 /*max*/,
616 8 /*num_buckets*/,
617 base::Histogram::kUmaTargetedHistogramFlag)
618 ->Add(num_missing_files);
620 FilePath dir_path = FilePath::FromUTF8Unsafe(dir);
621 for (const FilePath& backup : backups_only) {
622 FilePath restored_table_name = RestoreFromBackup(dir_path.Append(backup));
623 dir_entries->push_back(restored_table_name.BaseName().AsUTF8Unsafe());
627 Status ChromiumEnv::GetChildren(const std::string& dir,
628 std::vector<std::string>* result) {
629 std::vector<FilePath> entries;
630 base::File::Error error =
631 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir), &entries);
632 if (error != base::File::FILE_OK) {
633 RecordOSError(kGetChildren, error);
634 return MakeIOError(dir, "Could not open/read directory", kGetChildren,
635 error);
638 result->clear();
639 for (const auto& entry : entries)
640 result->push_back(entry.BaseName().AsUTF8Unsafe());
642 if (make_backup_)
643 RestoreIfNecessary(dir, result);
645 return Status::OK();
648 Status ChromiumEnv::DeleteFile(const std::string& fname) {
649 Status result;
650 FilePath fname_filepath = FilePath::FromUTF8Unsafe(fname);
651 // TODO(jorlow): Should we assert this is a file?
652 if (!base::DeleteFile(fname_filepath, false)) {
653 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
654 RecordErrorAt(kDeleteFile);
656 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
657 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
658 false);
660 return result;
663 Status ChromiumEnv::CreateDir(const std::string& name) {
664 Status result;
665 base::File::Error error = base::File::FILE_OK;
666 Retrier retrier(kCreateDir, this);
667 do {
668 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name),
669 &error))
670 return result;
671 } while (retrier.ShouldKeepTrying(error));
672 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
673 RecordOSError(kCreateDir, error);
674 return result;
677 Status ChromiumEnv::DeleteDir(const std::string& name) {
678 Status result;
679 // TODO(jorlow): Should we assert this is a directory?
680 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name), false)) {
681 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
682 RecordErrorAt(kDeleteDir);
684 return result;
687 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
688 Status s;
689 int64_t signed_size;
690 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname), &signed_size)) {
691 *size = 0;
692 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
693 RecordErrorAt(kGetFileSize);
694 } else {
695 *size = static_cast<uint64_t>(signed_size);
697 return s;
700 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
701 Status result;
702 FilePath src_file_path = FilePath::FromUTF8Unsafe(src);
703 if (!base::PathExists(src_file_path))
704 return result;
705 FilePath destination = FilePath::FromUTF8Unsafe(dst);
707 Retrier retrier(kRenameFile, this);
708 base::File::Error error = base::File::FILE_OK;
709 do {
710 if (base::ReplaceFile(src_file_path, destination, &error))
711 return result;
712 } while (retrier.ShouldKeepTrying(error));
714 DCHECK(error != base::File::FILE_OK);
715 RecordOSError(kRenameFile, error);
716 char buf[100];
717 snprintf(buf,
718 sizeof(buf),
719 "Could not rename file: %s",
720 FileErrorString(error));
721 return MakeIOError(src, buf, kRenameFile, error);
724 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
725 *lock = NULL;
726 Status result;
727 int flags = base::File::FLAG_OPEN_ALWAYS |
728 base::File::FLAG_READ |
729 base::File::FLAG_WRITE;
730 base::File::Error error_code;
731 base::File file;
732 Retrier retrier(kLockFile, this);
733 do {
734 file.Initialize(FilePath::FromUTF8Unsafe(fname), flags);
735 if (!file.IsValid())
736 error_code = file.error_details();
737 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
739 if (!file.IsValid()) {
740 if (error_code == base::File::FILE_ERROR_NOT_FOUND) {
741 FilePath parent = FilePath::FromUTF8Unsafe(fname).DirName();
742 FilePath last_parent;
743 int num_missing_ancestors = 0;
744 do {
745 if (base::DirectoryExists(parent))
746 break;
747 ++num_missing_ancestors;
748 last_parent = parent;
749 parent = parent.DirName();
750 } while (parent != last_parent);
751 RecordLockFileAncestors(num_missing_ancestors);
754 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
755 error_code);
756 RecordOSError(kLockFile, error_code);
757 return result;
760 if (!locks_.Insert(fname)) {
761 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
762 return result;
765 Retrier lock_retrier = Retrier(kLockFile, this);
766 do {
767 error_code = file.Lock();
768 } while (error_code != base::File::FILE_OK &&
769 retrier.ShouldKeepTrying(error_code));
771 if (error_code != base::File::FILE_OK) {
772 locks_.Remove(fname);
773 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
774 error_code);
775 RecordOSError(kLockFile, error_code);
776 return result;
779 ChromiumFileLock* my_lock = new ChromiumFileLock;
780 my_lock->file_ = file.Pass();
781 my_lock->name_ = fname;
782 *lock = my_lock;
783 return result;
786 Status ChromiumEnv::UnlockFile(FileLock* lock) {
787 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
788 Status result;
790 base::File::Error error_code = my_lock->file_.Unlock();
791 if (error_code != base::File::FILE_OK) {
792 result =
793 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
794 RecordOSError(kUnlockFile, error_code);
796 bool removed = locks_.Remove(my_lock->name_);
797 DCHECK(removed);
798 delete my_lock;
799 return result;
802 Status ChromiumEnv::GetTestDirectory(std::string* path) {
803 mu_.Acquire();
804 if (test_directory_.empty()) {
805 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
806 &test_directory_)) {
807 mu_.Release();
808 RecordErrorAt(kGetTestDirectory);
809 return MakeIOError(
810 "Could not create temp directory.", "", kGetTestDirectory);
813 *path = test_directory_.AsUTF8Unsafe();
814 mu_.Release();
815 return Status::OK();
818 Status ChromiumEnv::NewLogger(const std::string& fname,
819 leveldb::Logger** result) {
820 FilePath path = FilePath::FromUTF8Unsafe(fname);
821 scoped_ptr<base::File> f(new base::File(
822 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
823 if (!f->IsValid()) {
824 *result = NULL;
825 RecordOSError(kNewLogger, f->error_details());
826 return MakeIOError(fname, "Unable to create log file", kNewLogger,
827 f->error_details());
828 } else {
829 *result = new leveldb::ChromiumLogger(f.release());
830 return Status::OK();
834 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
835 leveldb::SequentialFile** result) {
836 FilePath path = FilePath::FromUTF8Unsafe(fname);
837 scoped_ptr<base::File> f(
838 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
839 if (!f->IsValid()) {
840 *result = NULL;
841 RecordOSError(kNewSequentialFile, f->error_details());
842 return MakeIOError(fname, "Unable to create sequential file",
843 kNewSequentialFile, f->error_details());
844 } else {
845 *result = new ChromiumSequentialFile(fname, f.release(), this);
846 return Status::OK();
850 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
851 #if defined(OS_POSIX)
852 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
853 #elif defined(OS_WIN)
854 // Windows is only limited by available memory
855 #else
856 #error "Need to determine limit to open files for this OS"
857 #endif
860 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
861 leveldb::RandomAccessFile** result) {
862 int flags = base::File::FLAG_READ | base::File::FLAG_OPEN;
863 base::File file(FilePath::FromUTF8Unsafe(fname), flags);
864 if (file.IsValid()) {
865 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
866 RecordOpenFilesLimit("Success");
867 return Status::OK();
869 base::File::Error error_code = file.error_details();
870 if (error_code == base::File::FILE_ERROR_TOO_MANY_OPENED)
871 RecordOpenFilesLimit("TooManyOpened");
872 else
873 RecordOpenFilesLimit("OtherError");
874 *result = NULL;
875 RecordOSError(kNewRandomAccessFile, error_code);
876 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
877 error_code);
880 Status ChromiumEnv::NewWritableFile(const std::string& fname,
881 leveldb::WritableFile** result) {
882 *result = NULL;
883 FilePath path = FilePath::FromUTF8Unsafe(fname);
884 scoped_ptr<base::File> f(new base::File(
885 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
886 if (!f->IsValid()) {
887 RecordErrorAt(kNewWritableFile);
888 return MakeIOError(fname, "Unable to create writable file",
889 kNewWritableFile, f->error_details());
890 } else {
891 *result =
892 new ChromiumWritableFile(fname, f.release(), this, this, make_backup_);
893 return Status::OK();
897 Status ChromiumEnv::NewAppendableFile(const std::string& fname,
898 leveldb::WritableFile** result) {
899 #if defined(OS_CHROMEOS)
900 // Disabled until crbug.com/460568 is fixed. Technically this method shouldn't
901 // be called if reuse_logs is false, but a leveldb bug (fixed, but not yet in
902 // Chrome) still calls this function. Using default leveldb Env implementation
903 // to workaround this bug.
904 return Env::NewAppendableFile(fname, result);
905 #endif
907 *result = NULL;
908 FilePath path = FilePath::FromUTF8Unsafe(fname);
909 scoped_ptr<base::File> f(new base::File(
910 path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND));
911 if (!f->IsValid()) {
912 RecordErrorAt(kNewAppendableFile);
913 return MakeIOError(fname, "Unable to create appendable file",
914 kNewAppendableFile, f->error_details());
916 *result =
917 new ChromiumWritableFile(fname, f.release(), this, this, make_backup_);
918 return Status::OK();
921 uint64_t ChromiumEnv::NowMicros() {
922 return base::TimeTicks::Now().ToInternalValue();
925 void ChromiumEnv::SleepForMicroseconds(int micros) {
926 // Round up to the next millisecond.
927 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
930 void ChromiumEnv::RecordErrorAt(MethodID method) const {
931 GetMethodIOErrorHistogram()->Add(method);
934 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
935 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
938 void ChromiumEnv::RecordOSError(MethodID method,
939 base::File::Error error) const {
940 DCHECK_LT(error, 0);
941 RecordErrorAt(method);
942 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
945 void ChromiumEnv::RecordBackupResult(bool result) const {
946 std::string uma_name(name_);
947 uma_name.append(".TableBackup");
948 base::BooleanHistogram::FactoryGet(
949 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
952 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
953 int limit) const {
954 std::string uma_name(name_);
955 // TODO(dgrogan): This is probably not the best way to concatenate strings.
956 uma_name.append(".IOError.").append(MethodIDToString(method));
957 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
958 base::Histogram::kUmaTargetedHistogramFlag);
961 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
962 std::string uma_name(name_);
963 uma_name.append(".IOError");
964 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
965 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
968 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
969 const std::string& type) const {
970 std::string uma_name(name_);
971 uma_name.append(".MaxFDs.").append(type);
972 // These numbers make each bucket twice as large as the previous bucket.
973 const int kFirstEntry = 1;
974 const int kLastEntry = 65536;
975 const int kNumBuckets = 18;
976 return base::Histogram::FactoryGet(
977 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
978 base::Histogram::kUmaTargetedHistogramFlag);
981 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
982 std::string uma_name(name_);
983 uma_name.append(".LockFileAncestorsNotFound");
984 const int kMin = 1;
985 const int kMax = 10;
986 const int kNumBuckets = 11;
987 return base::LinearHistogram::FactoryGet(
988 uma_name, kMin, kMax, kNumBuckets,
989 base::Histogram::kUmaTargetedHistogramFlag);
992 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
993 std::string uma_name(name_);
994 // TODO(dgrogan): This is probably not the best way to concatenate strings.
995 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
997 const int kBucketSizeMillis = 25;
998 // Add 2, 1 for each of the buckets <1 and >max.
999 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
1000 return base::Histogram::FactoryTimeGet(
1001 uma_name, base::TimeDelta::FromMilliseconds(1),
1002 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
1003 kNumBuckets,
1004 base::Histogram::kUmaTargetedHistogramFlag);
1007 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
1008 MethodID method) const {
1009 std::string uma_name(name_);
1010 uma_name.append(".RetryRecoveredFromErrorIn")
1011 .append(MethodIDToString(method));
1012 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1013 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1016 class Thread : public base::PlatformThread::Delegate {
1017 public:
1018 Thread(void (*function)(void* arg), void* arg)
1019 : function_(function), arg_(arg) {
1020 base::PlatformThreadHandle handle;
1021 bool success = base::PlatformThread::Create(0, this, &handle);
1022 DCHECK(success);
1024 virtual ~Thread() {}
1025 void ThreadMain() override {
1026 (*function_)(arg_);
1027 delete this;
1030 private:
1031 void (*function_)(void* arg);
1032 void* arg_;
1035 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1036 mu_.Acquire();
1038 // Start background thread if necessary
1039 if (!started_bgthread_) {
1040 started_bgthread_ = true;
1041 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1044 // If the queue is currently empty, the background thread may currently be
1045 // waiting.
1046 if (queue_.empty()) {
1047 bgsignal_.Signal();
1050 // Add to priority queue
1051 queue_.push_back(BGItem());
1052 queue_.back().function = function;
1053 queue_.back().arg = arg;
1055 mu_.Release();
1058 void ChromiumEnv::BGThread() {
1059 base::PlatformThread::SetName(name_.c_str());
1061 while (true) {
1062 // Wait until there is an item that is ready to run
1063 mu_.Acquire();
1064 while (queue_.empty()) {
1065 bgsignal_.Wait();
1068 void (*function)(void*) = queue_.front().function;
1069 void* arg = queue_.front().arg;
1070 queue_.pop_front();
1072 mu_.Release();
1073 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1074 (*function)(arg);
1078 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1079 new Thread(function, arg); // Will self-delete.
1082 static std::string GetDirName(const std::string& filename) {
1083 return FilePath::FromUTF8Unsafe(filename).DirName().AsUTF8Unsafe();
1086 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
1087 base::AutoLock auto_lock(directory_sync_lock_);
1088 directories_needing_sync_.insert(GetDirName(filename));
1091 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
1092 base::AutoLock auto_lock(directory_sync_lock_);
1093 return ContainsKey(directories_needing_sync_, GetDirName(filename));
1096 void ChromiumEnv::DidSyncDir(const std::string& filename) {
1097 base::AutoLock auto_lock(directory_sync_lock_);
1098 directories_needing_sync_.erase(GetDirName(filename));
1101 } // namespace leveldb_env
1103 namespace leveldb {
1105 Env* IDBEnv() {
1106 return leveldb_env::idb_env.Pointer();
1109 Env* Env::Default() {
1110 return leveldb_env::default_env.Pointer();
1113 } // namespace leveldb