Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob8ca4cc470484cb34faebb2ca7c0fa73b6bdfff35
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/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "base/trace_event/trace_event.h"
22 #include "third_party/leveldatabase/chromium_logger.h"
23 #include "third_party/re2/re2/re2.h"
25 using base::FilePath;
26 using leveldb::FileLock;
27 using leveldb::Slice;
28 using leveldb::Status;
30 namespace leveldb_env {
32 namespace {
34 const FilePath::CharType backup_table_extension[] = FILE_PATH_LITERAL(".bak");
35 const FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
37 static const FilePath::CharType kLevelDBTestDirectoryPrefix[] =
38 FILE_PATH_LITERAL("leveldb-test-");
40 static base::File::Error LastFileError() {
41 #if defined(OS_WIN)
42 return base::File::OSErrorToFileError(GetLastError());
43 #else
44 return base::File::OSErrorToFileError(errno);
45 #endif
48 // Making direct platform in lieu of using base::FileEnumerator because the
49 // latter can fail quietly without return an error result.
50 static base::File::Error GetDirectoryEntries(const FilePath& dir_param,
51 std::vector<FilePath>* result) {
52 base::ThreadRestrictions::AssertIOAllowed();
53 result->clear();
54 #if defined(OS_WIN)
55 FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
56 WIN32_FIND_DATA find_data;
57 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
58 if (find_handle == INVALID_HANDLE_VALUE) {
59 DWORD last_error = GetLastError();
60 if (last_error == ERROR_FILE_NOT_FOUND)
61 return base::File::FILE_OK;
62 return base::File::OSErrorToFileError(last_error);
64 do {
65 FilePath filepath(find_data.cFileName);
66 FilePath::StringType basename = filepath.BaseName().value();
67 if (basename == FILE_PATH_LITERAL(".") ||
68 basename == FILE_PATH_LITERAL(".."))
69 continue;
70 result->push_back(filepath.BaseName());
71 } while (FindNextFile(find_handle, &find_data));
72 DWORD last_error = GetLastError();
73 base::File::Error return_value = base::File::FILE_OK;
74 if (last_error != ERROR_NO_MORE_FILES)
75 return_value = base::File::OSErrorToFileError(last_error);
76 FindClose(find_handle);
77 return return_value;
78 #else
79 const std::string dir_string = dir_param.AsUTF8Unsafe();
80 DIR* dir = opendir(dir_string.c_str());
81 if (!dir)
82 return base::File::OSErrorToFileError(errno);
83 struct dirent dent_buf;
84 struct dirent* dent;
85 int readdir_result;
86 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
87 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
88 continue;
89 result->push_back(FilePath::FromUTF8Unsafe(dent->d_name));
91 int saved_errno = errno;
92 closedir(dir);
93 if (readdir_result != 0)
94 return base::File::OSErrorToFileError(saved_errno);
95 return base::File::FILE_OK;
96 #endif
99 class ChromiumFileLock : public FileLock {
100 public:
101 base::File file_;
102 std::string name_;
105 class Retrier {
106 public:
107 Retrier(MethodID method, RetrierProvider* provider)
108 : start_(base::TimeTicks::Now()),
109 limit_(start_ + base::TimeDelta::FromMilliseconds(
110 provider->MaxRetryTimeMillis())),
111 last_(start_),
112 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
113 success_(true),
114 method_(method),
115 last_error_(base::File::FILE_OK),
116 provider_(provider) {}
117 ~Retrier() {
118 if (success_) {
119 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
120 if (last_error_ != base::File::FILE_OK) {
121 DCHECK_LT(last_error_, 0);
122 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
126 bool ShouldKeepTrying(base::File::Error last_error) {
127 DCHECK_NE(last_error, base::File::FILE_OK);
128 last_error_ = last_error;
129 if (last_ < limit_) {
130 base::PlatformThread::Sleep(time_to_sleep_);
131 last_ = base::TimeTicks::Now();
132 return true;
134 success_ = false;
135 return false;
138 private:
139 base::TimeTicks start_;
140 base::TimeTicks limit_;
141 base::TimeTicks last_;
142 base::TimeDelta time_to_sleep_;
143 bool success_;
144 MethodID method_;
145 base::File::Error last_error_;
146 RetrierProvider* provider_;
149 class ChromiumSequentialFile : public leveldb::SequentialFile {
150 public:
151 ChromiumSequentialFile(const std::string& fname,
152 base::File* f,
153 const UMALogger* uma_logger)
154 : filename_(fname), file_(f), uma_logger_(uma_logger) {}
155 virtual ~ChromiumSequentialFile() {}
157 Status Read(size_t n, Slice* result, char* scratch) override {
158 int bytes_read = file_->ReadAtCurrentPosNoBestEffort(scratch, n);
159 if (bytes_read == -1) {
160 base::File::Error error = LastFileError();
161 uma_logger_->RecordErrorAt(kSequentialFileRead);
162 return MakeIOError(filename_, base::File::ErrorToString(error),
163 kSequentialFileRead, error);
164 } else {
165 *result = Slice(scratch, bytes_read);
166 return Status::OK();
170 Status Skip(uint64_t n) override {
171 if (file_->Seek(base::File::FROM_CURRENT, n) == -1) {
172 base::File::Error error = LastFileError();
173 uma_logger_->RecordErrorAt(kSequentialFileSkip);
174 return MakeIOError(filename_, base::File::ErrorToString(error),
175 kSequentialFileSkip, error);
176 } else {
177 return Status::OK();
181 private:
182 std::string filename_;
183 scoped_ptr<base::File> file_;
184 const UMALogger* uma_logger_;
187 class ChromiumRandomAccessFile : public leveldb::RandomAccessFile {
188 public:
189 ChromiumRandomAccessFile(const std::string& fname,
190 base::File file,
191 const UMALogger* uma_logger)
192 : filename_(fname), file_(file.Pass()), uma_logger_(uma_logger) {}
193 virtual ~ChromiumRandomAccessFile() {}
195 Status Read(uint64_t offset,
196 size_t n,
197 Slice* result,
198 char* scratch) const override {
199 Status s;
200 int r = file_.Read(offset, scratch, n);
201 *result = Slice(scratch, (r < 0) ? 0 : r);
202 if (r < 0) {
203 // An error: return a non-ok status
204 s = MakeIOError(filename_, "Could not perform read",
205 kRandomAccessFileRead);
206 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
208 return s;
211 private:
212 std::string filename_;
213 mutable base::File file_;
214 const UMALogger* uma_logger_;
217 class ChromiumWritableFile : public leveldb::WritableFile {
218 public:
219 ChromiumWritableFile(const std::string& fname,
220 base::File* f,
221 const UMALogger* uma_logger,
222 bool make_backup);
223 virtual ~ChromiumWritableFile() {}
224 leveldb::Status Append(const leveldb::Slice& data) override;
225 leveldb::Status Close() override;
226 leveldb::Status Flush() override;
227 leveldb::Status Sync() override;
229 private:
230 enum Type { kManifest, kTable, kOther };
231 leveldb::Status SyncParent();
233 std::string filename_;
234 scoped_ptr<base::File> file_;
235 const UMALogger* uma_logger_;
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 bool make_backup)
245 : filename_(fname),
246 file_(f),
247 uma_logger_(uma_logger),
248 file_type_(kOther),
249 make_backup_(make_backup) {
250 FilePath path = FilePath::FromUTF8Unsafe(fname);
251 if (path.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
252 file_type_ = kManifest;
253 else if (path.MatchesExtension(table_extension))
254 file_type_ = kTable;
255 parent_dir_ = FilePath::FromUTF8Unsafe(fname).DirName().AsUTF8Unsafe();
258 Status ChromiumWritableFile::SyncParent() {
259 TRACE_EVENT0("leveldb", "SyncParent");
260 #if defined(OS_POSIX)
261 FilePath path = FilePath::FromUTF8Unsafe(parent_dir_);
262 base::File f(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
263 if (!f.IsValid()) {
264 return MakeIOError(parent_dir_, "Unable to open directory", kSyncParent,
265 f.error_details());
267 if (!f.Flush()) {
268 base::File::Error error = LastFileError();
269 return MakeIOError(parent_dir_, base::File::ErrorToString(error),
270 kSyncParent, error);
272 #endif
273 return Status::OK();
276 Status ChromiumWritableFile::Append(const Slice& data) {
277 int bytes_written = file_->WriteAtCurrentPos(data.data(), data.size());
278 if (bytes_written != data.size()) {
279 base::File::Error error = LastFileError();
280 uma_logger_->RecordOSError(kWritableFileAppend, error);
281 return MakeIOError(filename_, base::File::ErrorToString(error),
282 kWritableFileAppend, error);
285 return Status::OK();
288 Status ChromiumWritableFile::Close() {
289 file_->Close();
290 return Status::OK();
293 Status ChromiumWritableFile::Flush() {
294 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
295 // flush.
296 return Status::OK();
299 Status ChromiumWritableFile::Sync() {
300 TRACE_EVENT0("leveldb", "WritableFile::Sync");
302 if (!file_->Flush()) {
303 base::File::Error error = LastFileError();
304 uma_logger_->RecordErrorAt(kWritableFileSync);
305 return MakeIOError(filename_, base::File::ErrorToString(error),
306 kWritableFileSync, error);
309 if (make_backup_ && file_type_ == kTable)
310 uma_logger_->RecordBackupResult(ChromiumEnv::MakeBackup(filename_));
312 // leveldb's implicit contract for Sync() is that if this instance is for a
313 // manifest file then the directory is also sync'ed. See leveldb's
314 // env_posix.cc.
315 if (file_type_ == kManifest)
316 return SyncParent();
318 return Status::OK();
321 class IDBEnv : public ChromiumEnv {
322 public:
323 IDBEnv() : ChromiumEnv() {
324 name_ = "LevelDBEnv.IDB";
325 uma_ioerror_base_name_ = name_ + ".IOError.BFE";
326 make_backup_ = true;
330 base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
332 base::LazyInstance<ChromiumEnv>::Leaky default_env = LAZY_INSTANCE_INITIALIZER;
334 } // unnamed namespace
336 const char* MethodIDToString(MethodID method) {
337 switch (method) {
338 case kSequentialFileRead:
339 return "SequentialFileRead";
340 case kSequentialFileSkip:
341 return "SequentialFileSkip";
342 case kRandomAccessFileRead:
343 return "RandomAccessFileRead";
344 case kWritableFileAppend:
345 return "WritableFileAppend";
346 case kWritableFileClose:
347 return "WritableFileClose";
348 case kWritableFileFlush:
349 return "WritableFileFlush";
350 case kWritableFileSync:
351 return "WritableFileSync";
352 case kNewSequentialFile:
353 return "NewSequentialFile";
354 case kNewRandomAccessFile:
355 return "NewRandomAccessFile";
356 case kNewWritableFile:
357 return "NewWritableFile";
358 case kNewAppendableFile:
359 return "NewAppendableFile";
360 case kDeleteFile:
361 return "DeleteFile";
362 case kCreateDir:
363 return "CreateDir";
364 case kDeleteDir:
365 return "DeleteDir";
366 case kGetFileSize:
367 return "GetFileSize";
368 case kRenameFile:
369 return "RenameFile";
370 case kLockFile:
371 return "LockFile";
372 case kUnlockFile:
373 return "UnlockFile";
374 case kGetTestDirectory:
375 return "GetTestDirectory";
376 case kNewLogger:
377 return "NewLogger";
378 case kSyncParent:
379 return "SyncParent";
380 case kGetChildren:
381 return "GetChildren";
382 case kNumEntries:
383 NOTREACHED();
384 return "kNumEntries";
386 NOTREACHED();
387 return "Unknown";
390 Status MakeIOError(Slice filename,
391 const std::string& message,
392 MethodID method,
393 base::File::Error error) {
394 DCHECK_LT(error, 0);
395 char buf[512];
396 snprintf(buf, sizeof(buf), "%s (ChromeMethodBFE: %d::%s::%d)",
397 message.c_str(), method, MethodIDToString(method), -error);
398 return Status::IOError(filename, buf);
401 Status MakeIOError(Slice filename,
402 const std::string& message,
403 MethodID method) {
404 char buf[512];
405 snprintf(buf, sizeof(buf), "%s (ChromeMethodOnly: %d::%s)", message.c_str(),
406 method, MethodIDToString(method));
407 return Status::IOError(filename, buf);
410 ErrorParsingResult ParseMethodAndError(const leveldb::Status& status,
411 MethodID* method_param,
412 base::File::Error* error) {
413 const std::string status_string = status.ToString();
414 int method;
415 if (RE2::PartialMatch(status_string.c_str(), "ChromeMethodOnly: (\\d+)",
416 &method)) {
417 *method_param = static_cast<MethodID>(method);
418 return METHOD_ONLY;
420 int parsed_error;
421 if (RE2::PartialMatch(status_string.c_str(),
422 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method,
423 &parsed_error)) {
424 *method_param = static_cast<MethodID>(method);
425 *error = static_cast<base::File::Error>(-parsed_error);
426 DCHECK_LT(*error, base::File::FILE_OK);
427 DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
428 return METHOD_AND_BFE;
430 return NONE;
433 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
434 // change the order because indices into this array have been recorded in uma
435 // histograms.
436 const char* patterns[] = {
437 "missing files",
438 "log record too small",
439 "corrupted internal key",
440 "partial record",
441 "missing start of fragmented record",
442 "error in middle of record",
443 "unknown record type",
444 "truncated record at end",
445 "bad record length",
446 "VersionEdit",
447 "FileReader invoked with unexpected value",
448 "corrupted key",
449 "CURRENT file does not end with newline",
450 "no meta-nextfile entry",
451 "no meta-lognumber entry",
452 "no last-sequence-number entry",
453 "malformed WriteBatch",
454 "bad WriteBatch Put",
455 "bad WriteBatch Delete",
456 "unknown WriteBatch tag",
457 "WriteBatch has wrong count",
458 "bad entry in block",
459 "bad block contents",
460 "bad block handle",
461 "truncated block read",
462 "block checksum mismatch",
463 "checksum mismatch",
464 "corrupted compressed block contents",
465 "bad block type",
466 "bad magic number",
467 "file is too short",
470 // Returns 1-based index into the above array or 0 if nothing matches.
471 int GetCorruptionCode(const leveldb::Status& status) {
472 DCHECK(!status.IsIOError());
473 DCHECK(!status.ok());
474 const int kOtherError = 0;
475 int error = kOtherError;
476 const std::string& str_error = status.ToString();
477 const size_t kNumPatterns = arraysize(patterns);
478 for (size_t i = 0; i < kNumPatterns; ++i) {
479 if (str_error.find(patterns[i]) != std::string::npos) {
480 error = i + 1;
481 break;
484 return error;
487 int GetNumCorruptionCodes() {
488 // + 1 for the "other" error that is returned when a corruption message
489 // doesn't match any of the patterns.
490 return arraysize(patterns) + 1;
493 std::string GetCorruptionMessage(const leveldb::Status& status) {
494 int code = GetCorruptionCode(status);
495 if (code == 0)
496 return "Unknown corruption";
497 return patterns[code - 1];
500 bool IndicatesDiskFull(const leveldb::Status& status) {
501 if (status.ok())
502 return false;
503 leveldb_env::MethodID method;
504 base::File::Error error = base::File::FILE_OK;
505 leveldb_env::ErrorParsingResult result =
506 leveldb_env::ParseMethodAndError(status, &method, &error);
507 return (result == leveldb_env::METHOD_AND_BFE &&
508 static_cast<base::File::Error>(error) ==
509 base::File::FILE_ERROR_NO_SPACE);
512 bool ChromiumEnv::MakeBackup(const std::string& fname) {
513 FilePath original_table_name = FilePath::FromUTF8Unsafe(fname);
514 FilePath backup_table_name =
515 original_table_name.ReplaceExtension(backup_table_extension);
516 return base::CopyFile(original_table_name, backup_table_name);
519 ChromiumEnv::ChromiumEnv()
520 : name_("LevelDBEnv"),
521 make_backup_(false),
522 bgsignal_(&mu_),
523 started_bgthread_(false),
524 kMaxRetryTimeMillis(1000) {
525 uma_ioerror_base_name_ = name_ + ".IOError.BFE";
528 ChromiumEnv::~ChromiumEnv() {
529 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
530 // ensure that behavior isn't accidentally changed, but there's an instance in
531 // a unit test that is deleted.
534 bool ChromiumEnv::FileExists(const std::string& fname) {
535 return base::PathExists(FilePath::FromUTF8Unsafe(fname));
538 const char* ChromiumEnv::FileErrorString(base::File::Error error) {
539 switch (error) {
540 case base::File::FILE_ERROR_FAILED:
541 return "No further details.";
542 case base::File::FILE_ERROR_IN_USE:
543 return "File currently in use.";
544 case base::File::FILE_ERROR_EXISTS:
545 return "File already exists.";
546 case base::File::FILE_ERROR_NOT_FOUND:
547 return "File not found.";
548 case base::File::FILE_ERROR_ACCESS_DENIED:
549 return "Access denied.";
550 case base::File::FILE_ERROR_TOO_MANY_OPENED:
551 return "Too many files open.";
552 case base::File::FILE_ERROR_NO_MEMORY:
553 return "Out of memory.";
554 case base::File::FILE_ERROR_NO_SPACE:
555 return "No space left on drive.";
556 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
557 return "Not a directory.";
558 case base::File::FILE_ERROR_INVALID_OPERATION:
559 return "Invalid operation.";
560 case base::File::FILE_ERROR_SECURITY:
561 return "Security error.";
562 case base::File::FILE_ERROR_ABORT:
563 return "File operation aborted.";
564 case base::File::FILE_ERROR_NOT_A_FILE:
565 return "The supplied path was not a file.";
566 case base::File::FILE_ERROR_NOT_EMPTY:
567 return "The file was not empty.";
568 case base::File::FILE_ERROR_INVALID_URL:
569 return "Invalid URL.";
570 case base::File::FILE_ERROR_IO:
571 return "OS or hardware error.";
572 case base::File::FILE_OK:
573 return "OK.";
574 case base::File::FILE_ERROR_MAX:
575 NOTREACHED();
577 NOTIMPLEMENTED();
578 return "Unknown error.";
581 FilePath ChromiumEnv::RestoreFromBackup(const FilePath& base_name) {
582 FilePath table_name = base_name.AddExtension(table_extension);
583 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
584 table_name);
585 std::string uma_name(name_);
586 uma_name.append(".TableRestore");
587 base::BooleanHistogram::FactoryGet(
588 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
589 return table_name;
592 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
593 std::vector<std::string>* dir_entries) {
594 std::set<FilePath> tables_found;
595 std::set<FilePath> backups_found;
596 for (const std::string& entry : *dir_entries) {
597 FilePath current = FilePath::FromUTF8Unsafe(entry);
598 if (current.MatchesExtension(table_extension))
599 tables_found.insert(current.RemoveExtension());
600 if (current.MatchesExtension(backup_table_extension))
601 backups_found.insert(current.RemoveExtension());
603 std::set<FilePath> backups_only =
604 base::STLSetDifference<std::set<FilePath>>(backups_found, tables_found);
606 if (backups_only.size()) {
607 std::string uma_name(name_);
608 uma_name.append(".MissingFiles");
609 int num_missing_files =
610 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
611 base::Histogram::FactoryGet(uma_name,
612 1 /*min*/,
613 100 /*max*/,
614 8 /*num_buckets*/,
615 base::Histogram::kUmaTargetedHistogramFlag)
616 ->Add(num_missing_files);
618 FilePath dir_path = FilePath::FromUTF8Unsafe(dir);
619 for (const FilePath& backup : backups_only) {
620 FilePath restored_table_name = RestoreFromBackup(dir_path.Append(backup));
621 dir_entries->push_back(restored_table_name.BaseName().AsUTF8Unsafe());
625 Status ChromiumEnv::GetChildren(const std::string& dir,
626 std::vector<std::string>* result) {
627 std::vector<FilePath> entries;
628 base::File::Error error =
629 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir), &entries);
630 if (error != base::File::FILE_OK) {
631 RecordOSError(kGetChildren, error);
632 return MakeIOError(dir, "Could not open/read directory", kGetChildren,
633 error);
636 result->clear();
637 for (const auto& entry : entries)
638 result->push_back(entry.BaseName().AsUTF8Unsafe());
640 if (make_backup_)
641 RestoreIfNecessary(dir, result);
643 return Status::OK();
646 Status ChromiumEnv::DeleteFile(const std::string& fname) {
647 Status result;
648 FilePath fname_filepath = FilePath::FromUTF8Unsafe(fname);
649 // TODO(jorlow): Should we assert this is a file?
650 if (!base::DeleteFile(fname_filepath, false)) {
651 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
652 RecordErrorAt(kDeleteFile);
654 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
655 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
656 false);
658 return result;
661 Status ChromiumEnv::CreateDir(const std::string& name) {
662 Status result;
663 base::File::Error error = base::File::FILE_OK;
664 Retrier retrier(kCreateDir, this);
665 do {
666 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name),
667 &error))
668 return result;
669 } while (retrier.ShouldKeepTrying(error));
670 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
671 RecordOSError(kCreateDir, error);
672 return result;
675 Status ChromiumEnv::DeleteDir(const std::string& name) {
676 Status result;
677 // TODO(jorlow): Should we assert this is a directory?
678 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name), false)) {
679 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
680 RecordErrorAt(kDeleteDir);
682 return result;
685 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
686 Status s;
687 int64_t signed_size;
688 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname), &signed_size)) {
689 *size = 0;
690 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
691 RecordErrorAt(kGetFileSize);
692 } else {
693 *size = static_cast<uint64_t>(signed_size);
695 return s;
698 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
699 Status result;
700 FilePath src_file_path = FilePath::FromUTF8Unsafe(src);
701 if (!base::PathExists(src_file_path))
702 return result;
703 FilePath destination = FilePath::FromUTF8Unsafe(dst);
705 Retrier retrier(kRenameFile, this);
706 base::File::Error error = base::File::FILE_OK;
707 do {
708 if (base::ReplaceFile(src_file_path, destination, &error))
709 return result;
710 } while (retrier.ShouldKeepTrying(error));
712 DCHECK(error != base::File::FILE_OK);
713 RecordOSError(kRenameFile, error);
714 char buf[100];
715 snprintf(buf,
716 sizeof(buf),
717 "Could not rename file: %s",
718 FileErrorString(error));
719 return MakeIOError(src, buf, kRenameFile, error);
722 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
723 *lock = NULL;
724 Status result;
725 int flags = base::File::FLAG_OPEN_ALWAYS |
726 base::File::FLAG_READ |
727 base::File::FLAG_WRITE;
728 base::File::Error error_code;
729 base::File file;
730 Retrier retrier(kLockFile, this);
731 do {
732 file.Initialize(FilePath::FromUTF8Unsafe(fname), flags);
733 if (!file.IsValid())
734 error_code = file.error_details();
735 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
737 if (!file.IsValid()) {
738 if (error_code == base::File::FILE_ERROR_NOT_FOUND) {
739 FilePath parent = FilePath::FromUTF8Unsafe(fname).DirName();
740 FilePath last_parent;
741 int num_missing_ancestors = 0;
742 do {
743 if (base::DirectoryExists(parent))
744 break;
745 ++num_missing_ancestors;
746 last_parent = parent;
747 parent = parent.DirName();
748 } while (parent != last_parent);
749 RecordLockFileAncestors(num_missing_ancestors);
752 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
753 error_code);
754 RecordOSError(kLockFile, error_code);
755 return result;
758 if (!locks_.Insert(fname)) {
759 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
760 return result;
763 Retrier lock_retrier = Retrier(kLockFile, this);
764 do {
765 error_code = file.Lock();
766 } while (error_code != base::File::FILE_OK &&
767 retrier.ShouldKeepTrying(error_code));
769 if (error_code != base::File::FILE_OK) {
770 locks_.Remove(fname);
771 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
772 error_code);
773 RecordOSError(kLockFile, error_code);
774 return result;
777 ChromiumFileLock* my_lock = new ChromiumFileLock;
778 my_lock->file_ = file.Pass();
779 my_lock->name_ = fname;
780 *lock = my_lock;
781 return result;
784 Status ChromiumEnv::UnlockFile(FileLock* lock) {
785 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
786 Status result;
788 base::File::Error error_code = my_lock->file_.Unlock();
789 if (error_code != base::File::FILE_OK) {
790 result =
791 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
792 RecordOSError(kUnlockFile, error_code);
794 bool removed = locks_.Remove(my_lock->name_);
795 DCHECK(removed);
796 delete my_lock;
797 return result;
800 Status ChromiumEnv::GetTestDirectory(std::string* path) {
801 mu_.Acquire();
802 if (test_directory_.empty()) {
803 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
804 &test_directory_)) {
805 mu_.Release();
806 RecordErrorAt(kGetTestDirectory);
807 return MakeIOError(
808 "Could not create temp directory.", "", kGetTestDirectory);
811 *path = test_directory_.AsUTF8Unsafe();
812 mu_.Release();
813 return Status::OK();
816 Status ChromiumEnv::NewLogger(const std::string& fname,
817 leveldb::Logger** result) {
818 FilePath path = FilePath::FromUTF8Unsafe(fname);
819 scoped_ptr<base::File> f(new base::File(
820 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
821 if (!f->IsValid()) {
822 *result = NULL;
823 RecordOSError(kNewLogger, f->error_details());
824 return MakeIOError(fname, "Unable to create log file", kNewLogger,
825 f->error_details());
826 } else {
827 *result = new leveldb::ChromiumLogger(f.release());
828 return Status::OK();
832 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
833 leveldb::SequentialFile** result) {
834 FilePath path = FilePath::FromUTF8Unsafe(fname);
835 scoped_ptr<base::File> f(
836 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
837 if (!f->IsValid()) {
838 *result = NULL;
839 RecordOSError(kNewSequentialFile, f->error_details());
840 return MakeIOError(fname, "Unable to create sequential file",
841 kNewSequentialFile, f->error_details());
842 } else {
843 *result = new ChromiumSequentialFile(fname, f.release(), this);
844 return Status::OK();
848 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
849 #if defined(OS_POSIX)
850 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
851 #elif defined(OS_WIN)
852 // Windows is only limited by available memory
853 #else
854 #error "Need to determine limit to open files for this OS"
855 #endif
858 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
859 leveldb::RandomAccessFile** result) {
860 int flags = base::File::FLAG_READ | base::File::FLAG_OPEN;
861 base::File file(FilePath::FromUTF8Unsafe(fname), flags);
862 if (file.IsValid()) {
863 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
864 RecordOpenFilesLimit("Success");
865 return Status::OK();
867 base::File::Error error_code = file.error_details();
868 if (error_code == base::File::FILE_ERROR_TOO_MANY_OPENED)
869 RecordOpenFilesLimit("TooManyOpened");
870 else
871 RecordOpenFilesLimit("OtherError");
872 *result = NULL;
873 RecordOSError(kNewRandomAccessFile, error_code);
874 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
875 error_code);
878 Status ChromiumEnv::NewWritableFile(const std::string& fname,
879 leveldb::WritableFile** result) {
880 *result = NULL;
881 FilePath path = FilePath::FromUTF8Unsafe(fname);
882 scoped_ptr<base::File> f(new base::File(
883 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
884 if (!f->IsValid()) {
885 RecordErrorAt(kNewWritableFile);
886 return MakeIOError(fname, "Unable to create writable file",
887 kNewWritableFile, f->error_details());
888 } else {
889 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
890 return Status::OK();
894 Status ChromiumEnv::NewAppendableFile(const std::string& fname,
895 leveldb::WritableFile** result) {
896 *result = NULL;
897 FilePath path = FilePath::FromUTF8Unsafe(fname);
898 scoped_ptr<base::File> f(new base::File(
899 path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND));
900 if (!f->IsValid()) {
901 RecordErrorAt(kNewAppendableFile);
902 return MakeIOError(fname, "Unable to create appendable file",
903 kNewAppendableFile, f->error_details());
905 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
906 return Status::OK();
909 uint64_t ChromiumEnv::NowMicros() {
910 return base::TimeTicks::Now().ToInternalValue();
913 void ChromiumEnv::SleepForMicroseconds(int micros) {
914 // Round up to the next millisecond.
915 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
918 void ChromiumEnv::RecordErrorAt(MethodID method) const {
919 GetMethodIOErrorHistogram()->Add(method);
922 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
923 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
926 void ChromiumEnv::RecordOSError(MethodID method,
927 base::File::Error error) const {
928 DCHECK_LT(error, 0);
929 RecordErrorAt(method);
930 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
933 void ChromiumEnv::RecordBackupResult(bool result) const {
934 std::string uma_name(name_);
935 uma_name.append(".TableBackup");
936 base::BooleanHistogram::FactoryGet(
937 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
940 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
941 int limit) const {
942 std::string uma_name;
943 base::StringAppendF(&uma_name, "%s.%s", uma_ioerror_base_name_.c_str(),
944 MethodIDToString(method));
945 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
946 base::Histogram::kUmaTargetedHistogramFlag);
949 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
950 std::string uma_name(name_);
951 uma_name.append(".IOError");
952 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
953 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
956 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
957 const std::string& type) const {
958 std::string uma_name(name_);
959 uma_name.append(".MaxFDs.").append(type);
960 // These numbers make each bucket twice as large as the previous bucket.
961 const int kFirstEntry = 1;
962 const int kLastEntry = 65536;
963 const int kNumBuckets = 18;
964 return base::Histogram::FactoryGet(
965 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
966 base::Histogram::kUmaTargetedHistogramFlag);
969 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
970 std::string uma_name(name_);
971 uma_name.append(".LockFileAncestorsNotFound");
972 const int kMin = 1;
973 const int kMax = 10;
974 const int kNumBuckets = 11;
975 return base::LinearHistogram::FactoryGet(
976 uma_name, kMin, kMax, kNumBuckets,
977 base::Histogram::kUmaTargetedHistogramFlag);
980 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
981 std::string uma_name(name_);
982 // TODO(dgrogan): This is probably not the best way to concatenate strings.
983 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
985 const int kBucketSizeMillis = 25;
986 // Add 2, 1 for each of the buckets <1 and >max.
987 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
988 return base::Histogram::FactoryTimeGet(
989 uma_name, base::TimeDelta::FromMilliseconds(1),
990 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
991 kNumBuckets,
992 base::Histogram::kUmaTargetedHistogramFlag);
995 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
996 MethodID method) const {
997 std::string uma_name(name_);
998 uma_name.append(".RetryRecoveredFromErrorIn")
999 .append(MethodIDToString(method));
1000 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1001 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1004 class Thread : public base::PlatformThread::Delegate {
1005 public:
1006 Thread(void (*function)(void* arg), void* arg)
1007 : function_(function), arg_(arg) {
1008 base::PlatformThreadHandle handle;
1009 bool success = base::PlatformThread::Create(0, this, &handle);
1010 DCHECK(success);
1012 virtual ~Thread() {}
1013 void ThreadMain() override {
1014 (*function_)(arg_);
1015 delete this;
1018 private:
1019 void (*function_)(void* arg);
1020 void* arg_;
1023 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1024 mu_.Acquire();
1026 // Start background thread if necessary
1027 if (!started_bgthread_) {
1028 started_bgthread_ = true;
1029 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1032 // If the queue is currently empty, the background thread may currently be
1033 // waiting.
1034 if (queue_.empty()) {
1035 bgsignal_.Signal();
1038 // Add to priority queue
1039 queue_.push_back(BGItem());
1040 queue_.back().function = function;
1041 queue_.back().arg = arg;
1043 mu_.Release();
1046 void ChromiumEnv::BGThread() {
1047 base::PlatformThread::SetName(name_.c_str());
1049 while (true) {
1050 // Wait until there is an item that is ready to run
1051 mu_.Acquire();
1052 while (queue_.empty()) {
1053 bgsignal_.Wait();
1056 void (*function)(void*) = queue_.front().function;
1057 void* arg = queue_.front().arg;
1058 queue_.pop_front();
1060 mu_.Release();
1061 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1062 (*function)(arg);
1066 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1067 new Thread(function, arg); // Will self-delete.
1070 } // namespace leveldb_env
1072 namespace leveldb {
1074 Env* IDBEnv() {
1075 return leveldb_env::idb_env.Pointer();
1078 Env* Env::Default() {
1079 return leveldb_env::default_env.Pointer();
1082 } // namespace leveldb