Add ICU message format support
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob6f2f84410a51fb612b7f524db0b9ce15862c896e
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 base::LazyInstance<ChromiumEnv>::Leaky default_env = LAZY_INSTANCE_INITIALIZER;
323 } // unnamed namespace
325 const char* MethodIDToString(MethodID method) {
326 switch (method) {
327 case kSequentialFileRead:
328 return "SequentialFileRead";
329 case kSequentialFileSkip:
330 return "SequentialFileSkip";
331 case kRandomAccessFileRead:
332 return "RandomAccessFileRead";
333 case kWritableFileAppend:
334 return "WritableFileAppend";
335 case kWritableFileClose:
336 return "WritableFileClose";
337 case kWritableFileFlush:
338 return "WritableFileFlush";
339 case kWritableFileSync:
340 return "WritableFileSync";
341 case kNewSequentialFile:
342 return "NewSequentialFile";
343 case kNewRandomAccessFile:
344 return "NewRandomAccessFile";
345 case kNewWritableFile:
346 return "NewWritableFile";
347 case kNewAppendableFile:
348 return "NewAppendableFile";
349 case kDeleteFile:
350 return "DeleteFile";
351 case kCreateDir:
352 return "CreateDir";
353 case kDeleteDir:
354 return "DeleteDir";
355 case kGetFileSize:
356 return "GetFileSize";
357 case kRenameFile:
358 return "RenameFile";
359 case kLockFile:
360 return "LockFile";
361 case kUnlockFile:
362 return "UnlockFile";
363 case kGetTestDirectory:
364 return "GetTestDirectory";
365 case kNewLogger:
366 return "NewLogger";
367 case kSyncParent:
368 return "SyncParent";
369 case kGetChildren:
370 return "GetChildren";
371 case kNumEntries:
372 NOTREACHED();
373 return "kNumEntries";
375 NOTREACHED();
376 return "Unknown";
379 Status MakeIOError(Slice filename,
380 const std::string& message,
381 MethodID method,
382 base::File::Error error) {
383 DCHECK_LT(error, 0);
384 char buf[512];
385 snprintf(buf, sizeof(buf), "%s (ChromeMethodBFE: %d::%s::%d)",
386 message.c_str(), method, MethodIDToString(method), -error);
387 return Status::IOError(filename, buf);
390 Status MakeIOError(Slice filename,
391 const std::string& message,
392 MethodID method) {
393 char buf[512];
394 snprintf(buf, sizeof(buf), "%s (ChromeMethodOnly: %d::%s)", message.c_str(),
395 method, MethodIDToString(method));
396 return Status::IOError(filename, buf);
399 ErrorParsingResult ParseMethodAndError(const leveldb::Status& status,
400 MethodID* method_param,
401 base::File::Error* error) {
402 const std::string status_string = status.ToString();
403 int method;
404 if (RE2::PartialMatch(status_string.c_str(), "ChromeMethodOnly: (\\d+)",
405 &method)) {
406 *method_param = static_cast<MethodID>(method);
407 return METHOD_ONLY;
409 int parsed_error;
410 if (RE2::PartialMatch(status_string.c_str(),
411 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method,
412 &parsed_error)) {
413 *method_param = static_cast<MethodID>(method);
414 *error = static_cast<base::File::Error>(-parsed_error);
415 DCHECK_LT(*error, base::File::FILE_OK);
416 DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
417 return METHOD_AND_BFE;
419 return NONE;
422 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
423 // change the order because indices into this array have been recorded in uma
424 // histograms.
425 const char* patterns[] = {
426 "missing files",
427 "log record too small",
428 "corrupted internal key",
429 "partial record",
430 "missing start of fragmented record",
431 "error in middle of record",
432 "unknown record type",
433 "truncated record at end",
434 "bad record length",
435 "VersionEdit",
436 "FileReader invoked with unexpected value",
437 "corrupted key",
438 "CURRENT file does not end with newline",
439 "no meta-nextfile entry",
440 "no meta-lognumber entry",
441 "no last-sequence-number entry",
442 "malformed WriteBatch",
443 "bad WriteBatch Put",
444 "bad WriteBatch Delete",
445 "unknown WriteBatch tag",
446 "WriteBatch has wrong count",
447 "bad entry in block",
448 "bad block contents",
449 "bad block handle",
450 "truncated block read",
451 "block checksum mismatch",
452 "checksum mismatch",
453 "corrupted compressed block contents",
454 "bad block type",
455 "bad magic number",
456 "file is too short",
459 // Returns 1-based index into the above array or 0 if nothing matches.
460 int GetCorruptionCode(const leveldb::Status& status) {
461 DCHECK(!status.IsIOError());
462 DCHECK(!status.ok());
463 const int kOtherError = 0;
464 int error = kOtherError;
465 const std::string& str_error = status.ToString();
466 const size_t kNumPatterns = arraysize(patterns);
467 for (size_t i = 0; i < kNumPatterns; ++i) {
468 if (str_error.find(patterns[i]) != std::string::npos) {
469 error = i + 1;
470 break;
473 return error;
476 int GetNumCorruptionCodes() {
477 // + 1 for the "other" error that is returned when a corruption message
478 // doesn't match any of the patterns.
479 return arraysize(patterns) + 1;
482 std::string GetCorruptionMessage(const leveldb::Status& status) {
483 int code = GetCorruptionCode(status);
484 if (code == 0)
485 return "Unknown corruption";
486 return patterns[code - 1];
489 bool IndicatesDiskFull(const leveldb::Status& status) {
490 if (status.ok())
491 return false;
492 leveldb_env::MethodID method;
493 base::File::Error error = base::File::FILE_OK;
494 leveldb_env::ErrorParsingResult result =
495 leveldb_env::ParseMethodAndError(status, &method, &error);
496 return (result == leveldb_env::METHOD_AND_BFE &&
497 static_cast<base::File::Error>(error) ==
498 base::File::FILE_ERROR_NO_SPACE);
501 bool ChromiumEnv::MakeBackup(const std::string& fname) {
502 FilePath original_table_name = FilePath::FromUTF8Unsafe(fname);
503 FilePath backup_table_name =
504 original_table_name.ReplaceExtension(backup_table_extension);
505 return base::CopyFile(original_table_name, backup_table_name);
508 ChromiumEnv::ChromiumEnv()
509 : ChromiumEnv("LevelDBEnv", false /* make_backup */) {}
511 ChromiumEnv::ChromiumEnv(const std::string& name, bool make_backup)
512 : kMaxRetryTimeMillis(1000),
513 name_(name),
514 make_backup_(make_backup),
515 bgsignal_(&mu_),
516 started_bgthread_(false) {
517 uma_ioerror_base_name_ = name_ + ".IOError.BFE";
520 ChromiumEnv::~ChromiumEnv() {
521 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
522 // ensure that behavior isn't accidentally changed, but there's an instance in
523 // a unit test that is deleted.
526 bool ChromiumEnv::FileExists(const std::string& fname) {
527 return base::PathExists(FilePath::FromUTF8Unsafe(fname));
530 const char* ChromiumEnv::FileErrorString(base::File::Error error) {
531 switch (error) {
532 case base::File::FILE_ERROR_FAILED:
533 return "No further details.";
534 case base::File::FILE_ERROR_IN_USE:
535 return "File currently in use.";
536 case base::File::FILE_ERROR_EXISTS:
537 return "File already exists.";
538 case base::File::FILE_ERROR_NOT_FOUND:
539 return "File not found.";
540 case base::File::FILE_ERROR_ACCESS_DENIED:
541 return "Access denied.";
542 case base::File::FILE_ERROR_TOO_MANY_OPENED:
543 return "Too many files open.";
544 case base::File::FILE_ERROR_NO_MEMORY:
545 return "Out of memory.";
546 case base::File::FILE_ERROR_NO_SPACE:
547 return "No space left on drive.";
548 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
549 return "Not a directory.";
550 case base::File::FILE_ERROR_INVALID_OPERATION:
551 return "Invalid operation.";
552 case base::File::FILE_ERROR_SECURITY:
553 return "Security error.";
554 case base::File::FILE_ERROR_ABORT:
555 return "File operation aborted.";
556 case base::File::FILE_ERROR_NOT_A_FILE:
557 return "The supplied path was not a file.";
558 case base::File::FILE_ERROR_NOT_EMPTY:
559 return "The file was not empty.";
560 case base::File::FILE_ERROR_INVALID_URL:
561 return "Invalid URL.";
562 case base::File::FILE_ERROR_IO:
563 return "OS or hardware error.";
564 case base::File::FILE_OK:
565 return "OK.";
566 case base::File::FILE_ERROR_MAX:
567 NOTREACHED();
569 NOTIMPLEMENTED();
570 return "Unknown error.";
573 FilePath ChromiumEnv::RestoreFromBackup(const FilePath& base_name) {
574 FilePath table_name = base_name.AddExtension(table_extension);
575 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
576 table_name);
577 std::string uma_name(name_);
578 uma_name.append(".TableRestore");
579 base::BooleanHistogram::FactoryGet(
580 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
581 return table_name;
584 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
585 std::vector<std::string>* dir_entries) {
586 std::set<FilePath> tables_found;
587 std::set<FilePath> backups_found;
588 for (const std::string& entry : *dir_entries) {
589 FilePath current = FilePath::FromUTF8Unsafe(entry);
590 if (current.MatchesExtension(table_extension))
591 tables_found.insert(current.RemoveExtension());
592 if (current.MatchesExtension(backup_table_extension))
593 backups_found.insert(current.RemoveExtension());
595 std::set<FilePath> backups_only =
596 base::STLSetDifference<std::set<FilePath>>(backups_found, tables_found);
598 if (backups_only.size()) {
599 std::string uma_name(name_);
600 uma_name.append(".MissingFiles");
601 int num_missing_files =
602 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
603 base::Histogram::FactoryGet(uma_name,
604 1 /*min*/,
605 100 /*max*/,
606 8 /*num_buckets*/,
607 base::Histogram::kUmaTargetedHistogramFlag)
608 ->Add(num_missing_files);
610 FilePath dir_path = FilePath::FromUTF8Unsafe(dir);
611 for (const FilePath& backup : backups_only) {
612 FilePath restored_table_name = RestoreFromBackup(dir_path.Append(backup));
613 dir_entries->push_back(restored_table_name.BaseName().AsUTF8Unsafe());
617 Status ChromiumEnv::GetChildren(const std::string& dir,
618 std::vector<std::string>* result) {
619 std::vector<FilePath> entries;
620 base::File::Error error =
621 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir), &entries);
622 if (error != base::File::FILE_OK) {
623 RecordOSError(kGetChildren, error);
624 return MakeIOError(dir, "Could not open/read directory", kGetChildren,
625 error);
628 result->clear();
629 for (const auto& entry : entries)
630 result->push_back(entry.BaseName().AsUTF8Unsafe());
632 if (make_backup_)
633 RestoreIfNecessary(dir, result);
635 return Status::OK();
638 Status ChromiumEnv::DeleteFile(const std::string& fname) {
639 Status result;
640 FilePath fname_filepath = FilePath::FromUTF8Unsafe(fname);
641 // TODO(jorlow): Should we assert this is a file?
642 if (!base::DeleteFile(fname_filepath, false)) {
643 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
644 RecordErrorAt(kDeleteFile);
646 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
647 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
648 false);
650 return result;
653 Status ChromiumEnv::CreateDir(const std::string& name) {
654 Status result;
655 base::File::Error error = base::File::FILE_OK;
656 Retrier retrier(kCreateDir, this);
657 do {
658 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name),
659 &error))
660 return result;
661 } while (retrier.ShouldKeepTrying(error));
662 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
663 RecordOSError(kCreateDir, error);
664 return result;
667 Status ChromiumEnv::DeleteDir(const std::string& name) {
668 Status result;
669 // TODO(jorlow): Should we assert this is a directory?
670 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name), false)) {
671 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
672 RecordErrorAt(kDeleteDir);
674 return result;
677 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
678 Status s;
679 int64_t signed_size;
680 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname), &signed_size)) {
681 *size = 0;
682 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
683 RecordErrorAt(kGetFileSize);
684 } else {
685 *size = static_cast<uint64_t>(signed_size);
687 return s;
690 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
691 Status result;
692 FilePath src_file_path = FilePath::FromUTF8Unsafe(src);
693 if (!base::PathExists(src_file_path))
694 return result;
695 FilePath destination = FilePath::FromUTF8Unsafe(dst);
697 Retrier retrier(kRenameFile, this);
698 base::File::Error error = base::File::FILE_OK;
699 do {
700 if (base::ReplaceFile(src_file_path, destination, &error))
701 return result;
702 } while (retrier.ShouldKeepTrying(error));
704 DCHECK(error != base::File::FILE_OK);
705 RecordOSError(kRenameFile, error);
706 char buf[100];
707 snprintf(buf,
708 sizeof(buf),
709 "Could not rename file: %s",
710 FileErrorString(error));
711 return MakeIOError(src, buf, kRenameFile, error);
714 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
715 *lock = NULL;
716 Status result;
717 int flags = base::File::FLAG_OPEN_ALWAYS |
718 base::File::FLAG_READ |
719 base::File::FLAG_WRITE;
720 base::File::Error error_code;
721 base::File file;
722 Retrier retrier(kLockFile, this);
723 do {
724 file.Initialize(FilePath::FromUTF8Unsafe(fname), flags);
725 if (!file.IsValid())
726 error_code = file.error_details();
727 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
729 if (!file.IsValid()) {
730 if (error_code == base::File::FILE_ERROR_NOT_FOUND) {
731 FilePath parent = FilePath::FromUTF8Unsafe(fname).DirName();
732 FilePath last_parent;
733 int num_missing_ancestors = 0;
734 do {
735 if (base::DirectoryExists(parent))
736 break;
737 ++num_missing_ancestors;
738 last_parent = parent;
739 parent = parent.DirName();
740 } while (parent != last_parent);
741 RecordLockFileAncestors(num_missing_ancestors);
744 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
745 error_code);
746 RecordOSError(kLockFile, error_code);
747 return result;
750 if (!locks_.Insert(fname)) {
751 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
752 return result;
755 Retrier lock_retrier = Retrier(kLockFile, this);
756 do {
757 error_code = file.Lock();
758 } while (error_code != base::File::FILE_OK &&
759 retrier.ShouldKeepTrying(error_code));
761 if (error_code != base::File::FILE_OK) {
762 locks_.Remove(fname);
763 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
764 error_code);
765 RecordOSError(kLockFile, error_code);
766 return result;
769 ChromiumFileLock* my_lock = new ChromiumFileLock;
770 my_lock->file_ = file.Pass();
771 my_lock->name_ = fname;
772 *lock = my_lock;
773 return result;
776 Status ChromiumEnv::UnlockFile(FileLock* lock) {
777 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
778 Status result;
780 base::File::Error error_code = my_lock->file_.Unlock();
781 if (error_code != base::File::FILE_OK) {
782 result =
783 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
784 RecordOSError(kUnlockFile, error_code);
786 bool removed = locks_.Remove(my_lock->name_);
787 DCHECK(removed);
788 delete my_lock;
789 return result;
792 Status ChromiumEnv::GetTestDirectory(std::string* path) {
793 mu_.Acquire();
794 if (test_directory_.empty()) {
795 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
796 &test_directory_)) {
797 mu_.Release();
798 RecordErrorAt(kGetTestDirectory);
799 return MakeIOError(
800 "Could not create temp directory.", "", kGetTestDirectory);
803 *path = test_directory_.AsUTF8Unsafe();
804 mu_.Release();
805 return Status::OK();
808 Status ChromiumEnv::NewLogger(const std::string& fname,
809 leveldb::Logger** result) {
810 FilePath path = FilePath::FromUTF8Unsafe(fname);
811 scoped_ptr<base::File> f(new base::File(
812 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
813 if (!f->IsValid()) {
814 *result = NULL;
815 RecordOSError(kNewLogger, f->error_details());
816 return MakeIOError(fname, "Unable to create log file", kNewLogger,
817 f->error_details());
818 } else {
819 *result = new leveldb::ChromiumLogger(f.release());
820 return Status::OK();
824 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
825 leveldb::SequentialFile** result) {
826 FilePath path = FilePath::FromUTF8Unsafe(fname);
827 scoped_ptr<base::File> f(
828 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
829 if (!f->IsValid()) {
830 *result = NULL;
831 RecordOSError(kNewSequentialFile, f->error_details());
832 return MakeIOError(fname, "Unable to create sequential file",
833 kNewSequentialFile, f->error_details());
834 } else {
835 *result = new ChromiumSequentialFile(fname, f.release(), this);
836 return Status::OK();
840 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
841 #if defined(OS_POSIX)
842 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
843 #elif defined(OS_WIN)
844 // Windows is only limited by available memory
845 #else
846 #error "Need to determine limit to open files for this OS"
847 #endif
850 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
851 leveldb::RandomAccessFile** result) {
852 int flags = base::File::FLAG_READ | base::File::FLAG_OPEN;
853 base::File file(FilePath::FromUTF8Unsafe(fname), flags);
854 if (file.IsValid()) {
855 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
856 RecordOpenFilesLimit("Success");
857 return Status::OK();
859 base::File::Error error_code = file.error_details();
860 if (error_code == base::File::FILE_ERROR_TOO_MANY_OPENED)
861 RecordOpenFilesLimit("TooManyOpened");
862 else
863 RecordOpenFilesLimit("OtherError");
864 *result = NULL;
865 RecordOSError(kNewRandomAccessFile, error_code);
866 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
867 error_code);
870 Status ChromiumEnv::NewWritableFile(const std::string& fname,
871 leveldb::WritableFile** result) {
872 *result = NULL;
873 FilePath path = FilePath::FromUTF8Unsafe(fname);
874 scoped_ptr<base::File> f(new base::File(
875 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
876 if (!f->IsValid()) {
877 RecordErrorAt(kNewWritableFile);
878 return MakeIOError(fname, "Unable to create writable file",
879 kNewWritableFile, f->error_details());
880 } else {
881 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
882 return Status::OK();
886 Status ChromiumEnv::NewAppendableFile(const std::string& fname,
887 leveldb::WritableFile** result) {
888 *result = NULL;
889 FilePath path = FilePath::FromUTF8Unsafe(fname);
890 scoped_ptr<base::File> f(new base::File(
891 path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND));
892 if (!f->IsValid()) {
893 RecordErrorAt(kNewAppendableFile);
894 return MakeIOError(fname, "Unable to create appendable file",
895 kNewAppendableFile, f->error_details());
897 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
898 return Status::OK();
901 uint64_t ChromiumEnv::NowMicros() {
902 return base::TimeTicks::Now().ToInternalValue();
905 void ChromiumEnv::SleepForMicroseconds(int micros) {
906 // Round up to the next millisecond.
907 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
910 void ChromiumEnv::RecordErrorAt(MethodID method) const {
911 GetMethodIOErrorHistogram()->Add(method);
914 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
915 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
918 void ChromiumEnv::RecordOSError(MethodID method,
919 base::File::Error error) const {
920 DCHECK_LT(error, 0);
921 RecordErrorAt(method);
922 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
925 void ChromiumEnv::RecordBackupResult(bool result) const {
926 std::string uma_name(name_);
927 uma_name.append(".TableBackup");
928 base::BooleanHistogram::FactoryGet(
929 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
932 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
933 int limit) const {
934 std::string uma_name;
935 base::StringAppendF(&uma_name, "%s.%s", uma_ioerror_base_name_.c_str(),
936 MethodIDToString(method));
937 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
938 base::Histogram::kUmaTargetedHistogramFlag);
941 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
942 std::string uma_name(name_);
943 uma_name.append(".IOError");
944 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
945 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
948 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
949 const std::string& type) const {
950 std::string uma_name(name_);
951 uma_name.append(".MaxFDs.").append(type);
952 // These numbers make each bucket twice as large as the previous bucket.
953 const int kFirstEntry = 1;
954 const int kLastEntry = 65536;
955 const int kNumBuckets = 18;
956 return base::Histogram::FactoryGet(
957 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
958 base::Histogram::kUmaTargetedHistogramFlag);
961 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
962 std::string uma_name(name_);
963 uma_name.append(".LockFileAncestorsNotFound");
964 const int kMin = 1;
965 const int kMax = 10;
966 const int kNumBuckets = 11;
967 return base::LinearHistogram::FactoryGet(
968 uma_name, kMin, kMax, kNumBuckets,
969 base::Histogram::kUmaTargetedHistogramFlag);
972 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
973 std::string uma_name(name_);
974 // TODO(dgrogan): This is probably not the best way to concatenate strings.
975 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
977 const int kBucketSizeMillis = 25;
978 // Add 2, 1 for each of the buckets <1 and >max.
979 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
980 return base::Histogram::FactoryTimeGet(
981 uma_name, base::TimeDelta::FromMilliseconds(1),
982 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
983 kNumBuckets,
984 base::Histogram::kUmaTargetedHistogramFlag);
987 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
988 MethodID method) const {
989 std::string uma_name(name_);
990 uma_name.append(".RetryRecoveredFromErrorIn")
991 .append(MethodIDToString(method));
992 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
993 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
996 class Thread : public base::PlatformThread::Delegate {
997 public:
998 Thread(void (*function)(void* arg), void* arg)
999 : function_(function), arg_(arg) {
1000 base::PlatformThreadHandle handle;
1001 bool success = base::PlatformThread::Create(0, this, &handle);
1002 DCHECK(success);
1004 virtual ~Thread() {}
1005 void ThreadMain() override {
1006 (*function_)(arg_);
1007 delete this;
1010 private:
1011 void (*function_)(void* arg);
1012 void* arg_;
1015 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1016 mu_.Acquire();
1018 // Start background thread if necessary
1019 if (!started_bgthread_) {
1020 started_bgthread_ = true;
1021 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1024 // If the queue is currently empty, the background thread may currently be
1025 // waiting.
1026 if (queue_.empty()) {
1027 bgsignal_.Signal();
1030 // Add to priority queue
1031 queue_.push_back(BGItem());
1032 queue_.back().function = function;
1033 queue_.back().arg = arg;
1035 mu_.Release();
1038 void ChromiumEnv::BGThread() {
1039 base::PlatformThread::SetName(name_.c_str());
1041 while (true) {
1042 // Wait until there is an item that is ready to run
1043 mu_.Acquire();
1044 while (queue_.empty()) {
1045 bgsignal_.Wait();
1048 void (*function)(void*) = queue_.front().function;
1049 void* arg = queue_.front().arg;
1050 queue_.pop_front();
1052 mu_.Release();
1053 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1054 (*function)(arg);
1058 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1059 new Thread(function, arg); // Will self-delete.
1062 LevelDBStatusValue GetLevelDBStatusUMAValue(const leveldb::Status& s) {
1063 if (s.ok())
1064 return LEVELDB_STATUS_OK;
1065 if (s.IsNotFound())
1066 return LEVELDB_STATUS_NOT_FOUND;
1067 if (s.IsCorruption())
1068 return LEVELDB_STATUS_CORRUPTION;
1069 if (s.IsNotSupportedError())
1070 return LEVELDB_STATUS_NOT_SUPPORTED;
1071 if (s.IsIOError())
1072 return LEVELDB_STATUS_IO_ERROR;
1073 // TODO(cmumford): IsInvalidArgument() was just added to leveldb. Use this
1074 // function once that change goes to the public repository.
1075 return LEVELDB_STATUS_INVALID_ARGUMENT;
1078 } // namespace leveldb_env
1080 namespace leveldb {
1082 Env* Env::Default() {
1083 return leveldb_env::default_env.Pointer();
1086 } // namespace leveldb