Update .DEPS.git
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob3857be19499671e5a105f642608355bb79aa9be0
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 <errno.h>
6 #include <stdio.h>
7 #include <string.h>
9 #include <deque>
11 #include "base/at_exit.h"
12 #include "base/debug/trace_event.h"
13 #include "base/file_util.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/files/file_path.h"
16 #include "base/lazy_instance.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/metrics/histogram.h"
20 #include "base/platform_file.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/synchronization/lock.h"
24 #include "base/sys_info.h"
25 #include "base/threading/platform_thread.h"
26 #include "base/threading/thread.h"
27 #include "chromium_logger.h"
28 #include "env_chromium.h"
29 #include "leveldb/env.h"
30 #include "leveldb/slice.h"
31 #include "port/port.h"
32 #include "third_party/re2/re2/re2.h"
33 #include "util/logging.h"
35 #if defined(OS_WIN)
36 #include <io.h>
37 #include "base/win/win_util.h"
38 #endif
40 #if defined(OS_POSIX)
41 #include <dirent.h>
42 #include <fcntl.h>
43 #include <sys/resource.h>
44 #include <sys/time.h>
45 #endif
47 using namespace leveldb;
49 namespace leveldb_env {
51 namespace {
53 const base::FilePath::CharType backup_table_extension[] =
54 FILE_PATH_LITERAL(".bak");
55 const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
57 #if (defined(OS_POSIX) && !defined(OS_LINUX)) || defined(OS_WIN)
58 // The following are glibc-specific
60 size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *file) {
61 return fread(ptr, size, n, file);
64 size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *file) {
65 return fwrite(ptr, size, n, file);
68 int fflush_unlocked(FILE *file) {
69 return fflush(file);
72 #if !defined(OS_ANDROID)
73 int fdatasync(int fildes) {
74 #if defined(OS_WIN)
75 return _commit(fildes);
76 #else
77 return HANDLE_EINTR(fsync(fildes));
78 #endif
80 #endif
82 #endif
84 // Wide-char safe fopen wrapper.
85 FILE* fopen_internal(const char* fname, const char* mode) {
86 #if defined(OS_WIN)
87 return _wfopen(base::UTF8ToUTF16(fname).c_str(),
88 base::ASCIIToUTF16(mode).c_str());
89 #else
90 return fopen(fname, mode);
91 #endif
94 base::FilePath CreateFilePath(const std::string& file_path) {
95 #if defined(OS_WIN)
96 return base::FilePath(base::UTF8ToUTF16(file_path));
97 #else
98 return base::FilePath(file_path);
99 #endif
102 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
103 = FILE_PATH_LITERAL("leveldb-test-");
105 const char* PlatformFileErrorString(const ::base::PlatformFileError& error) {
106 switch (error) {
107 case ::base::PLATFORM_FILE_ERROR_FAILED:
108 return "No further details.";
109 case ::base::PLATFORM_FILE_ERROR_IN_USE:
110 return "File currently in use.";
111 case ::base::PLATFORM_FILE_ERROR_EXISTS:
112 return "File already exists.";
113 case ::base::PLATFORM_FILE_ERROR_NOT_FOUND:
114 return "File not found.";
115 case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
116 return "Access denied.";
117 case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED:
118 return "Too many files open.";
119 case ::base::PLATFORM_FILE_ERROR_NO_MEMORY:
120 return "Out of memory.";
121 case ::base::PLATFORM_FILE_ERROR_NO_SPACE:
122 return "No space left on drive.";
123 case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
124 return "Not a directory.";
125 case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
126 return "Invalid operation.";
127 case ::base::PLATFORM_FILE_ERROR_SECURITY:
128 return "Security error.";
129 case ::base::PLATFORM_FILE_ERROR_ABORT:
130 return "File operation aborted.";
131 case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE:
132 return "The supplied path was not a file.";
133 case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY:
134 return "The file was not empty.";
135 case ::base::PLATFORM_FILE_ERROR_INVALID_URL:
136 return "Invalid URL.";
137 case ::base::PLATFORM_FILE_ERROR_IO:
138 return "OS or hardware error.";
139 case ::base::PLATFORM_FILE_OK:
140 return "OK.";
141 case ::base::PLATFORM_FILE_ERROR_MAX:
142 NOTREACHED();
144 NOTIMPLEMENTED();
145 return "Unknown error.";
148 class ChromiumSequentialFile: public SequentialFile {
149 private:
150 std::string filename_;
151 FILE* file_;
152 const UMALogger* uma_logger_;
154 public:
155 ChromiumSequentialFile(const std::string& fname, FILE* f,
156 const UMALogger* uma_logger)
157 : filename_(fname), file_(f), uma_logger_(uma_logger) { }
158 virtual ~ChromiumSequentialFile() { fclose(file_); }
160 virtual Status Read(size_t n, Slice* result, char* scratch) {
161 Status s;
162 size_t r = fread_unlocked(scratch, 1, n, file_);
163 *result = Slice(scratch, r);
164 if (r < n) {
165 if (feof(file_)) {
166 // We leave status as ok if we hit the end of the file
167 } else {
168 // A partial read with an error: return a non-ok status
169 s = MakeIOError(filename_, strerror(errno), kSequentialFileRead, errno);
170 uma_logger_->RecordErrorAt(kSequentialFileRead);
173 return s;
176 virtual Status Skip(uint64_t n) {
177 if (fseek(file_, n, SEEK_CUR)) {
178 int saved_errno = errno;
179 uma_logger_->RecordErrorAt(kSequentialFileSkip);
180 return MakeIOError(
181 filename_, strerror(saved_errno), kSequentialFileSkip, saved_errno);
183 return Status::OK();
187 class ChromiumRandomAccessFile: public RandomAccessFile {
188 private:
189 std::string filename_;
190 ::base::PlatformFile file_;
191 const UMALogger* uma_logger_;
193 public:
194 ChromiumRandomAccessFile(const std::string& fname, ::base::PlatformFile file,
195 const UMALogger* uma_logger)
196 : filename_(fname), file_(file), uma_logger_(uma_logger) { }
197 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_); }
199 virtual Status Read(uint64_t offset, size_t n, Slice* result,
200 char* scratch) const {
201 Status s;
202 int r = ::base::ReadPlatformFile(file_, offset, scratch, n);
203 *result = Slice(scratch, (r < 0) ? 0 : r);
204 if (r < 0) {
205 // An error: return a non-ok status
206 s = MakeIOError(
207 filename_, "Could not perform read", kRandomAccessFileRead);
208 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
210 return s;
214 class ChromiumFileLock : public FileLock {
215 public:
216 ::base::PlatformFile file_;
217 std::string name_;
220 class Retrier {
221 public:
222 Retrier(MethodID method, RetrierProvider* provider)
223 : start_(base::TimeTicks::Now()),
224 limit_(start_ + base::TimeDelta::FromMilliseconds(
225 provider->MaxRetryTimeMillis())),
226 last_(start_),
227 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
228 success_(true),
229 method_(method),
230 last_error_(base::PLATFORM_FILE_OK),
231 provider_(provider) {}
232 ~Retrier() {
233 if (success_) {
234 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
235 if (last_error_ != base::PLATFORM_FILE_OK) {
236 DCHECK(last_error_ < 0);
237 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
241 bool ShouldKeepTrying(base::PlatformFileError last_error) {
242 DCHECK_NE(last_error, base::PLATFORM_FILE_OK);
243 last_error_ = last_error;
244 if (last_ < limit_) {
245 base::PlatformThread::Sleep(time_to_sleep_);
246 last_ = base::TimeTicks::Now();
247 return true;
249 success_ = false;
250 return false;
253 private:
254 base::TimeTicks start_;
255 base::TimeTicks limit_;
256 base::TimeTicks last_;
257 base::TimeDelta time_to_sleep_;
258 bool success_;
259 MethodID method_;
260 base::PlatformFileError last_error_;
261 RetrierProvider* provider_;
264 class IDBEnv : public ChromiumEnv {
265 public:
266 IDBEnv() : ChromiumEnv() {
267 name_ = "LevelDBEnv.IDB";
268 make_backup_ = true;
272 ::base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
274 ::base::LazyInstance<ChromiumEnv>::Leaky default_env =
275 LAZY_INSTANCE_INITIALIZER;
277 } // unnamed namespace
279 const char* MethodIDToString(MethodID method) {
280 switch (method) {
281 case kSequentialFileRead:
282 return "SequentialFileRead";
283 case kSequentialFileSkip:
284 return "SequentialFileSkip";
285 case kRandomAccessFileRead:
286 return "RandomAccessFileRead";
287 case kWritableFileAppend:
288 return "WritableFileAppend";
289 case kWritableFileClose:
290 return "WritableFileClose";
291 case kWritableFileFlush:
292 return "WritableFileFlush";
293 case kWritableFileSync:
294 return "WritableFileSync";
295 case kNewSequentialFile:
296 return "NewSequentialFile";
297 case kNewRandomAccessFile:
298 return "NewRandomAccessFile";
299 case kNewWritableFile:
300 return "NewWritableFile";
301 case kDeleteFile:
302 return "DeleteFile";
303 case kCreateDir:
304 return "CreateDir";
305 case kDeleteDir:
306 return "DeleteDir";
307 case kGetFileSize:
308 return "GetFileSize";
309 case kRenameFile:
310 return "RenameFile";
311 case kLockFile:
312 return "LockFile";
313 case kUnlockFile:
314 return "UnlockFile";
315 case kGetTestDirectory:
316 return "GetTestDirectory";
317 case kNewLogger:
318 return "NewLogger";
319 case kSyncParent:
320 return "SyncParent";
321 case kGetChildren:
322 return "GetChildren";
323 case kNumEntries:
324 NOTREACHED();
325 return "kNumEntries";
327 NOTREACHED();
328 return "Unknown";
331 Status MakeIOError(Slice filename,
332 const char* message,
333 MethodID method,
334 int saved_errno) {
335 char buf[512];
336 snprintf(buf,
337 sizeof(buf),
338 "%s (ChromeMethodErrno: %d::%s::%d)",
339 message,
340 method,
341 MethodIDToString(method),
342 saved_errno);
343 return Status::IOError(filename, buf);
346 Status MakeIOError(Slice filename,
347 const char* message,
348 MethodID method,
349 base::PlatformFileError error) {
350 DCHECK(error < 0);
351 char buf[512];
352 snprintf(buf,
353 sizeof(buf),
354 "%s (ChromeMethodPFE: %d::%s::%d)",
355 message,
356 method,
357 MethodIDToString(method),
358 -error);
359 return Status::IOError(filename, buf);
362 Status MakeIOError(Slice filename, const char* message, MethodID method) {
363 char buf[512];
364 snprintf(buf,
365 sizeof(buf),
366 "%s (ChromeMethodOnly: %d::%s)",
367 message,
368 method,
369 MethodIDToString(method));
370 return Status::IOError(filename, buf);
373 ErrorParsingResult ParseMethodAndError(const char* string,
374 MethodID* method_param,
375 int* error) {
376 int method;
377 if (RE2::PartialMatch(string, "ChromeMethodOnly: (\\d+)", &method)) {
378 *method_param = static_cast<MethodID>(method);
379 return METHOD_ONLY;
381 if (RE2::PartialMatch(
382 string, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method, error)) {
383 *error = -*error;
384 *method_param = static_cast<MethodID>(method);
385 return METHOD_AND_PFE;
387 if (RE2::PartialMatch(
388 string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) {
389 *method_param = static_cast<MethodID>(method);
390 return METHOD_AND_ERRNO;
392 return NONE;
395 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
396 // change the order because indices into this array have been recorded in uma
397 // histograms.
398 const char* patterns[] = {
399 "missing files",
400 "log record too small",
401 "corrupted internal key",
402 "partial record",
403 "missing start of fragmented record",
404 "error in middle of record",
405 "unknown record type",
406 "truncated record at end",
407 "bad record length",
408 "VersionEdit",
409 "FileReader invoked with unexpected value",
410 "corrupted key",
411 "CURRENT file does not end with newline",
412 "no meta-nextfile entry",
413 "no meta-lognumber entry",
414 "no last-sequence-number entry",
415 "malformed WriteBatch",
416 "bad WriteBatch Put",
417 "bad WriteBatch Delete",
418 "unknown WriteBatch tag",
419 "WriteBatch has wrong count",
420 "bad entry in block",
421 "bad block contents",
422 "bad block handle",
423 "truncated block read",
424 "block checksum mismatch",
425 "checksum mismatch",
426 "corrupted compressed block contents",
427 "bad block type",
428 "bad magic number",
429 "file is too short",
432 // Returns 1-based index into the above array or 0 if nothing matches.
433 int GetCorruptionCode(const leveldb::Status& status) {
434 DCHECK(!status.IsIOError());
435 DCHECK(!status.ok());
436 const int kOtherError = 0;
437 int error = kOtherError;
438 const std::string& str_error = status.ToString();
439 const size_t kNumPatterns = arraysize(patterns);
440 for (size_t i = 0; i < kNumPatterns; ++i) {
441 if (str_error.find(patterns[i]) != std::string::npos) {
442 error = i + 1;
443 break;
446 return error;
449 int GetNumCorruptionCodes() {
450 // + 1 for the "other" error that is returned when a corruption message
451 // doesn't match any of the patterns.
452 return arraysize(patterns) + 1;
455 std::string GetCorruptionMessage(const leveldb::Status& status) {
456 int code = GetCorruptionCode(status);
457 if (code == 0)
458 return "Unknown corruption";
459 return patterns[code - 1];
462 bool IndicatesDiskFull(const leveldb::Status& status) {
463 if (status.ok())
464 return false;
465 leveldb_env::MethodID method;
466 int error = -1;
467 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
468 status.ToString().c_str(), &method, &error);
469 return (result == leveldb_env::METHOD_AND_PFE &&
470 static_cast<base::PlatformFileError>(error) ==
471 base::PLATFORM_FILE_ERROR_NO_SPACE) ||
472 (result == leveldb_env::METHOD_AND_ERRNO && error == ENOSPC);
475 bool IsIOError(const leveldb::Status& status) {
476 leveldb_env::MethodID method;
477 int error = -1;
478 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
479 status.ToString().c_str(), &method, &error);
480 return result != leveldb_env::NONE;
483 bool IsCorruption(const leveldb::Status& status) {
484 // LevelDB returns InvalidArgument when an sst file is truncated but there is
485 // no IsInvalidArgument() accessor defined.
486 return status.IsCorruption() || (!status.ok() && !IsIOError(status));
489 std::string FilePathToString(const base::FilePath& file_path) {
490 #if defined(OS_WIN)
491 return base::UTF16ToUTF8(file_path.value());
492 #else
493 return file_path.value();
494 #endif
497 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
498 FILE* f,
499 const UMALogger* uma_logger,
500 WriteTracker* tracker,
501 bool make_backup)
502 : filename_(fname),
503 file_(f),
504 uma_logger_(uma_logger),
505 tracker_(tracker),
506 file_type_(kOther),
507 make_backup_(make_backup) {
508 base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
509 if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
510 file_type_ = kManifest;
511 else if (path.MatchesExtension(table_extension))
512 file_type_ = kTable;
513 if (file_type_ != kManifest)
514 tracker_->DidCreateNewFile(filename_);
515 parent_dir_ = FilePathToString(CreateFilePath(fname).DirName());
518 ChromiumWritableFile::~ChromiumWritableFile() {
519 if (file_ != NULL) {
520 // Ignoring any potential errors
521 fclose(file_);
525 Status ChromiumWritableFile::SyncParent() {
526 Status s;
527 #if !defined(OS_WIN)
528 TRACE_EVENT0("leveldb", "SyncParent");
530 int parent_fd =
531 HANDLE_EINTR(open(parent_dir_.c_str(), O_RDONLY));
532 if (parent_fd < 0) {
533 int saved_errno = errno;
534 return MakeIOError(
535 parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
537 if (HANDLE_EINTR(fsync(parent_fd)) != 0) {
538 int saved_errno = errno;
539 s = MakeIOError(
540 parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
542 close(parent_fd);
543 #endif
544 return s;
547 Status ChromiumWritableFile::Append(const Slice& data) {
548 if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
549 Status s = SyncParent();
550 if (!s.ok())
551 return s;
552 tracker_->DidSyncDir(filename_);
555 size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);
556 if (r != data.size()) {
557 int saved_errno = errno;
558 uma_logger_->RecordOSError(kWritableFileAppend, saved_errno);
559 return MakeIOError(
560 filename_, strerror(saved_errno), kWritableFileAppend, saved_errno);
562 return Status::OK();
565 Status ChromiumWritableFile::Close() {
566 Status result;
567 if (fclose(file_) != 0) {
568 result = MakeIOError(filename_, strerror(errno), kWritableFileClose, errno);
569 uma_logger_->RecordErrorAt(kWritableFileClose);
571 file_ = NULL;
572 return result;
575 Status ChromiumWritableFile::Flush() {
576 Status result;
577 if (HANDLE_EINTR(fflush_unlocked(file_))) {
578 int saved_errno = errno;
579 result = MakeIOError(
580 filename_, strerror(saved_errno), kWritableFileFlush, saved_errno);
581 uma_logger_->RecordOSError(kWritableFileFlush, saved_errno);
583 return result;
586 static bool MakeBackup(const std::string& fname) {
587 base::FilePath original_table_name = CreateFilePath(fname);
588 base::FilePath backup_table_name =
589 original_table_name.ReplaceExtension(backup_table_extension);
590 return base::CopyFile(original_table_name, backup_table_name);
593 Status ChromiumWritableFile::Sync() {
594 TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
595 Status result;
596 int error = 0;
598 if (HANDLE_EINTR(fflush_unlocked(file_)))
599 error = errno;
600 // Sync even if fflush gave an error; perhaps the data actually got out,
601 // even though something went wrong.
602 if (fdatasync(fileno(file_)) && !error)
603 error = errno;
604 // Report the first error we found.
605 if (error) {
606 result = MakeIOError(filename_, strerror(error), kWritableFileSync, error);
607 uma_logger_->RecordErrorAt(kWritableFileSync);
608 } else if (make_backup_ && file_type_ == kTable) {
609 bool success = MakeBackup(filename_);
610 uma_logger_->RecordBackupResult(success);
612 return result;
615 ChromiumEnv::ChromiumEnv()
616 : name_("LevelDBEnv"),
617 make_backup_(false),
618 bgsignal_(&mu_),
619 started_bgthread_(false),
620 kMaxRetryTimeMillis(1000) {
623 ChromiumEnv::~ChromiumEnv() {
624 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
625 // ensure that behavior isn't accidentally changed, but there's an instance in
626 // a unit test that is deleted.
629 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
630 SequentialFile** result) {
631 FILE* f = fopen_internal(fname.c_str(), "rb");
632 if (f == NULL) {
633 *result = NULL;
634 int saved_errno = errno;
635 RecordOSError(kNewSequentialFile, saved_errno);
636 return MakeIOError(
637 fname, strerror(saved_errno), kNewSequentialFile, saved_errno);
638 } else {
639 *result = new ChromiumSequentialFile(fname, f, this);
640 return Status::OK();
644 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
645 #if defined(OS_POSIX)
646 struct rlimit nofile;
647 if (getrlimit(RLIMIT_NOFILE, &nofile))
648 return;
649 GetMaxFDHistogram(type)->Add(nofile.rlim_cur);
650 #endif
653 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
654 RandomAccessFile** result) {
655 int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
656 bool created;
657 ::base::PlatformFileError error_code;
658 ::base::PlatformFile file = ::base::CreatePlatformFile(
659 CreateFilePath(fname), flags, &created, &error_code);
660 if (error_code == ::base::PLATFORM_FILE_OK) {
661 *result = new ChromiumRandomAccessFile(fname, file, this);
662 RecordOpenFilesLimit("Success");
663 return Status::OK();
665 if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED)
666 RecordOpenFilesLimit("TooManyOpened");
667 else
668 RecordOpenFilesLimit("OtherError");
669 *result = NULL;
670 RecordOSError(kNewRandomAccessFile, error_code);
671 return MakeIOError(fname,
672 PlatformFileErrorString(error_code),
673 kNewRandomAccessFile,
674 error_code);
677 Status ChromiumEnv::NewWritableFile(const std::string& fname,
678 WritableFile** result) {
679 *result = NULL;
680 FILE* f = fopen_internal(fname.c_str(), "wb");
681 if (f == NULL) {
682 int saved_errno = errno;
683 RecordErrorAt(kNewWritableFile);
684 return MakeIOError(
685 fname, strerror(saved_errno), kNewWritableFile, saved_errno);
686 } else {
687 *result = new ChromiumWritableFile(fname, f, this, this, make_backup_);
688 return Status::OK();
692 bool ChromiumEnv::FileExists(const std::string& fname) {
693 return ::base::PathExists(CreateFilePath(fname));
696 base::FilePath ChromiumEnv::RestoreFromBackup(const base::FilePath& base_name) {
697 base::FilePath table_name =
698 base_name.AddExtension(table_extension);
699 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
700 table_name);
701 std::string uma_name(name_);
702 uma_name.append(".TableRestore");
703 base::BooleanHistogram::FactoryGet(
704 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
705 return table_name;
708 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
709 std::vector<std::string>* result) {
710 std::set<base::FilePath> tables_found;
711 std::set<base::FilePath> backups_found;
712 for (std::vector<std::string>::iterator it = result->begin();
713 it != result->end();
714 ++it) {
715 base::FilePath current = CreateFilePath(*it);
716 if (current.MatchesExtension(table_extension))
717 tables_found.insert(current.RemoveExtension());
718 if (current.MatchesExtension(backup_table_extension))
719 backups_found.insert(current.RemoveExtension());
721 std::set<base::FilePath> backups_only;
722 std::set_difference(backups_found.begin(),
723 backups_found.end(),
724 tables_found.begin(),
725 tables_found.end(),
726 std::inserter(backups_only, backups_only.begin()));
727 if (backups_only.size()) {
728 std::string uma_name(name_);
729 uma_name.append(".MissingFiles");
730 int num_missing_files =
731 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
732 base::Histogram::FactoryGet(uma_name,
733 1 /*min*/,
734 100 /*max*/,
735 8 /*num_buckets*/,
736 base::Histogram::kUmaTargetedHistogramFlag)
737 ->Add(num_missing_files);
739 base::FilePath dir_filepath = base::FilePath::FromUTF8Unsafe(dir);
740 for (std::set<base::FilePath>::iterator it = backups_only.begin();
741 it != backups_only.end();
742 ++it) {
743 base::FilePath restored_table_name =
744 RestoreFromBackup(dir_filepath.Append(*it));
745 result->push_back(FilePathToString(restored_table_name.BaseName()));
749 namespace {
750 #if defined(OS_WIN)
751 static base::PlatformFileError GetDirectoryEntries(
752 const base::FilePath& dir_param,
753 std::vector<base::FilePath>* result) {
754 result->clear();
755 base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
756 WIN32_FIND_DATA find_data;
757 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
758 if (find_handle == INVALID_HANDLE_VALUE) {
759 DWORD last_error = GetLastError();
760 if (last_error == ERROR_FILE_NOT_FOUND)
761 return base::PLATFORM_FILE_OK;
762 return base::LastErrorToPlatformFileError(last_error);
764 do {
765 base::FilePath filepath(find_data.cFileName);
766 base::FilePath::StringType basename = filepath.BaseName().value();
767 if (basename == FILE_PATH_LITERAL(".") ||
768 basename == FILE_PATH_LITERAL(".."))
769 continue;
770 result->push_back(filepath.BaseName());
771 } while (FindNextFile(find_handle, &find_data));
772 DWORD last_error = GetLastError();
773 base::PlatformFileError return_value = base::PLATFORM_FILE_OK;
774 if (last_error != ERROR_NO_MORE_FILES)
775 return_value = base::LastErrorToPlatformFileError(last_error);
776 FindClose(find_handle);
777 return return_value;
779 #else
780 static base::PlatformFileError GetDirectoryEntries(
781 const base::FilePath& dir_filepath,
782 std::vector<base::FilePath>* result) {
783 const std::string dir_string = FilePathToString(dir_filepath);
784 result->clear();
785 DIR* dir = opendir(dir_string.c_str());
786 if (!dir)
787 return base::ErrnoToPlatformFileError(errno);
788 struct dirent dent_buf;
789 struct dirent* dent;
790 int readdir_result;
791 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
792 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
793 continue;
794 result->push_back(CreateFilePath(dent->d_name));
796 int saved_errno = errno;
797 closedir(dir);
798 if (readdir_result != 0)
799 return base::ErrnoToPlatformFileError(saved_errno);
800 return base::PLATFORM_FILE_OK;
802 #endif
805 Status ChromiumEnv::GetChildren(const std::string& dir_string,
806 std::vector<std::string>* result) {
807 std::vector<base::FilePath> entries;
808 base::PlatformFileError error =
809 GetDirectoryEntries(CreateFilePath(dir_string), &entries);
810 if (error != base::PLATFORM_FILE_OK) {
811 RecordOSError(kGetChildren, error);
812 return MakeIOError(
813 dir_string, "Could not open/read directory", kGetChildren, error);
815 result->clear();
816 for (std::vector<base::FilePath>::iterator it = entries.begin();
817 it != entries.end();
818 ++it) {
819 result->push_back(FilePathToString(*it));
822 if (make_backup_)
823 RestoreIfNecessary(dir_string, result);
824 return Status::OK();
827 Status ChromiumEnv::DeleteFile(const std::string& fname) {
828 Status result;
829 base::FilePath fname_filepath = CreateFilePath(fname);
830 // TODO(jorlow): Should we assert this is a file?
831 if (!::base::DeleteFile(fname_filepath, false)) {
832 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
833 RecordErrorAt(kDeleteFile);
835 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
836 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
837 false);
839 return result;
842 Status ChromiumEnv::CreateDir(const std::string& name) {
843 Status result;
844 // TODO(rvargas): convert this code to base::File::Error.
845 base::PlatformFileError error = base::PLATFORM_FILE_OK;
846 Retrier retrier(kCreateDir, this);
847 do {
848 if (base::CreateDirectoryAndGetError(
849 CreateFilePath(name),
850 reinterpret_cast<base::File::Error*>(&error))) {
851 return result;
853 } while (retrier.ShouldKeepTrying(error));
854 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
855 RecordOSError(kCreateDir, error);
856 return result;
859 Status ChromiumEnv::DeleteDir(const std::string& name) {
860 Status result;
861 // TODO(jorlow): Should we assert this is a directory?
862 if (!::base::DeleteFile(CreateFilePath(name), false)) {
863 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
864 RecordErrorAt(kDeleteDir);
866 return result;
869 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
870 Status s;
871 int64_t signed_size;
872 if (!::base::GetFileSize(CreateFilePath(fname), &signed_size)) {
873 *size = 0;
874 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
875 RecordErrorAt(kGetFileSize);
876 } else {
877 *size = static_cast<uint64_t>(signed_size);
879 return s;
882 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
883 Status result;
884 base::FilePath src_file_path = CreateFilePath(src);
885 if (!::base::PathExists(src_file_path))
886 return result;
887 base::FilePath destination = CreateFilePath(dst);
889 Retrier retrier(kRenameFile, this);
890 // TODO(rvargas): convert this code to base::File::Error.
891 base::PlatformFileError error = base::PLATFORM_FILE_OK;
892 do {
893 if (base::ReplaceFile(src_file_path, destination,
894 reinterpret_cast<base::File::Error*>(&error))) {
895 return result;
897 } while (retrier.ShouldKeepTrying(error));
899 DCHECK(error != base::PLATFORM_FILE_OK);
900 RecordOSError(kRenameFile, error);
901 char buf[100];
902 snprintf(buf,
903 sizeof(buf),
904 "Could not rename file: %s",
905 PlatformFileErrorString(error));
906 return MakeIOError(src, buf, kRenameFile, error);
909 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
910 *lock = NULL;
911 Status result;
912 int flags = ::base::PLATFORM_FILE_OPEN_ALWAYS |
913 ::base::PLATFORM_FILE_READ |
914 ::base::PLATFORM_FILE_WRITE;
915 bool created;
916 ::base::PlatformFileError error_code;
917 ::base::PlatformFile file;
918 Retrier retrier(kLockFile, this);
919 do {
920 file = ::base::CreatePlatformFile(
921 CreateFilePath(fname), flags, &created, &error_code);
922 } while (error_code != ::base::PLATFORM_FILE_OK &&
923 retrier.ShouldKeepTrying(error_code));
925 if (error_code == ::base::PLATFORM_FILE_ERROR_NOT_FOUND) {
926 ::base::FilePath parent = CreateFilePath(fname).DirName();
927 ::base::FilePath last_parent;
928 int num_missing_ancestors = 0;
929 do {
930 if (base::DirectoryExists(parent))
931 break;
932 ++num_missing_ancestors;
933 last_parent = parent;
934 parent = parent.DirName();
935 } while (parent != last_parent);
936 RecordLockFileAncestors(num_missing_ancestors);
939 if (error_code != ::base::PLATFORM_FILE_OK) {
940 result = MakeIOError(
941 fname, PlatformFileErrorString(error_code), kLockFile, error_code);
942 RecordOSError(kLockFile, error_code);
943 return result;
946 if (!locks_.Insert(fname)) {
947 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
948 ::base::ClosePlatformFile(file);
949 return result;
952 Retrier lock_retrier = Retrier(kLockFile, this);
953 do {
954 error_code = ::base::LockPlatformFile(file);
955 } while (error_code != ::base::PLATFORM_FILE_OK &&
956 retrier.ShouldKeepTrying(error_code));
958 if (error_code != ::base::PLATFORM_FILE_OK) {
959 ::base::ClosePlatformFile(file);
960 locks_.Remove(fname);
961 result = MakeIOError(
962 fname, PlatformFileErrorString(error_code), kLockFile, error_code);
963 RecordOSError(kLockFile, error_code);
964 return result;
967 ChromiumFileLock* my_lock = new ChromiumFileLock;
968 my_lock->file_ = file;
969 my_lock->name_ = fname;
970 *lock = my_lock;
971 return result;
974 Status ChromiumEnv::UnlockFile(FileLock* lock) {
975 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
976 Status result;
978 ::base::PlatformFileError error_code =
979 ::base::UnlockPlatformFile(my_lock->file_);
980 if (error_code != ::base::PLATFORM_FILE_OK) {
981 result =
982 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
983 RecordOSError(kUnlockFile, error_code);
984 ::base::ClosePlatformFile(my_lock->file_);
985 } else if (!::base::ClosePlatformFile(my_lock->file_)) {
986 result =
987 MakeIOError(my_lock->name_, "Could not close lock file.", kUnlockFile);
988 RecordErrorAt(kUnlockFile);
990 bool removed = locks_.Remove(my_lock->name_);
991 DCHECK(removed);
992 delete my_lock;
993 return result;
996 Status ChromiumEnv::GetTestDirectory(std::string* path) {
997 mu_.Acquire();
998 if (test_directory_.empty()) {
999 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
1000 &test_directory_)) {
1001 mu_.Release();
1002 RecordErrorAt(kGetTestDirectory);
1003 return MakeIOError(
1004 "Could not create temp directory.", "", kGetTestDirectory);
1007 *path = FilePathToString(test_directory_);
1008 mu_.Release();
1009 return Status::OK();
1012 Status ChromiumEnv::NewLogger(const std::string& fname, Logger** result) {
1013 FILE* f = fopen_internal(fname.c_str(), "w");
1014 if (f == NULL) {
1015 *result = NULL;
1016 int saved_errno = errno;
1017 RecordOSError(kNewLogger, saved_errno);
1018 return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
1019 } else {
1020 *result = new ChromiumLogger(f);
1021 return Status::OK();
1025 uint64_t ChromiumEnv::NowMicros() {
1026 return ::base::TimeTicks::Now().ToInternalValue();
1029 void ChromiumEnv::SleepForMicroseconds(int micros) {
1030 // Round up to the next millisecond.
1031 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
1034 void ChromiumEnv::RecordErrorAt(MethodID method) const {
1035 GetMethodIOErrorHistogram()->Add(method);
1038 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
1039 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
1042 void ChromiumEnv::RecordOSError(MethodID method,
1043 base::PlatformFileError error) const {
1044 DCHECK(error < 0);
1045 RecordErrorAt(method);
1046 GetOSErrorHistogram(method, -base::PLATFORM_FILE_ERROR_MAX)->Add(-error);
1049 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
1050 DCHECK(error > 0);
1051 RecordErrorAt(method);
1052 GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
1055 void ChromiumEnv::RecordBackupResult(bool result) const {
1056 std::string uma_name(name_);
1057 uma_name.append(".TableBackup");
1058 base::BooleanHistogram::FactoryGet(
1059 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
1062 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
1063 int limit) const {
1064 std::string uma_name(name_);
1065 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1066 uma_name.append(".IOError.").append(MethodIDToString(method));
1067 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
1068 base::Histogram::kUmaTargetedHistogramFlag);
1071 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
1072 std::string uma_name(name_);
1073 uma_name.append(".IOError");
1074 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1075 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1078 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
1079 const std::string& type) const {
1080 std::string uma_name(name_);
1081 uma_name.append(".MaxFDs.").append(type);
1082 // These numbers make each bucket twice as large as the previous bucket.
1083 const int kFirstEntry = 1;
1084 const int kLastEntry = 65536;
1085 const int kNumBuckets = 18;
1086 return base::Histogram::FactoryGet(
1087 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
1088 base::Histogram::kUmaTargetedHistogramFlag);
1091 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
1092 std::string uma_name(name_);
1093 uma_name.append(".LockFileAncestorsNotFound");
1094 const int kMin = 1;
1095 const int kMax = 10;
1096 const int kNumBuckets = 11;
1097 return base::LinearHistogram::FactoryGet(
1098 uma_name, kMin, kMax, kNumBuckets,
1099 base::Histogram::kUmaTargetedHistogramFlag);
1102 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
1103 std::string uma_name(name_);
1104 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1105 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
1107 const int kBucketSizeMillis = 25;
1108 // Add 2, 1 for each of the buckets <1 and >max.
1109 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
1110 return base::Histogram::FactoryTimeGet(
1111 uma_name, base::TimeDelta::FromMilliseconds(1),
1112 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
1113 kNumBuckets,
1114 base::Histogram::kUmaTargetedHistogramFlag);
1117 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
1118 MethodID method) const {
1119 std::string uma_name(name_);
1120 uma_name.append(".RetryRecoveredFromErrorIn")
1121 .append(MethodIDToString(method));
1122 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
1123 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1126 class Thread : public ::base::PlatformThread::Delegate {
1127 public:
1128 Thread(void (*function)(void* arg), void* arg)
1129 : function_(function), arg_(arg) {
1130 ::base::PlatformThreadHandle handle;
1131 bool success = ::base::PlatformThread::Create(0, this, &handle);
1132 DCHECK(success);
1134 virtual ~Thread() {}
1135 virtual void ThreadMain() {
1136 (*function_)(arg_);
1137 delete this;
1140 private:
1141 void (*function_)(void* arg);
1142 void* arg_;
1145 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
1146 mu_.Acquire();
1148 // Start background thread if necessary
1149 if (!started_bgthread_) {
1150 started_bgthread_ = true;
1151 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1154 // If the queue is currently empty, the background thread may currently be
1155 // waiting.
1156 if (queue_.empty()) {
1157 bgsignal_.Signal();
1160 // Add to priority queue
1161 queue_.push_back(BGItem());
1162 queue_.back().function = function;
1163 queue_.back().arg = arg;
1165 mu_.Release();
1168 void ChromiumEnv::BGThread() {
1169 base::PlatformThread::SetName(name_.c_str());
1171 while (true) {
1172 // Wait until there is an item that is ready to run
1173 mu_.Acquire();
1174 while (queue_.empty()) {
1175 bgsignal_.Wait();
1178 void (*function)(void*) = queue_.front().function;
1179 void* arg = queue_.front().arg;
1180 queue_.pop_front();
1182 mu_.Release();
1183 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1184 (*function)(arg);
1188 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1189 new Thread(function, arg); // Will self-delete.
1192 static std::string GetDirName(const std::string& filename) {
1193 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
1194 return FilePathToString(file.DirName());
1197 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
1198 base::AutoLock auto_lock(map_lock_);
1199 needs_sync_map_[GetDirName(filename)] = true;
1202 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
1203 base::AutoLock auto_lock(map_lock_);
1204 return needs_sync_map_.find(GetDirName(filename)) != needs_sync_map_.end();
1207 void ChromiumEnv::DidSyncDir(const std::string& filename) {
1208 base::AutoLock auto_lock(map_lock_);
1209 needs_sync_map_.erase(GetDirName(filename));
1212 } // namespace leveldb_env
1214 namespace leveldb {
1216 Env* IDBEnv() {
1217 return leveldb_env::idb_env.Pointer();
1220 Env* Env::Default() {
1221 return leveldb_env::default_env.Pointer();
1224 } // namespace leveldb