Roll src/third_party/skia 2440fcd:4de8c3a
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob939534c6dfe27e2ab267355b3538d4a7d9a47a78
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/metrics/histogram.h"
15 #include "base/process/process_metrics.h"
16 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "base/trace_event/trace_event.h"
21 #include "third_party/leveldatabase/chromium_logger.h"
22 #include "third_party/re2/re2/re2.h"
24 using base::FilePath;
25 using leveldb::FileLock;
26 using leveldb::Slice;
27 using leveldb::Status;
29 namespace leveldb_env {
31 namespace {
33 const FilePath::CharType backup_table_extension[] = FILE_PATH_LITERAL(".bak");
34 const FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
36 static const FilePath::CharType kLevelDBTestDirectoryPrefix[] =
37 FILE_PATH_LITERAL("leveldb-test-");
39 static base::File::Error LastFileError() {
40 #if defined(OS_WIN)
41 return base::File::OSErrorToFileError(GetLastError());
42 #else
43 return base::File::OSErrorToFileError(errno);
44 #endif
47 // Making direct platform in lieu of using base::FileEnumerator because the
48 // latter can fail quietly without return an error result.
49 static base::File::Error GetDirectoryEntries(const FilePath& dir_param,
50 std::vector<FilePath>* result) {
51 base::ThreadRestrictions::AssertIOAllowed();
52 result->clear();
53 #if defined(OS_WIN)
54 FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
55 WIN32_FIND_DATA find_data;
56 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
57 if (find_handle == INVALID_HANDLE_VALUE) {
58 DWORD last_error = GetLastError();
59 if (last_error == ERROR_FILE_NOT_FOUND)
60 return base::File::FILE_OK;
61 return base::File::OSErrorToFileError(last_error);
63 do {
64 FilePath filepath(find_data.cFileName);
65 FilePath::StringType basename = filepath.BaseName().value();
66 if (basename == FILE_PATH_LITERAL(".") ||
67 basename == FILE_PATH_LITERAL(".."))
68 continue;
69 result->push_back(filepath.BaseName());
70 } while (FindNextFile(find_handle, &find_data));
71 DWORD last_error = GetLastError();
72 base::File::Error return_value = base::File::FILE_OK;
73 if (last_error != ERROR_NO_MORE_FILES)
74 return_value = base::File::OSErrorToFileError(last_error);
75 FindClose(find_handle);
76 return return_value;
77 #else
78 const std::string dir_string = dir_param.AsUTF8Unsafe();
79 DIR* dir = opendir(dir_string.c_str());
80 if (!dir)
81 return base::File::OSErrorToFileError(errno);
82 struct dirent dent_buf;
83 struct dirent* dent;
84 int readdir_result;
85 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
86 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
87 continue;
88 result->push_back(FilePath::FromUTF8Unsafe(dent->d_name));
90 int saved_errno = errno;
91 closedir(dir);
92 if (readdir_result != 0)
93 return base::File::OSErrorToFileError(saved_errno);
94 return base::File::FILE_OK;
95 #endif
98 class ChromiumFileLock : public FileLock {
99 public:
100 base::File file_;
101 std::string name_;
104 class Retrier {
105 public:
106 Retrier(MethodID method, RetrierProvider* provider)
107 : start_(base::TimeTicks::Now()),
108 limit_(start_ + base::TimeDelta::FromMilliseconds(
109 provider->MaxRetryTimeMillis())),
110 last_(start_),
111 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
112 success_(true),
113 method_(method),
114 last_error_(base::File::FILE_OK),
115 provider_(provider) {}
116 ~Retrier() {
117 if (success_) {
118 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
119 if (last_error_ != base::File::FILE_OK) {
120 DCHECK_LT(last_error_, 0);
121 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
125 bool ShouldKeepTrying(base::File::Error last_error) {
126 DCHECK_NE(last_error, base::File::FILE_OK);
127 last_error_ = last_error;
128 if (last_ < limit_) {
129 base::PlatformThread::Sleep(time_to_sleep_);
130 last_ = base::TimeTicks::Now();
131 return true;
133 success_ = false;
134 return false;
137 private:
138 base::TimeTicks start_;
139 base::TimeTicks limit_;
140 base::TimeTicks last_;
141 base::TimeDelta time_to_sleep_;
142 bool success_;
143 MethodID method_;
144 base::File::Error last_error_;
145 RetrierProvider* provider_;
148 class ChromiumSequentialFile : public leveldb::SequentialFile {
149 public:
150 ChromiumSequentialFile(const std::string& fname,
151 base::File* f,
152 const UMALogger* uma_logger)
153 : filename_(fname), file_(f), uma_logger_(uma_logger) {}
154 virtual ~ChromiumSequentialFile() {}
156 Status Read(size_t n, Slice* result, char* scratch) override {
157 int bytes_read = file_->ReadAtCurrentPosNoBestEffort(scratch, n);
158 if (bytes_read == -1) {
159 base::File::Error error = LastFileError();
160 uma_logger_->RecordErrorAt(kSequentialFileRead);
161 return MakeIOError(filename_, base::File::ErrorToString(error),
162 kSequentialFileRead, error);
163 } else {
164 *result = Slice(scratch, bytes_read);
165 return Status::OK();
169 Status Skip(uint64_t n) override {
170 if (file_->Seek(base::File::FROM_CURRENT, n) == -1) {
171 base::File::Error error = LastFileError();
172 uma_logger_->RecordErrorAt(kSequentialFileSkip);
173 return MakeIOError(filename_, base::File::ErrorToString(error),
174 kSequentialFileSkip, error);
175 } else {
176 return Status::OK();
180 private:
181 std::string filename_;
182 scoped_ptr<base::File> file_;
183 const UMALogger* uma_logger_;
186 class ChromiumRandomAccessFile : public leveldb::RandomAccessFile {
187 public:
188 ChromiumRandomAccessFile(const std::string& fname,
189 base::File file,
190 const UMALogger* uma_logger)
191 : filename_(fname), file_(file.Pass()), uma_logger_(uma_logger) {}
192 virtual ~ChromiumRandomAccessFile() {}
194 Status Read(uint64_t offset,
195 size_t n,
196 Slice* result,
197 char* scratch) const override {
198 Status s;
199 int r = file_.Read(offset, scratch, n);
200 *result = Slice(scratch, (r < 0) ? 0 : r);
201 if (r < 0) {
202 // An error: return a non-ok status
203 s = MakeIOError(filename_, "Could not perform read",
204 kRandomAccessFileRead);
205 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
207 return s;
210 private:
211 std::string filename_;
212 mutable base::File file_;
213 const UMALogger* uma_logger_;
216 class ChromiumWritableFile : public leveldb::WritableFile {
217 public:
218 ChromiumWritableFile(const std::string& fname,
219 base::File* f,
220 const UMALogger* uma_logger,
221 bool make_backup);
222 virtual ~ChromiumWritableFile() {}
223 leveldb::Status Append(const leveldb::Slice& data) override;
224 leveldb::Status Close() override;
225 leveldb::Status Flush() override;
226 leveldb::Status Sync() override;
228 private:
229 enum Type { kManifest, kTable, kOther };
230 leveldb::Status SyncParent();
232 std::string filename_;
233 scoped_ptr<base::File> file_;
234 const UMALogger* uma_logger_;
235 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 bool make_backup)
244 : filename_(fname),
245 file_(f),
246 uma_logger_(uma_logger),
247 file_type_(kOther),
248 make_backup_(make_backup) {
249 FilePath path = FilePath::FromUTF8Unsafe(fname);
250 if (path.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
251 file_type_ = kManifest;
252 else if (path.MatchesExtension(table_extension))
253 file_type_ = kTable;
254 parent_dir_ = FilePath::FromUTF8Unsafe(fname).DirName().AsUTF8Unsafe();
257 Status ChromiumWritableFile::SyncParent() {
258 TRACE_EVENT0("leveldb", "SyncParent");
259 #if defined(OS_POSIX)
260 FilePath path = FilePath::FromUTF8Unsafe(parent_dir_);
261 base::File f(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
262 if (!f.IsValid()) {
263 return MakeIOError(parent_dir_, "Unable to open directory", kSyncParent,
264 f.error_details());
266 if (!f.Flush()) {
267 base::File::Error error = LastFileError();
268 return MakeIOError(parent_dir_, base::File::ErrorToString(error),
269 kSyncParent, error);
271 #endif
272 return Status::OK();
275 Status ChromiumWritableFile::Append(const Slice& data) {
276 int bytes_written = file_->WriteAtCurrentPos(data.data(), data.size());
277 if (bytes_written != data.size()) {
278 base::File::Error error = LastFileError();
279 uma_logger_->RecordOSError(kWritableFileAppend, error);
280 return MakeIOError(filename_, base::File::ErrorToString(error),
281 kWritableFileAppend, error);
284 return Status::OK();
287 Status ChromiumWritableFile::Close() {
288 file_->Close();
289 return Status::OK();
292 Status ChromiumWritableFile::Flush() {
293 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
294 // flush.
295 return Status::OK();
298 Status ChromiumWritableFile::Sync() {
299 TRACE_EVENT0("leveldb", "WritableFile::Sync");
301 if (!file_->Flush()) {
302 base::File::Error error = LastFileError();
303 uma_logger_->RecordErrorAt(kWritableFileSync);
304 return MakeIOError(filename_, base::File::ErrorToString(error),
305 kWritableFileSync, error);
308 if (make_backup_ && file_type_ == kTable)
309 uma_logger_->RecordBackupResult(ChromiumEnv::MakeBackup(filename_));
311 // leveldb's implicit contract for Sync() is that if this instance is for a
312 // manifest file then the directory is also sync'ed. See leveldb's
313 // env_posix.cc.
314 if (file_type_ == kManifest)
315 return SyncParent();
317 return Status::OK();
320 base::LazyInstance<ChromiumEnv>::Leaky default_env = LAZY_INSTANCE_INITIALIZER;
322 } // unnamed namespace
324 const char* MethodIDToString(MethodID method) {
325 switch (method) {
326 case kSequentialFileRead:
327 return "SequentialFileRead";
328 case kSequentialFileSkip:
329 return "SequentialFileSkip";
330 case kRandomAccessFileRead:
331 return "RandomAccessFileRead";
332 case kWritableFileAppend:
333 return "WritableFileAppend";
334 case kWritableFileClose:
335 return "WritableFileClose";
336 case kWritableFileFlush:
337 return "WritableFileFlush";
338 case kWritableFileSync:
339 return "WritableFileSync";
340 case kNewSequentialFile:
341 return "NewSequentialFile";
342 case kNewRandomAccessFile:
343 return "NewRandomAccessFile";
344 case kNewWritableFile:
345 return "NewWritableFile";
346 case kNewAppendableFile:
347 return "NewAppendableFile";
348 case kDeleteFile:
349 return "DeleteFile";
350 case kCreateDir:
351 return "CreateDir";
352 case kDeleteDir:
353 return "DeleteDir";
354 case kGetFileSize:
355 return "GetFileSize";
356 case kRenameFile:
357 return "RenameFile";
358 case kLockFile:
359 return "LockFile";
360 case kUnlockFile:
361 return "UnlockFile";
362 case kGetTestDirectory:
363 return "GetTestDirectory";
364 case kNewLogger:
365 return "NewLogger";
366 case kSyncParent:
367 return "SyncParent";
368 case kGetChildren:
369 return "GetChildren";
370 case kNumEntries:
371 NOTREACHED();
372 return "kNumEntries";
374 NOTREACHED();
375 return "Unknown";
378 Status MakeIOError(Slice filename,
379 const std::string& message,
380 MethodID method,
381 base::File::Error error) {
382 DCHECK_LT(error, 0);
383 char buf[512];
384 snprintf(buf, sizeof(buf), "%s (ChromeMethodBFE: %d::%s::%d)",
385 message.c_str(), method, MethodIDToString(method), -error);
386 return Status::IOError(filename, buf);
389 Status MakeIOError(Slice filename,
390 const std::string& message,
391 MethodID method) {
392 char buf[512];
393 snprintf(buf, sizeof(buf), "%s (ChromeMethodOnly: %d::%s)", message.c_str(),
394 method, MethodIDToString(method));
395 return Status::IOError(filename, buf);
398 ErrorParsingResult ParseMethodAndError(const leveldb::Status& status,
399 MethodID* method_param,
400 base::File::Error* error) {
401 const std::string status_string = status.ToString();
402 int method;
403 if (RE2::PartialMatch(status_string.c_str(), "ChromeMethodOnly: (\\d+)",
404 &method)) {
405 *method_param = static_cast<MethodID>(method);
406 return METHOD_ONLY;
408 int parsed_error;
409 if (RE2::PartialMatch(status_string.c_str(),
410 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method,
411 &parsed_error)) {
412 *method_param = static_cast<MethodID>(method);
413 *error = static_cast<base::File::Error>(-parsed_error);
414 DCHECK_LT(*error, base::File::FILE_OK);
415 DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
416 return METHOD_AND_BFE;
418 return NONE;
421 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
422 // change the order because indices into this array have been recorded in uma
423 // histograms.
424 const char* patterns[] = {
425 "missing files",
426 "log record too small",
427 "corrupted internal key",
428 "partial record",
429 "missing start of fragmented record",
430 "error in middle of record",
431 "unknown record type",
432 "truncated record at end",
433 "bad record length",
434 "VersionEdit",
435 "FileReader invoked with unexpected value",
436 "corrupted key",
437 "CURRENT file does not end with newline",
438 "no meta-nextfile entry",
439 "no meta-lognumber entry",
440 "no last-sequence-number entry",
441 "malformed WriteBatch",
442 "bad WriteBatch Put",
443 "bad WriteBatch Delete",
444 "unknown WriteBatch tag",
445 "WriteBatch has wrong count",
446 "bad entry in block",
447 "bad block contents",
448 "bad block handle",
449 "truncated block read",
450 "block checksum mismatch",
451 "checksum mismatch",
452 "corrupted compressed block contents",
453 "bad block type",
454 "bad magic number",
455 "file is too short",
458 // Returns 1-based index into the above array or 0 if nothing matches.
459 int GetCorruptionCode(const leveldb::Status& status) {
460 DCHECK(!status.IsIOError());
461 DCHECK(!status.ok());
462 const int kOtherError = 0;
463 int error = kOtherError;
464 const std::string& str_error = status.ToString();
465 const size_t kNumPatterns = arraysize(patterns);
466 for (size_t i = 0; i < kNumPatterns; ++i) {
467 if (str_error.find(patterns[i]) != std::string::npos) {
468 error = i + 1;
469 break;
472 return error;
475 int GetNumCorruptionCodes() {
476 // + 1 for the "other" error that is returned when a corruption message
477 // doesn't match any of the patterns.
478 return arraysize(patterns) + 1;
481 std::string GetCorruptionMessage(const leveldb::Status& status) {
482 int code = GetCorruptionCode(status);
483 if (code == 0)
484 return "Unknown corruption";
485 return patterns[code - 1];
488 bool IndicatesDiskFull(const leveldb::Status& status) {
489 if (status.ok())
490 return false;
491 leveldb_env::MethodID method;
492 base::File::Error error = base::File::FILE_OK;
493 leveldb_env::ErrorParsingResult result =
494 leveldb_env::ParseMethodAndError(status, &method, &error);
495 return (result == leveldb_env::METHOD_AND_BFE &&
496 static_cast<base::File::Error>(error) ==
497 base::File::FILE_ERROR_NO_SPACE);
500 bool ChromiumEnv::MakeBackup(const std::string& fname) {
501 FilePath original_table_name = FilePath::FromUTF8Unsafe(fname);
502 FilePath backup_table_name =
503 original_table_name.ReplaceExtension(backup_table_extension);
504 return base::CopyFile(original_table_name, backup_table_name);
507 ChromiumEnv::ChromiumEnv()
508 : ChromiumEnv("LevelDBEnv", false /* make_backup */) {}
510 ChromiumEnv::ChromiumEnv(const std::string& name, bool make_backup)
511 : kMaxRetryTimeMillis(1000),
512 name_(name),
513 make_backup_(make_backup),
514 bgsignal_(&mu_),
515 started_bgthread_(false) {
516 uma_ioerror_base_name_ = name_ + ".IOError.BFE";
519 ChromiumEnv::~ChromiumEnv() {
520 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
521 // ensure that behavior isn't accidentally changed, but there's an instance in
522 // a unit test that is deleted.
525 bool ChromiumEnv::FileExists(const std::string& fname) {
526 return base::PathExists(FilePath::FromUTF8Unsafe(fname));
529 const char* ChromiumEnv::FileErrorString(base::File::Error error) {
530 switch (error) {
531 case base::File::FILE_ERROR_FAILED:
532 return "No further details.";
533 case base::File::FILE_ERROR_IN_USE:
534 return "File currently in use.";
535 case base::File::FILE_ERROR_EXISTS:
536 return "File already exists.";
537 case base::File::FILE_ERROR_NOT_FOUND:
538 return "File not found.";
539 case base::File::FILE_ERROR_ACCESS_DENIED:
540 return "Access denied.";
541 case base::File::FILE_ERROR_TOO_MANY_OPENED:
542 return "Too many files open.";
543 case base::File::FILE_ERROR_NO_MEMORY:
544 return "Out of memory.";
545 case base::File::FILE_ERROR_NO_SPACE:
546 return "No space left on drive.";
547 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
548 return "Not a directory.";
549 case base::File::FILE_ERROR_INVALID_OPERATION:
550 return "Invalid operation.";
551 case base::File::FILE_ERROR_SECURITY:
552 return "Security error.";
553 case base::File::FILE_ERROR_ABORT:
554 return "File operation aborted.";
555 case base::File::FILE_ERROR_NOT_A_FILE:
556 return "The supplied path was not a file.";
557 case base::File::FILE_ERROR_NOT_EMPTY:
558 return "The file was not empty.";
559 case base::File::FILE_ERROR_INVALID_URL:
560 return "Invalid URL.";
561 case base::File::FILE_ERROR_IO:
562 return "OS or hardware error.";
563 case base::File::FILE_OK:
564 return "OK.";
565 case base::File::FILE_ERROR_MAX:
566 NOTREACHED();
568 NOTIMPLEMENTED();
569 return "Unknown error.";
572 FilePath ChromiumEnv::RestoreFromBackup(const FilePath& base_name) {
573 FilePath table_name = base_name.AddExtension(table_extension);
574 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
575 table_name);
576 std::string uma_name(name_);
577 uma_name.append(".TableRestore");
578 base::BooleanHistogram::FactoryGet(
579 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
580 return table_name;
583 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
584 std::vector<std::string>* dir_entries) {
585 std::set<FilePath> tables_found;
586 std::set<FilePath> backups_found;
587 for (const std::string& entry : *dir_entries) {
588 FilePath current = FilePath::FromUTF8Unsafe(entry);
589 if (current.MatchesExtension(table_extension))
590 tables_found.insert(current.RemoveExtension());
591 if (current.MatchesExtension(backup_table_extension))
592 backups_found.insert(current.RemoveExtension());
594 std::set<FilePath> backups_only =
595 base::STLSetDifference<std::set<FilePath>>(backups_found, tables_found);
597 if (backups_only.size()) {
598 std::string uma_name(name_);
599 uma_name.append(".MissingFiles");
600 int num_missing_files =
601 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
602 base::Histogram::FactoryGet(uma_name,
603 1 /*min*/,
604 100 /*max*/,
605 8 /*num_buckets*/,
606 base::Histogram::kUmaTargetedHistogramFlag)
607 ->Add(num_missing_files);
609 FilePath dir_path = FilePath::FromUTF8Unsafe(dir);
610 for (const FilePath& backup : backups_only) {
611 FilePath restored_table_name = RestoreFromBackup(dir_path.Append(backup));
612 dir_entries->push_back(restored_table_name.BaseName().AsUTF8Unsafe());
616 Status ChromiumEnv::GetChildren(const std::string& dir,
617 std::vector<std::string>* result) {
618 std::vector<FilePath> entries;
619 base::File::Error error =
620 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir), &entries);
621 if (error != base::File::FILE_OK) {
622 RecordOSError(kGetChildren, error);
623 return MakeIOError(dir, "Could not open/read directory", kGetChildren,
624 error);
627 result->clear();
628 for (const auto& entry : entries)
629 result->push_back(entry.BaseName().AsUTF8Unsafe());
631 if (make_backup_)
632 RestoreIfNecessary(dir, result);
634 return Status::OK();
637 Status ChromiumEnv::DeleteFile(const std::string& fname) {
638 Status result;
639 FilePath fname_filepath = FilePath::FromUTF8Unsafe(fname);
640 // TODO(jorlow): Should we assert this is a file?
641 if (!base::DeleteFile(fname_filepath, false)) {
642 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
643 RecordErrorAt(kDeleteFile);
645 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
646 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
647 false);
649 return result;
652 Status ChromiumEnv::CreateDir(const std::string& name) {
653 Status result;
654 base::File::Error error = base::File::FILE_OK;
655 Retrier retrier(kCreateDir, this);
656 do {
657 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name),
658 &error))
659 return result;
660 } while (retrier.ShouldKeepTrying(error));
661 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
662 RecordOSError(kCreateDir, error);
663 return result;
666 Status ChromiumEnv::DeleteDir(const std::string& name) {
667 Status result;
668 // TODO(jorlow): Should we assert this is a directory?
669 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name), false)) {
670 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
671 RecordErrorAt(kDeleteDir);
673 return result;
676 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
677 Status s;
678 int64_t signed_size;
679 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname), &signed_size)) {
680 *size = 0;
681 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
682 RecordErrorAt(kGetFileSize);
683 } else {
684 *size = static_cast<uint64_t>(signed_size);
686 return s;
689 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
690 Status result;
691 FilePath src_file_path = FilePath::FromUTF8Unsafe(src);
692 if (!base::PathExists(src_file_path))
693 return result;
694 FilePath destination = FilePath::FromUTF8Unsafe(dst);
696 Retrier retrier(kRenameFile, this);
697 base::File::Error error = base::File::FILE_OK;
698 do {
699 if (base::ReplaceFile(src_file_path, destination, &error))
700 return result;
701 } while (retrier.ShouldKeepTrying(error));
703 DCHECK(error != base::File::FILE_OK);
704 RecordOSError(kRenameFile, error);
705 char buf[100];
706 snprintf(buf,
707 sizeof(buf),
708 "Could not rename file: %s",
709 FileErrorString(error));
710 return MakeIOError(src, buf, kRenameFile, error);
713 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
714 *lock = NULL;
715 Status result;
716 int flags = base::File::FLAG_OPEN_ALWAYS |
717 base::File::FLAG_READ |
718 base::File::FLAG_WRITE;
719 base::File::Error error_code;
720 base::File file;
721 Retrier retrier(kLockFile, this);
722 do {
723 file.Initialize(FilePath::FromUTF8Unsafe(fname), flags);
724 if (!file.IsValid())
725 error_code = file.error_details();
726 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
728 if (!file.IsValid()) {
729 if (error_code == base::File::FILE_ERROR_NOT_FOUND) {
730 FilePath parent = FilePath::FromUTF8Unsafe(fname).DirName();
731 FilePath last_parent;
732 int num_missing_ancestors = 0;
733 do {
734 if (base::DirectoryExists(parent))
735 break;
736 ++num_missing_ancestors;
737 last_parent = parent;
738 parent = parent.DirName();
739 } while (parent != last_parent);
740 RecordLockFileAncestors(num_missing_ancestors);
743 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
744 error_code);
745 RecordOSError(kLockFile, error_code);
746 return result;
749 if (!locks_.Insert(fname)) {
750 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
751 return result;
754 Retrier lock_retrier = Retrier(kLockFile, this);
755 do {
756 error_code = file.Lock();
757 } while (error_code != base::File::FILE_OK &&
758 retrier.ShouldKeepTrying(error_code));
760 if (error_code != base::File::FILE_OK) {
761 locks_.Remove(fname);
762 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
763 error_code);
764 RecordOSError(kLockFile, error_code);
765 return result;
768 ChromiumFileLock* my_lock = new ChromiumFileLock;
769 my_lock->file_ = file.Pass();
770 my_lock->name_ = fname;
771 *lock = my_lock;
772 return result;
775 Status ChromiumEnv::UnlockFile(FileLock* lock) {
776 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
777 Status result;
779 base::File::Error error_code = my_lock->file_.Unlock();
780 if (error_code != base::File::FILE_OK) {
781 result =
782 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
783 RecordOSError(kUnlockFile, error_code);
785 bool removed = locks_.Remove(my_lock->name_);
786 DCHECK(removed);
787 delete my_lock;
788 return result;
791 Status ChromiumEnv::GetTestDirectory(std::string* path) {
792 mu_.Acquire();
793 if (test_directory_.empty()) {
794 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
795 &test_directory_)) {
796 mu_.Release();
797 RecordErrorAt(kGetTestDirectory);
798 return MakeIOError(
799 "Could not create temp directory.", "", kGetTestDirectory);
802 *path = test_directory_.AsUTF8Unsafe();
803 mu_.Release();
804 return Status::OK();
807 Status ChromiumEnv::NewLogger(const std::string& fname,
808 leveldb::Logger** result) {
809 FilePath path = FilePath::FromUTF8Unsafe(fname);
810 scoped_ptr<base::File> f(new base::File(
811 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
812 if (!f->IsValid()) {
813 *result = NULL;
814 RecordOSError(kNewLogger, f->error_details());
815 return MakeIOError(fname, "Unable to create log file", kNewLogger,
816 f->error_details());
817 } else {
818 *result = new leveldb::ChromiumLogger(f.release());
819 return Status::OK();
823 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
824 leveldb::SequentialFile** result) {
825 FilePath path = FilePath::FromUTF8Unsafe(fname);
826 scoped_ptr<base::File> f(
827 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
828 if (!f->IsValid()) {
829 *result = NULL;
830 RecordOSError(kNewSequentialFile, f->error_details());
831 return MakeIOError(fname, "Unable to create sequential file",
832 kNewSequentialFile, f->error_details());
833 } else {
834 *result = new ChromiumSequentialFile(fname, f.release(), this);
835 return Status::OK();
839 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
840 #if defined(OS_POSIX)
841 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
842 #elif defined(OS_WIN)
843 // Windows is only limited by available memory
844 #else
845 #error "Need to determine limit to open files for this OS"
846 #endif
849 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
850 leveldb::RandomAccessFile** result) {
851 int flags = base::File::FLAG_READ | base::File::FLAG_OPEN;
852 base::File file(FilePath::FromUTF8Unsafe(fname), flags);
853 if (file.IsValid()) {
854 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
855 RecordOpenFilesLimit("Success");
856 return Status::OK();
858 base::File::Error error_code = file.error_details();
859 if (error_code == base::File::FILE_ERROR_TOO_MANY_OPENED)
860 RecordOpenFilesLimit("TooManyOpened");
861 else
862 RecordOpenFilesLimit("OtherError");
863 *result = NULL;
864 RecordOSError(kNewRandomAccessFile, error_code);
865 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
866 error_code);
869 Status ChromiumEnv::NewWritableFile(const std::string& fname,
870 leveldb::WritableFile** result) {
871 *result = NULL;
872 FilePath path = FilePath::FromUTF8Unsafe(fname);
873 scoped_ptr<base::File> f(new base::File(
874 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
875 if (!f->IsValid()) {
876 RecordErrorAt(kNewWritableFile);
877 return MakeIOError(fname, "Unable to create writable file",
878 kNewWritableFile, f->error_details());
879 } else {
880 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
881 return Status::OK();
885 Status ChromiumEnv::NewAppendableFile(const std::string& fname,
886 leveldb::WritableFile** result) {
887 *result = NULL;
888 FilePath path = FilePath::FromUTF8Unsafe(fname);
889 scoped_ptr<base::File> f(new base::File(
890 path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND));
891 if (!f->IsValid()) {
892 RecordErrorAt(kNewAppendableFile);
893 return MakeIOError(fname, "Unable to create appendable file",
894 kNewAppendableFile, f->error_details());
896 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
897 return Status::OK();
900 uint64_t ChromiumEnv::NowMicros() {
901 return base::TimeTicks::Now().ToInternalValue();
904 void ChromiumEnv::SleepForMicroseconds(int micros) {
905 // Round up to the next millisecond.
906 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
909 void ChromiumEnv::RecordErrorAt(MethodID method) const {
910 GetMethodIOErrorHistogram()->Add(method);
913 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
914 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
917 void ChromiumEnv::RecordOSError(MethodID method,
918 base::File::Error error) const {
919 DCHECK_LT(error, 0);
920 RecordErrorAt(method);
921 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
924 void ChromiumEnv::RecordBackupResult(bool result) const {
925 std::string uma_name(name_);
926 uma_name.append(".TableBackup");
927 base::BooleanHistogram::FactoryGet(
928 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
931 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
932 int limit) const {
933 std::string uma_name;
934 base::StringAppendF(&uma_name, "%s.%s", uma_ioerror_base_name_.c_str(),
935 MethodIDToString(method));
936 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
937 base::Histogram::kUmaTargetedHistogramFlag);
940 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
941 std::string uma_name(name_);
942 uma_name.append(".IOError");
943 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
944 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
947 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
948 const std::string& type) const {
949 std::string uma_name(name_);
950 uma_name.append(".MaxFDs.").append(type);
951 // These numbers make each bucket twice as large as the previous bucket.
952 const int kFirstEntry = 1;
953 const int kLastEntry = 65536;
954 const int kNumBuckets = 18;
955 return base::Histogram::FactoryGet(
956 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
957 base::Histogram::kUmaTargetedHistogramFlag);
960 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
961 std::string uma_name(name_);
962 uma_name.append(".LockFileAncestorsNotFound");
963 const int kMin = 1;
964 const int kMax = 10;
965 const int kNumBuckets = 11;
966 return base::LinearHistogram::FactoryGet(
967 uma_name, kMin, kMax, kNumBuckets,
968 base::Histogram::kUmaTargetedHistogramFlag);
971 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
972 std::string uma_name(name_);
973 // TODO(dgrogan): This is probably not the best way to concatenate strings.
974 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
976 const int kBucketSizeMillis = 25;
977 // Add 2, 1 for each of the buckets <1 and >max.
978 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
979 return base::Histogram::FactoryTimeGet(
980 uma_name, base::TimeDelta::FromMilliseconds(1),
981 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
982 kNumBuckets,
983 base::Histogram::kUmaTargetedHistogramFlag);
986 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
987 MethodID method) const {
988 std::string uma_name(name_);
989 uma_name.append(".RetryRecoveredFromErrorIn")
990 .append(MethodIDToString(method));
991 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
992 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
995 class Thread : public base::PlatformThread::Delegate {
996 public:
997 Thread(void (*function)(void* arg), void* arg)
998 : function_(function), arg_(arg) {
999 base::PlatformThreadHandle handle;
1000 bool success = base::PlatformThread::Create(0, this, &handle);
1001 DCHECK(success);
1003 virtual ~Thread() {}
1004 void ThreadMain() override {
1005 (*function_)(arg_);
1006 delete this;
1009 private:
1010 void (*function_)(void* arg);
1011 void* arg_;
1014 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1015 mu_.Acquire();
1017 // Start background thread if necessary
1018 if (!started_bgthread_) {
1019 started_bgthread_ = true;
1020 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1023 // If the queue is currently empty, the background thread may currently be
1024 // waiting.
1025 if (queue_.empty()) {
1026 bgsignal_.Signal();
1029 // Add to priority queue
1030 queue_.push_back(BGItem());
1031 queue_.back().function = function;
1032 queue_.back().arg = arg;
1034 mu_.Release();
1037 void ChromiumEnv::BGThread() {
1038 base::PlatformThread::SetName(name_.c_str());
1040 while (true) {
1041 // Wait until there is an item that is ready to run
1042 mu_.Acquire();
1043 while (queue_.empty()) {
1044 bgsignal_.Wait();
1047 void (*function)(void*) = queue_.front().function;
1048 void* arg = queue_.front().arg;
1049 queue_.pop_front();
1051 mu_.Release();
1052 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1053 (*function)(arg);
1057 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1058 new Thread(function, arg); // Will self-delete.
1061 LevelDBStatusValue GetLevelDBStatusUMAValue(const leveldb::Status& s) {
1062 if (s.ok())
1063 return LEVELDB_STATUS_OK;
1064 if (s.IsNotFound())
1065 return LEVELDB_STATUS_NOT_FOUND;
1066 if (s.IsCorruption())
1067 return LEVELDB_STATUS_CORRUPTION;
1068 if (s.IsNotSupportedError())
1069 return LEVELDB_STATUS_NOT_SUPPORTED;
1070 if (s.IsIOError())
1071 return LEVELDB_STATUS_IO_ERROR;
1072 // TODO(cmumford): IsInvalidArgument() was just added to leveldb. Use this
1073 // function once that change goes to the public repository.
1074 return LEVELDB_STATUS_INVALID_ARGUMENT;
1077 } // namespace leveldb_env
1079 namespace leveldb {
1081 Env* Env::Default() {
1082 return leveldb_env::default_env.Pointer();
1085 } // namespace leveldb