Forward accessibility events to the automation extension process.
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob67a3a5494c0996c5acac4d19813cc3f929e2e2d4
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("LevelDBEnv.IDB", true /* make_backup */) {}
326 base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
328 base::LazyInstance<ChromiumEnv>::Leaky default_env = LAZY_INSTANCE_INITIALIZER;
330 } // unnamed namespace
332 const char* MethodIDToString(MethodID method) {
333 switch (method) {
334 case kSequentialFileRead:
335 return "SequentialFileRead";
336 case kSequentialFileSkip:
337 return "SequentialFileSkip";
338 case kRandomAccessFileRead:
339 return "RandomAccessFileRead";
340 case kWritableFileAppend:
341 return "WritableFileAppend";
342 case kWritableFileClose:
343 return "WritableFileClose";
344 case kWritableFileFlush:
345 return "WritableFileFlush";
346 case kWritableFileSync:
347 return "WritableFileSync";
348 case kNewSequentialFile:
349 return "NewSequentialFile";
350 case kNewRandomAccessFile:
351 return "NewRandomAccessFile";
352 case kNewWritableFile:
353 return "NewWritableFile";
354 case kNewAppendableFile:
355 return "NewAppendableFile";
356 case kDeleteFile:
357 return "DeleteFile";
358 case kCreateDir:
359 return "CreateDir";
360 case kDeleteDir:
361 return "DeleteDir";
362 case kGetFileSize:
363 return "GetFileSize";
364 case kRenameFile:
365 return "RenameFile";
366 case kLockFile:
367 return "LockFile";
368 case kUnlockFile:
369 return "UnlockFile";
370 case kGetTestDirectory:
371 return "GetTestDirectory";
372 case kNewLogger:
373 return "NewLogger";
374 case kSyncParent:
375 return "SyncParent";
376 case kGetChildren:
377 return "GetChildren";
378 case kNumEntries:
379 NOTREACHED();
380 return "kNumEntries";
382 NOTREACHED();
383 return "Unknown";
386 Status MakeIOError(Slice filename,
387 const std::string& message,
388 MethodID method,
389 base::File::Error error) {
390 DCHECK_LT(error, 0);
391 char buf[512];
392 snprintf(buf, sizeof(buf), "%s (ChromeMethodBFE: %d::%s::%d)",
393 message.c_str(), method, MethodIDToString(method), -error);
394 return Status::IOError(filename, buf);
397 Status MakeIOError(Slice filename,
398 const std::string& message,
399 MethodID method) {
400 char buf[512];
401 snprintf(buf, sizeof(buf), "%s (ChromeMethodOnly: %d::%s)", message.c_str(),
402 method, MethodIDToString(method));
403 return Status::IOError(filename, buf);
406 ErrorParsingResult ParseMethodAndError(const leveldb::Status& status,
407 MethodID* method_param,
408 base::File::Error* error) {
409 const std::string status_string = status.ToString();
410 int method;
411 if (RE2::PartialMatch(status_string.c_str(), "ChromeMethodOnly: (\\d+)",
412 &method)) {
413 *method_param = static_cast<MethodID>(method);
414 return METHOD_ONLY;
416 int parsed_error;
417 if (RE2::PartialMatch(status_string.c_str(),
418 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method,
419 &parsed_error)) {
420 *method_param = static_cast<MethodID>(method);
421 *error = static_cast<base::File::Error>(-parsed_error);
422 DCHECK_LT(*error, base::File::FILE_OK);
423 DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
424 return METHOD_AND_BFE;
426 return NONE;
429 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
430 // change the order because indices into this array have been recorded in uma
431 // histograms.
432 const char* patterns[] = {
433 "missing files",
434 "log record too small",
435 "corrupted internal key",
436 "partial record",
437 "missing start of fragmented record",
438 "error in middle of record",
439 "unknown record type",
440 "truncated record at end",
441 "bad record length",
442 "VersionEdit",
443 "FileReader invoked with unexpected value",
444 "corrupted key",
445 "CURRENT file does not end with newline",
446 "no meta-nextfile entry",
447 "no meta-lognumber entry",
448 "no last-sequence-number entry",
449 "malformed WriteBatch",
450 "bad WriteBatch Put",
451 "bad WriteBatch Delete",
452 "unknown WriteBatch tag",
453 "WriteBatch has wrong count",
454 "bad entry in block",
455 "bad block contents",
456 "bad block handle",
457 "truncated block read",
458 "block checksum mismatch",
459 "checksum mismatch",
460 "corrupted compressed block contents",
461 "bad block type",
462 "bad magic number",
463 "file is too short",
466 // Returns 1-based index into the above array or 0 if nothing matches.
467 int GetCorruptionCode(const leveldb::Status& status) {
468 DCHECK(!status.IsIOError());
469 DCHECK(!status.ok());
470 const int kOtherError = 0;
471 int error = kOtherError;
472 const std::string& str_error = status.ToString();
473 const size_t kNumPatterns = arraysize(patterns);
474 for (size_t i = 0; i < kNumPatterns; ++i) {
475 if (str_error.find(patterns[i]) != std::string::npos) {
476 error = i + 1;
477 break;
480 return error;
483 int GetNumCorruptionCodes() {
484 // + 1 for the "other" error that is returned when a corruption message
485 // doesn't match any of the patterns.
486 return arraysize(patterns) + 1;
489 std::string GetCorruptionMessage(const leveldb::Status& status) {
490 int code = GetCorruptionCode(status);
491 if (code == 0)
492 return "Unknown corruption";
493 return patterns[code - 1];
496 bool IndicatesDiskFull(const leveldb::Status& status) {
497 if (status.ok())
498 return false;
499 leveldb_env::MethodID method;
500 base::File::Error error = base::File::FILE_OK;
501 leveldb_env::ErrorParsingResult result =
502 leveldb_env::ParseMethodAndError(status, &method, &error);
503 return (result == leveldb_env::METHOD_AND_BFE &&
504 static_cast<base::File::Error>(error) ==
505 base::File::FILE_ERROR_NO_SPACE);
508 bool ChromiumEnv::MakeBackup(const std::string& fname) {
509 FilePath original_table_name = FilePath::FromUTF8Unsafe(fname);
510 FilePath backup_table_name =
511 original_table_name.ReplaceExtension(backup_table_extension);
512 return base::CopyFile(original_table_name, backup_table_name);
515 ChromiumEnv::ChromiumEnv()
516 : ChromiumEnv("LevelDBEnv", false /* make_backup */) {}
518 ChromiumEnv::ChromiumEnv(const std::string& name, bool make_backup)
519 : name_(name),
520 make_backup_(make_backup),
521 bgsignal_(&mu_),
522 started_bgthread_(false),
523 kMaxRetryTimeMillis(1000) {
524 uma_ioerror_base_name_ = name_ + ".IOError.BFE";
527 ChromiumEnv::~ChromiumEnv() {
528 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
529 // ensure that behavior isn't accidentally changed, but there's an instance in
530 // a unit test that is deleted.
533 bool ChromiumEnv::FileExists(const std::string& fname) {
534 return base::PathExists(FilePath::FromUTF8Unsafe(fname));
537 const char* ChromiumEnv::FileErrorString(base::File::Error error) {
538 switch (error) {
539 case base::File::FILE_ERROR_FAILED:
540 return "No further details.";
541 case base::File::FILE_ERROR_IN_USE:
542 return "File currently in use.";
543 case base::File::FILE_ERROR_EXISTS:
544 return "File already exists.";
545 case base::File::FILE_ERROR_NOT_FOUND:
546 return "File not found.";
547 case base::File::FILE_ERROR_ACCESS_DENIED:
548 return "Access denied.";
549 case base::File::FILE_ERROR_TOO_MANY_OPENED:
550 return "Too many files open.";
551 case base::File::FILE_ERROR_NO_MEMORY:
552 return "Out of memory.";
553 case base::File::FILE_ERROR_NO_SPACE:
554 return "No space left on drive.";
555 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
556 return "Not a directory.";
557 case base::File::FILE_ERROR_INVALID_OPERATION:
558 return "Invalid operation.";
559 case base::File::FILE_ERROR_SECURITY:
560 return "Security error.";
561 case base::File::FILE_ERROR_ABORT:
562 return "File operation aborted.";
563 case base::File::FILE_ERROR_NOT_A_FILE:
564 return "The supplied path was not a file.";
565 case base::File::FILE_ERROR_NOT_EMPTY:
566 return "The file was not empty.";
567 case base::File::FILE_ERROR_INVALID_URL:
568 return "Invalid URL.";
569 case base::File::FILE_ERROR_IO:
570 return "OS or hardware error.";
571 case base::File::FILE_OK:
572 return "OK.";
573 case base::File::FILE_ERROR_MAX:
574 NOTREACHED();
576 NOTIMPLEMENTED();
577 return "Unknown error.";
580 FilePath ChromiumEnv::RestoreFromBackup(const FilePath& base_name) {
581 FilePath table_name = base_name.AddExtension(table_extension);
582 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
583 table_name);
584 std::string uma_name(name_);
585 uma_name.append(".TableRestore");
586 base::BooleanHistogram::FactoryGet(
587 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
588 return table_name;
591 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
592 std::vector<std::string>* dir_entries) {
593 std::set<FilePath> tables_found;
594 std::set<FilePath> backups_found;
595 for (const std::string& entry : *dir_entries) {
596 FilePath current = FilePath::FromUTF8Unsafe(entry);
597 if (current.MatchesExtension(table_extension))
598 tables_found.insert(current.RemoveExtension());
599 if (current.MatchesExtension(backup_table_extension))
600 backups_found.insert(current.RemoveExtension());
602 std::set<FilePath> backups_only =
603 base::STLSetDifference<std::set<FilePath>>(backups_found, tables_found);
605 if (backups_only.size()) {
606 std::string uma_name(name_);
607 uma_name.append(".MissingFiles");
608 int num_missing_files =
609 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
610 base::Histogram::FactoryGet(uma_name,
611 1 /*min*/,
612 100 /*max*/,
613 8 /*num_buckets*/,
614 base::Histogram::kUmaTargetedHistogramFlag)
615 ->Add(num_missing_files);
617 FilePath dir_path = FilePath::FromUTF8Unsafe(dir);
618 for (const FilePath& backup : backups_only) {
619 FilePath restored_table_name = RestoreFromBackup(dir_path.Append(backup));
620 dir_entries->push_back(restored_table_name.BaseName().AsUTF8Unsafe());
624 Status ChromiumEnv::GetChildren(const std::string& dir,
625 std::vector<std::string>* result) {
626 std::vector<FilePath> entries;
627 base::File::Error error =
628 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir), &entries);
629 if (error != base::File::FILE_OK) {
630 RecordOSError(kGetChildren, error);
631 return MakeIOError(dir, "Could not open/read directory", kGetChildren,
632 error);
635 result->clear();
636 for (const auto& entry : entries)
637 result->push_back(entry.BaseName().AsUTF8Unsafe());
639 if (make_backup_)
640 RestoreIfNecessary(dir, result);
642 return Status::OK();
645 Status ChromiumEnv::DeleteFile(const std::string& fname) {
646 Status result;
647 FilePath fname_filepath = FilePath::FromUTF8Unsafe(fname);
648 // TODO(jorlow): Should we assert this is a file?
649 if (!base::DeleteFile(fname_filepath, false)) {
650 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
651 RecordErrorAt(kDeleteFile);
653 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
654 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
655 false);
657 return result;
660 Status ChromiumEnv::CreateDir(const std::string& name) {
661 Status result;
662 base::File::Error error = base::File::FILE_OK;
663 Retrier retrier(kCreateDir, this);
664 do {
665 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name),
666 &error))
667 return result;
668 } while (retrier.ShouldKeepTrying(error));
669 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
670 RecordOSError(kCreateDir, error);
671 return result;
674 Status ChromiumEnv::DeleteDir(const std::string& name) {
675 Status result;
676 // TODO(jorlow): Should we assert this is a directory?
677 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name), false)) {
678 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
679 RecordErrorAt(kDeleteDir);
681 return result;
684 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
685 Status s;
686 int64_t signed_size;
687 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname), &signed_size)) {
688 *size = 0;
689 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
690 RecordErrorAt(kGetFileSize);
691 } else {
692 *size = static_cast<uint64_t>(signed_size);
694 return s;
697 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
698 Status result;
699 FilePath src_file_path = FilePath::FromUTF8Unsafe(src);
700 if (!base::PathExists(src_file_path))
701 return result;
702 FilePath destination = FilePath::FromUTF8Unsafe(dst);
704 Retrier retrier(kRenameFile, this);
705 base::File::Error error = base::File::FILE_OK;
706 do {
707 if (base::ReplaceFile(src_file_path, destination, &error))
708 return result;
709 } while (retrier.ShouldKeepTrying(error));
711 DCHECK(error != base::File::FILE_OK);
712 RecordOSError(kRenameFile, error);
713 char buf[100];
714 snprintf(buf,
715 sizeof(buf),
716 "Could not rename file: %s",
717 FileErrorString(error));
718 return MakeIOError(src, buf, kRenameFile, error);
721 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
722 *lock = NULL;
723 Status result;
724 int flags = base::File::FLAG_OPEN_ALWAYS |
725 base::File::FLAG_READ |
726 base::File::FLAG_WRITE;
727 base::File::Error error_code;
728 base::File file;
729 Retrier retrier(kLockFile, this);
730 do {
731 file.Initialize(FilePath::FromUTF8Unsafe(fname), flags);
732 if (!file.IsValid())
733 error_code = file.error_details();
734 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
736 if (!file.IsValid()) {
737 if (error_code == base::File::FILE_ERROR_NOT_FOUND) {
738 FilePath parent = FilePath::FromUTF8Unsafe(fname).DirName();
739 FilePath last_parent;
740 int num_missing_ancestors = 0;
741 do {
742 if (base::DirectoryExists(parent))
743 break;
744 ++num_missing_ancestors;
745 last_parent = parent;
746 parent = parent.DirName();
747 } while (parent != last_parent);
748 RecordLockFileAncestors(num_missing_ancestors);
751 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
752 error_code);
753 RecordOSError(kLockFile, error_code);
754 return result;
757 if (!locks_.Insert(fname)) {
758 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
759 return result;
762 Retrier lock_retrier = Retrier(kLockFile, this);
763 do {
764 error_code = file.Lock();
765 } while (error_code != base::File::FILE_OK &&
766 retrier.ShouldKeepTrying(error_code));
768 if (error_code != base::File::FILE_OK) {
769 locks_.Remove(fname);
770 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
771 error_code);
772 RecordOSError(kLockFile, error_code);
773 return result;
776 ChromiumFileLock* my_lock = new ChromiumFileLock;
777 my_lock->file_ = file.Pass();
778 my_lock->name_ = fname;
779 *lock = my_lock;
780 return result;
783 Status ChromiumEnv::UnlockFile(FileLock* lock) {
784 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
785 Status result;
787 base::File::Error error_code = my_lock->file_.Unlock();
788 if (error_code != base::File::FILE_OK) {
789 result =
790 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
791 RecordOSError(kUnlockFile, error_code);
793 bool removed = locks_.Remove(my_lock->name_);
794 DCHECK(removed);
795 delete my_lock;
796 return result;
799 Status ChromiumEnv::GetTestDirectory(std::string* path) {
800 mu_.Acquire();
801 if (test_directory_.empty()) {
802 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
803 &test_directory_)) {
804 mu_.Release();
805 RecordErrorAt(kGetTestDirectory);
806 return MakeIOError(
807 "Could not create temp directory.", "", kGetTestDirectory);
810 *path = test_directory_.AsUTF8Unsafe();
811 mu_.Release();
812 return Status::OK();
815 Status ChromiumEnv::NewLogger(const std::string& fname,
816 leveldb::Logger** result) {
817 FilePath path = FilePath::FromUTF8Unsafe(fname);
818 scoped_ptr<base::File> f(new base::File(
819 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
820 if (!f->IsValid()) {
821 *result = NULL;
822 RecordOSError(kNewLogger, f->error_details());
823 return MakeIOError(fname, "Unable to create log file", kNewLogger,
824 f->error_details());
825 } else {
826 *result = new leveldb::ChromiumLogger(f.release());
827 return Status::OK();
831 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
832 leveldb::SequentialFile** result) {
833 FilePath path = FilePath::FromUTF8Unsafe(fname);
834 scoped_ptr<base::File> f(
835 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
836 if (!f->IsValid()) {
837 *result = NULL;
838 RecordOSError(kNewSequentialFile, f->error_details());
839 return MakeIOError(fname, "Unable to create sequential file",
840 kNewSequentialFile, f->error_details());
841 } else {
842 *result = new ChromiumSequentialFile(fname, f.release(), this);
843 return Status::OK();
847 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
848 #if defined(OS_POSIX)
849 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
850 #elif defined(OS_WIN)
851 // Windows is only limited by available memory
852 #else
853 #error "Need to determine limit to open files for this OS"
854 #endif
857 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
858 leveldb::RandomAccessFile** result) {
859 int flags = base::File::FLAG_READ | base::File::FLAG_OPEN;
860 base::File file(FilePath::FromUTF8Unsafe(fname), flags);
861 if (file.IsValid()) {
862 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
863 RecordOpenFilesLimit("Success");
864 return Status::OK();
866 base::File::Error error_code = file.error_details();
867 if (error_code == base::File::FILE_ERROR_TOO_MANY_OPENED)
868 RecordOpenFilesLimit("TooManyOpened");
869 else
870 RecordOpenFilesLimit("OtherError");
871 *result = NULL;
872 RecordOSError(kNewRandomAccessFile, error_code);
873 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
874 error_code);
877 Status ChromiumEnv::NewWritableFile(const std::string& fname,
878 leveldb::WritableFile** result) {
879 *result = NULL;
880 FilePath path = FilePath::FromUTF8Unsafe(fname);
881 scoped_ptr<base::File> f(new base::File(
882 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
883 if (!f->IsValid()) {
884 RecordErrorAt(kNewWritableFile);
885 return MakeIOError(fname, "Unable to create writable file",
886 kNewWritableFile, f->error_details());
887 } else {
888 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
889 return Status::OK();
893 Status ChromiumEnv::NewAppendableFile(const std::string& fname,
894 leveldb::WritableFile** result) {
895 *result = NULL;
896 FilePath path = FilePath::FromUTF8Unsafe(fname);
897 scoped_ptr<base::File> f(new base::File(
898 path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND));
899 if (!f->IsValid()) {
900 RecordErrorAt(kNewAppendableFile);
901 return MakeIOError(fname, "Unable to create appendable file",
902 kNewAppendableFile, f->error_details());
904 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
905 return Status::OK();
908 uint64_t ChromiumEnv::NowMicros() {
909 return base::TimeTicks::Now().ToInternalValue();
912 void ChromiumEnv::SleepForMicroseconds(int micros) {
913 // Round up to the next millisecond.
914 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
917 void ChromiumEnv::RecordErrorAt(MethodID method) const {
918 GetMethodIOErrorHistogram()->Add(method);
921 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
922 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
925 void ChromiumEnv::RecordOSError(MethodID method,
926 base::File::Error error) const {
927 DCHECK_LT(error, 0);
928 RecordErrorAt(method);
929 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
932 void ChromiumEnv::RecordBackupResult(bool result) const {
933 std::string uma_name(name_);
934 uma_name.append(".TableBackup");
935 base::BooleanHistogram::FactoryGet(
936 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
939 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
940 int limit) const {
941 std::string uma_name;
942 base::StringAppendF(&uma_name, "%s.%s", uma_ioerror_base_name_.c_str(),
943 MethodIDToString(method));
944 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
945 base::Histogram::kUmaTargetedHistogramFlag);
948 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
949 std::string uma_name(name_);
950 uma_name.append(".IOError");
951 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
952 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
955 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
956 const std::string& type) const {
957 std::string uma_name(name_);
958 uma_name.append(".MaxFDs.").append(type);
959 // These numbers make each bucket twice as large as the previous bucket.
960 const int kFirstEntry = 1;
961 const int kLastEntry = 65536;
962 const int kNumBuckets = 18;
963 return base::Histogram::FactoryGet(
964 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
965 base::Histogram::kUmaTargetedHistogramFlag);
968 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
969 std::string uma_name(name_);
970 uma_name.append(".LockFileAncestorsNotFound");
971 const int kMin = 1;
972 const int kMax = 10;
973 const int kNumBuckets = 11;
974 return base::LinearHistogram::FactoryGet(
975 uma_name, kMin, kMax, kNumBuckets,
976 base::Histogram::kUmaTargetedHistogramFlag);
979 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
980 std::string uma_name(name_);
981 // TODO(dgrogan): This is probably not the best way to concatenate strings.
982 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
984 const int kBucketSizeMillis = 25;
985 // Add 2, 1 for each of the buckets <1 and >max.
986 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
987 return base::Histogram::FactoryTimeGet(
988 uma_name, base::TimeDelta::FromMilliseconds(1),
989 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
990 kNumBuckets,
991 base::Histogram::kUmaTargetedHistogramFlag);
994 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
995 MethodID method) const {
996 std::string uma_name(name_);
997 uma_name.append(".RetryRecoveredFromErrorIn")
998 .append(MethodIDToString(method));
999 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1000 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1003 class Thread : public base::PlatformThread::Delegate {
1004 public:
1005 Thread(void (*function)(void* arg), void* arg)
1006 : function_(function), arg_(arg) {
1007 base::PlatformThreadHandle handle;
1008 bool success = base::PlatformThread::Create(0, this, &handle);
1009 DCHECK(success);
1011 virtual ~Thread() {}
1012 void ThreadMain() override {
1013 (*function_)(arg_);
1014 delete this;
1017 private:
1018 void (*function_)(void* arg);
1019 void* arg_;
1022 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1023 mu_.Acquire();
1025 // Start background thread if necessary
1026 if (!started_bgthread_) {
1027 started_bgthread_ = true;
1028 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1031 // If the queue is currently empty, the background thread may currently be
1032 // waiting.
1033 if (queue_.empty()) {
1034 bgsignal_.Signal();
1037 // Add to priority queue
1038 queue_.push_back(BGItem());
1039 queue_.back().function = function;
1040 queue_.back().arg = arg;
1042 mu_.Release();
1045 void ChromiumEnv::BGThread() {
1046 base::PlatformThread::SetName(name_.c_str());
1048 while (true) {
1049 // Wait until there is an item that is ready to run
1050 mu_.Acquire();
1051 while (queue_.empty()) {
1052 bgsignal_.Wait();
1055 void (*function)(void*) = queue_.front().function;
1056 void* arg = queue_.front().arg;
1057 queue_.pop_front();
1059 mu_.Release();
1060 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1061 (*function)(arg);
1065 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1066 new Thread(function, arg); // Will self-delete.
1069 } // namespace leveldb_env
1071 namespace leveldb {
1073 Env* IDBEnv() {
1074 return leveldb_env::idb_env.Pointer();
1077 Env* Env::Default() {
1078 return leveldb_env::default_env.Pointer();
1081 } // namespace leveldb