HttpServer: don't crash in SetReceiveBufferSize/SetSendBufferSize if connection is...
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob732068b250bd8466eb469b46411c5cc01e143c81
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/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 result->clear();
52 #if defined(OS_WIN)
53 FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
54 WIN32_FIND_DATA find_data;
55 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
56 if (find_handle == INVALID_HANDLE_VALUE) {
57 DWORD last_error = GetLastError();
58 if (last_error == ERROR_FILE_NOT_FOUND)
59 return base::File::FILE_OK;
60 return base::File::OSErrorToFileError(last_error);
62 do {
63 FilePath filepath(find_data.cFileName);
64 FilePath::StringType basename = filepath.BaseName().value();
65 if (basename == FILE_PATH_LITERAL(".") ||
66 basename == FILE_PATH_LITERAL(".."))
67 continue;
68 result->push_back(filepath.BaseName());
69 } while (FindNextFile(find_handle, &find_data));
70 DWORD last_error = GetLastError();
71 base::File::Error return_value = base::File::FILE_OK;
72 if (last_error != ERROR_NO_MORE_FILES)
73 return_value = base::File::OSErrorToFileError(last_error);
74 FindClose(find_handle);
75 return return_value;
76 #else
77 const std::string dir_string = dir_param.AsUTF8Unsafe();
78 DIR* dir = opendir(dir_string.c_str());
79 if (!dir)
80 return base::File::OSErrorToFileError(errno);
81 struct dirent dent_buf;
82 struct dirent* dent;
83 int readdir_result;
84 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
85 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
86 continue;
87 result->push_back(FilePath::FromUTF8Unsafe(dent->d_name));
89 int saved_errno = errno;
90 closedir(dir);
91 if (readdir_result != 0)
92 return base::File::OSErrorToFileError(saved_errno);
93 return base::File::FILE_OK;
94 #endif
97 class ChromiumFileLock : public FileLock {
98 public:
99 base::File file_;
100 std::string name_;
103 class Retrier {
104 public:
105 Retrier(MethodID method, RetrierProvider* provider)
106 : start_(base::TimeTicks::Now()),
107 limit_(start_ + base::TimeDelta::FromMilliseconds(
108 provider->MaxRetryTimeMillis())),
109 last_(start_),
110 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
111 success_(true),
112 method_(method),
113 last_error_(base::File::FILE_OK),
114 provider_(provider) {}
115 ~Retrier() {
116 if (success_) {
117 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
118 if (last_error_ != base::File::FILE_OK) {
119 DCHECK_LT(last_error_, 0);
120 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
124 bool ShouldKeepTrying(base::File::Error last_error) {
125 DCHECK_NE(last_error, base::File::FILE_OK);
126 last_error_ = last_error;
127 if (last_ < limit_) {
128 base::PlatformThread::Sleep(time_to_sleep_);
129 last_ = base::TimeTicks::Now();
130 return true;
132 success_ = false;
133 return false;
136 private:
137 base::TimeTicks start_;
138 base::TimeTicks limit_;
139 base::TimeTicks last_;
140 base::TimeDelta time_to_sleep_;
141 bool success_;
142 MethodID method_;
143 base::File::Error last_error_;
144 RetrierProvider* provider_;
147 class ChromiumSequentialFile : public leveldb::SequentialFile {
148 public:
149 ChromiumSequentialFile(const std::string& fname,
150 base::File* f,
151 const UMALogger* uma_logger)
152 : filename_(fname), file_(f), uma_logger_(uma_logger) {}
153 virtual ~ChromiumSequentialFile() {}
155 Status Read(size_t n, Slice* result, char* scratch) override {
156 int bytes_read = file_->ReadAtCurrentPosNoBestEffort(scratch, n);
157 if (bytes_read == -1) {
158 base::File::Error error = LastFileError();
159 uma_logger_->RecordErrorAt(kSequentialFileRead);
160 return MakeIOError(filename_, base::File::ErrorToString(error),
161 kSequentialFileRead, error);
162 } else {
163 *result = Slice(scratch, bytes_read);
164 return Status::OK();
168 Status Skip(uint64_t n) override {
169 if (file_->Seek(base::File::FROM_CURRENT, n) == -1) {
170 base::File::Error error = LastFileError();
171 uma_logger_->RecordErrorAt(kSequentialFileSkip);
172 return MakeIOError(filename_, base::File::ErrorToString(error),
173 kSequentialFileSkip, error);
174 } else {
175 return Status::OK();
179 private:
180 std::string filename_;
181 scoped_ptr<base::File> file_;
182 const UMALogger* uma_logger_;
185 class ChromiumRandomAccessFile : public leveldb::RandomAccessFile {
186 public:
187 ChromiumRandomAccessFile(const std::string& fname,
188 base::File file,
189 const UMALogger* uma_logger)
190 : filename_(fname), file_(file.Pass()), uma_logger_(uma_logger) {}
191 virtual ~ChromiumRandomAccessFile() {}
193 Status Read(uint64_t offset,
194 size_t n,
195 Slice* result,
196 char* scratch) const override {
197 Status s;
198 int r = file_.Read(offset, scratch, n);
199 *result = Slice(scratch, (r < 0) ? 0 : r);
200 if (r < 0) {
201 // An error: return a non-ok status
202 s = MakeIOError(filename_, "Could not perform read",
203 kRandomAccessFileRead);
204 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
206 return s;
209 private:
210 std::string filename_;
211 mutable base::File file_;
212 const UMALogger* uma_logger_;
215 class ChromiumWritableFile : public leveldb::WritableFile {
216 public:
217 ChromiumWritableFile(const std::string& fname,
218 base::File* f,
219 const UMALogger* uma_logger,
220 bool make_backup);
221 virtual ~ChromiumWritableFile() {}
222 leveldb::Status Append(const leveldb::Slice& data) override;
223 leveldb::Status Close() override;
224 leveldb::Status Flush() override;
225 leveldb::Status Sync() override;
227 private:
228 enum Type { kManifest, kTable, kOther };
229 leveldb::Status SyncParent();
231 std::string filename_;
232 scoped_ptr<base::File> file_;
233 const UMALogger* uma_logger_;
234 Type file_type_;
235 std::string parent_dir_;
236 bool make_backup_;
239 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
240 base::File* f,
241 const UMALogger* uma_logger,
242 bool make_backup)
243 : filename_(fname),
244 file_(f),
245 uma_logger_(uma_logger),
246 file_type_(kOther),
247 make_backup_(make_backup) {
248 FilePath path = FilePath::FromUTF8Unsafe(fname);
249 if (path.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
250 file_type_ = kManifest;
251 else if (path.MatchesExtension(table_extension))
252 file_type_ = kTable;
253 parent_dir_ = FilePath::FromUTF8Unsafe(fname).DirName().AsUTF8Unsafe();
256 Status ChromiumWritableFile::SyncParent() {
257 TRACE_EVENT0("leveldb", "SyncParent");
258 #if defined(OS_POSIX)
259 FilePath path = FilePath::FromUTF8Unsafe(parent_dir_);
260 base::File f(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
261 if (!f.IsValid()) {
262 return MakeIOError(parent_dir_, "Unable to open directory", kSyncParent,
263 f.error_details());
265 if (!f.Flush()) {
266 base::File::Error error = LastFileError();
267 return MakeIOError(parent_dir_, base::File::ErrorToString(error),
268 kSyncParent, error);
270 #endif
271 return Status::OK();
274 Status ChromiumWritableFile::Append(const Slice& data) {
275 int bytes_written = file_->WriteAtCurrentPos(data.data(), data.size());
276 if (bytes_written != data.size()) {
277 base::File::Error error = LastFileError();
278 uma_logger_->RecordOSError(kWritableFileAppend, error);
279 return MakeIOError(filename_, base::File::ErrorToString(error),
280 kWritableFileAppend, error);
283 return Status::OK();
286 Status ChromiumWritableFile::Close() {
287 file_->Close();
288 return Status::OK();
291 Status ChromiumWritableFile::Flush() {
292 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
293 // flush.
294 return Status::OK();
297 Status ChromiumWritableFile::Sync() {
298 TRACE_EVENT0("leveldb", "WritableFile::Sync");
300 if (!file_->Flush()) {
301 base::File::Error error = LastFileError();
302 uma_logger_->RecordErrorAt(kWritableFileSync);
303 return MakeIOError(filename_, base::File::ErrorToString(error),
304 kWritableFileSync, error);
307 if (make_backup_ && file_type_ == kTable)
308 uma_logger_->RecordBackupResult(ChromiumEnv::MakeBackup(filename_));
310 // leveldb's implicit contract for Sync() is that if this instance is for a
311 // manifest file then the directory is also sync'ed. See leveldb's
312 // env_posix.cc.
313 if (file_type_ == kManifest)
314 return SyncParent();
316 return Status::OK();
319 class IDBEnv : public ChromiumEnv {
320 public:
321 IDBEnv() : ChromiumEnv() {
322 name_ = "LevelDBEnv.IDB";
323 uma_ioerror_base_name_ = name_ + ".IOError.BFE";
324 make_backup_ = true;
328 base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
330 base::LazyInstance<ChromiumEnv>::Leaky default_env = LAZY_INSTANCE_INITIALIZER;
332 } // unnamed namespace
334 const char* MethodIDToString(MethodID method) {
335 switch (method) {
336 case kSequentialFileRead:
337 return "SequentialFileRead";
338 case kSequentialFileSkip:
339 return "SequentialFileSkip";
340 case kRandomAccessFileRead:
341 return "RandomAccessFileRead";
342 case kWritableFileAppend:
343 return "WritableFileAppend";
344 case kWritableFileClose:
345 return "WritableFileClose";
346 case kWritableFileFlush:
347 return "WritableFileFlush";
348 case kWritableFileSync:
349 return "WritableFileSync";
350 case kNewSequentialFile:
351 return "NewSequentialFile";
352 case kNewRandomAccessFile:
353 return "NewRandomAccessFile";
354 case kNewWritableFile:
355 return "NewWritableFile";
356 case kNewAppendableFile:
357 return "NewAppendableFile";
358 case kDeleteFile:
359 return "DeleteFile";
360 case kCreateDir:
361 return "CreateDir";
362 case kDeleteDir:
363 return "DeleteDir";
364 case kGetFileSize:
365 return "GetFileSize";
366 case kRenameFile:
367 return "RenameFile";
368 case kLockFile:
369 return "LockFile";
370 case kUnlockFile:
371 return "UnlockFile";
372 case kGetTestDirectory:
373 return "GetTestDirectory";
374 case kNewLogger:
375 return "NewLogger";
376 case kSyncParent:
377 return "SyncParent";
378 case kGetChildren:
379 return "GetChildren";
380 case kNumEntries:
381 NOTREACHED();
382 return "kNumEntries";
384 NOTREACHED();
385 return "Unknown";
388 Status MakeIOError(Slice filename,
389 const std::string& message,
390 MethodID method,
391 base::File::Error error) {
392 DCHECK_LT(error, 0);
393 char buf[512];
394 snprintf(buf, sizeof(buf), "%s (ChromeMethodBFE: %d::%s::%d)",
395 message.c_str(), method, MethodIDToString(method), -error);
396 return Status::IOError(filename, buf);
399 Status MakeIOError(Slice filename,
400 const std::string& message,
401 MethodID method) {
402 char buf[512];
403 snprintf(buf, sizeof(buf), "%s (ChromeMethodOnly: %d::%s)", message.c_str(),
404 method, MethodIDToString(method));
405 return Status::IOError(filename, buf);
408 ErrorParsingResult ParseMethodAndError(const leveldb::Status& status,
409 MethodID* method_param,
410 base::File::Error* error) {
411 const std::string status_string = status.ToString();
412 int method;
413 if (RE2::PartialMatch(status_string.c_str(), "ChromeMethodOnly: (\\d+)",
414 &method)) {
415 *method_param = static_cast<MethodID>(method);
416 return METHOD_ONLY;
418 int parsed_error;
419 if (RE2::PartialMatch(status_string.c_str(),
420 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method,
421 &parsed_error)) {
422 *method_param = static_cast<MethodID>(method);
423 *error = static_cast<base::File::Error>(-parsed_error);
424 DCHECK_LT(*error, base::File::FILE_OK);
425 DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
426 return METHOD_AND_BFE;
428 return NONE;
431 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
432 // change the order because indices into this array have been recorded in uma
433 // histograms.
434 const char* patterns[] = {
435 "missing files",
436 "log record too small",
437 "corrupted internal key",
438 "partial record",
439 "missing start of fragmented record",
440 "error in middle of record",
441 "unknown record type",
442 "truncated record at end",
443 "bad record length",
444 "VersionEdit",
445 "FileReader invoked with unexpected value",
446 "corrupted key",
447 "CURRENT file does not end with newline",
448 "no meta-nextfile entry",
449 "no meta-lognumber entry",
450 "no last-sequence-number entry",
451 "malformed WriteBatch",
452 "bad WriteBatch Put",
453 "bad WriteBatch Delete",
454 "unknown WriteBatch tag",
455 "WriteBatch has wrong count",
456 "bad entry in block",
457 "bad block contents",
458 "bad block handle",
459 "truncated block read",
460 "block checksum mismatch",
461 "checksum mismatch",
462 "corrupted compressed block contents",
463 "bad block type",
464 "bad magic number",
465 "file is too short",
468 // Returns 1-based index into the above array or 0 if nothing matches.
469 int GetCorruptionCode(const leveldb::Status& status) {
470 DCHECK(!status.IsIOError());
471 DCHECK(!status.ok());
472 const int kOtherError = 0;
473 int error = kOtherError;
474 const std::string& str_error = status.ToString();
475 const size_t kNumPatterns = arraysize(patterns);
476 for (size_t i = 0; i < kNumPatterns; ++i) {
477 if (str_error.find(patterns[i]) != std::string::npos) {
478 error = i + 1;
479 break;
482 return error;
485 int GetNumCorruptionCodes() {
486 // + 1 for the "other" error that is returned when a corruption message
487 // doesn't match any of the patterns.
488 return arraysize(patterns) + 1;
491 std::string GetCorruptionMessage(const leveldb::Status& status) {
492 int code = GetCorruptionCode(status);
493 if (code == 0)
494 return "Unknown corruption";
495 return patterns[code - 1];
498 bool IndicatesDiskFull(const leveldb::Status& status) {
499 if (status.ok())
500 return false;
501 leveldb_env::MethodID method;
502 base::File::Error error = base::File::FILE_OK;
503 leveldb_env::ErrorParsingResult result =
504 leveldb_env::ParseMethodAndError(status, &method, &error);
505 return (result == leveldb_env::METHOD_AND_BFE &&
506 static_cast<base::File::Error>(error) ==
507 base::File::FILE_ERROR_NO_SPACE);
510 bool ChromiumEnv::MakeBackup(const std::string& fname) {
511 FilePath original_table_name = FilePath::FromUTF8Unsafe(fname);
512 FilePath backup_table_name =
513 original_table_name.ReplaceExtension(backup_table_extension);
514 return base::CopyFile(original_table_name, backup_table_name);
517 ChromiumEnv::ChromiumEnv()
518 : name_("LevelDBEnv"),
519 make_backup_(false),
520 bgsignal_(&mu_),
521 started_bgthread_(false),
522 kMaxRetryTimeMillis(1000) {
523 uma_ioerror_base_name_ = name_ + ".IOError.BFE";
526 ChromiumEnv::~ChromiumEnv() {
527 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
528 // ensure that behavior isn't accidentally changed, but there's an instance in
529 // a unit test that is deleted.
532 bool ChromiumEnv::FileExists(const std::string& fname) {
533 return base::PathExists(FilePath::FromUTF8Unsafe(fname));
536 const char* ChromiumEnv::FileErrorString(base::File::Error error) {
537 switch (error) {
538 case base::File::FILE_ERROR_FAILED:
539 return "No further details.";
540 case base::File::FILE_ERROR_IN_USE:
541 return "File currently in use.";
542 case base::File::FILE_ERROR_EXISTS:
543 return "File already exists.";
544 case base::File::FILE_ERROR_NOT_FOUND:
545 return "File not found.";
546 case base::File::FILE_ERROR_ACCESS_DENIED:
547 return "Access denied.";
548 case base::File::FILE_ERROR_TOO_MANY_OPENED:
549 return "Too many files open.";
550 case base::File::FILE_ERROR_NO_MEMORY:
551 return "Out of memory.";
552 case base::File::FILE_ERROR_NO_SPACE:
553 return "No space left on drive.";
554 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
555 return "Not a directory.";
556 case base::File::FILE_ERROR_INVALID_OPERATION:
557 return "Invalid operation.";
558 case base::File::FILE_ERROR_SECURITY:
559 return "Security error.";
560 case base::File::FILE_ERROR_ABORT:
561 return "File operation aborted.";
562 case base::File::FILE_ERROR_NOT_A_FILE:
563 return "The supplied path was not a file.";
564 case base::File::FILE_ERROR_NOT_EMPTY:
565 return "The file was not empty.";
566 case base::File::FILE_ERROR_INVALID_URL:
567 return "Invalid URL.";
568 case base::File::FILE_ERROR_IO:
569 return "OS or hardware error.";
570 case base::File::FILE_OK:
571 return "OK.";
572 case base::File::FILE_ERROR_MAX:
573 NOTREACHED();
575 NOTIMPLEMENTED();
576 return "Unknown error.";
579 FilePath ChromiumEnv::RestoreFromBackup(const FilePath& base_name) {
580 FilePath table_name = base_name.AddExtension(table_extension);
581 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
582 table_name);
583 std::string uma_name(name_);
584 uma_name.append(".TableRestore");
585 base::BooleanHistogram::FactoryGet(
586 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
587 return table_name;
590 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
591 std::vector<std::string>* dir_entries) {
592 std::set<FilePath> tables_found;
593 std::set<FilePath> backups_found;
594 for (const std::string& entry : *dir_entries) {
595 FilePath current = FilePath::FromUTF8Unsafe(entry);
596 if (current.MatchesExtension(table_extension))
597 tables_found.insert(current.RemoveExtension());
598 if (current.MatchesExtension(backup_table_extension))
599 backups_found.insert(current.RemoveExtension());
601 std::set<FilePath> backups_only =
602 base::STLSetDifference<std::set<FilePath>>(backups_found, tables_found);
604 if (backups_only.size()) {
605 std::string uma_name(name_);
606 uma_name.append(".MissingFiles");
607 int num_missing_files =
608 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
609 base::Histogram::FactoryGet(uma_name,
610 1 /*min*/,
611 100 /*max*/,
612 8 /*num_buckets*/,
613 base::Histogram::kUmaTargetedHistogramFlag)
614 ->Add(num_missing_files);
616 FilePath dir_path = FilePath::FromUTF8Unsafe(dir);
617 for (const FilePath& backup : backups_only) {
618 FilePath restored_table_name = RestoreFromBackup(dir_path.Append(backup));
619 dir_entries->push_back(restored_table_name.BaseName().AsUTF8Unsafe());
623 Status ChromiumEnv::GetChildren(const std::string& dir,
624 std::vector<std::string>* result) {
625 std::vector<FilePath> entries;
626 base::File::Error error =
627 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir), &entries);
628 if (error != base::File::FILE_OK) {
629 RecordOSError(kGetChildren, error);
630 return MakeIOError(dir, "Could not open/read directory", kGetChildren,
631 error);
634 result->clear();
635 for (const auto& entry : entries)
636 result->push_back(entry.BaseName().AsUTF8Unsafe());
638 if (make_backup_)
639 RestoreIfNecessary(dir, result);
641 return Status::OK();
644 Status ChromiumEnv::DeleteFile(const std::string& fname) {
645 Status result;
646 FilePath fname_filepath = FilePath::FromUTF8Unsafe(fname);
647 // TODO(jorlow): Should we assert this is a file?
648 if (!base::DeleteFile(fname_filepath, false)) {
649 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
650 RecordErrorAt(kDeleteFile);
652 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
653 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
654 false);
656 return result;
659 Status ChromiumEnv::CreateDir(const std::string& name) {
660 Status result;
661 base::File::Error error = base::File::FILE_OK;
662 Retrier retrier(kCreateDir, this);
663 do {
664 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name),
665 &error))
666 return result;
667 } while (retrier.ShouldKeepTrying(error));
668 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
669 RecordOSError(kCreateDir, error);
670 return result;
673 Status ChromiumEnv::DeleteDir(const std::string& name) {
674 Status result;
675 // TODO(jorlow): Should we assert this is a directory?
676 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name), false)) {
677 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
678 RecordErrorAt(kDeleteDir);
680 return result;
683 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
684 Status s;
685 int64_t signed_size;
686 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname), &signed_size)) {
687 *size = 0;
688 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
689 RecordErrorAt(kGetFileSize);
690 } else {
691 *size = static_cast<uint64_t>(signed_size);
693 return s;
696 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
697 Status result;
698 FilePath src_file_path = FilePath::FromUTF8Unsafe(src);
699 if (!base::PathExists(src_file_path))
700 return result;
701 FilePath destination = FilePath::FromUTF8Unsafe(dst);
703 Retrier retrier(kRenameFile, this);
704 base::File::Error error = base::File::FILE_OK;
705 do {
706 if (base::ReplaceFile(src_file_path, destination, &error))
707 return result;
708 } while (retrier.ShouldKeepTrying(error));
710 DCHECK(error != base::File::FILE_OK);
711 RecordOSError(kRenameFile, error);
712 char buf[100];
713 snprintf(buf,
714 sizeof(buf),
715 "Could not rename file: %s",
716 FileErrorString(error));
717 return MakeIOError(src, buf, kRenameFile, error);
720 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
721 *lock = NULL;
722 Status result;
723 int flags = base::File::FLAG_OPEN_ALWAYS |
724 base::File::FLAG_READ |
725 base::File::FLAG_WRITE;
726 base::File::Error error_code;
727 base::File file;
728 Retrier retrier(kLockFile, this);
729 do {
730 file.Initialize(FilePath::FromUTF8Unsafe(fname), flags);
731 if (!file.IsValid())
732 error_code = file.error_details();
733 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
735 if (!file.IsValid()) {
736 if (error_code == base::File::FILE_ERROR_NOT_FOUND) {
737 FilePath parent = FilePath::FromUTF8Unsafe(fname).DirName();
738 FilePath last_parent;
739 int num_missing_ancestors = 0;
740 do {
741 if (base::DirectoryExists(parent))
742 break;
743 ++num_missing_ancestors;
744 last_parent = parent;
745 parent = parent.DirName();
746 } while (parent != last_parent);
747 RecordLockFileAncestors(num_missing_ancestors);
750 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
751 error_code);
752 RecordOSError(kLockFile, error_code);
753 return result;
756 if (!locks_.Insert(fname)) {
757 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
758 return result;
761 Retrier lock_retrier = Retrier(kLockFile, this);
762 do {
763 error_code = file.Lock();
764 } while (error_code != base::File::FILE_OK &&
765 retrier.ShouldKeepTrying(error_code));
767 if (error_code != base::File::FILE_OK) {
768 locks_.Remove(fname);
769 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
770 error_code);
771 RecordOSError(kLockFile, error_code);
772 return result;
775 ChromiumFileLock* my_lock = new ChromiumFileLock;
776 my_lock->file_ = file.Pass();
777 my_lock->name_ = fname;
778 *lock = my_lock;
779 return result;
782 Status ChromiumEnv::UnlockFile(FileLock* lock) {
783 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
784 Status result;
786 base::File::Error error_code = my_lock->file_.Unlock();
787 if (error_code != base::File::FILE_OK) {
788 result =
789 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
790 RecordOSError(kUnlockFile, error_code);
792 bool removed = locks_.Remove(my_lock->name_);
793 DCHECK(removed);
794 delete my_lock;
795 return result;
798 Status ChromiumEnv::GetTestDirectory(std::string* path) {
799 mu_.Acquire();
800 if (test_directory_.empty()) {
801 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
802 &test_directory_)) {
803 mu_.Release();
804 RecordErrorAt(kGetTestDirectory);
805 return MakeIOError(
806 "Could not create temp directory.", "", kGetTestDirectory);
809 *path = test_directory_.AsUTF8Unsafe();
810 mu_.Release();
811 return Status::OK();
814 Status ChromiumEnv::NewLogger(const std::string& fname,
815 leveldb::Logger** result) {
816 FilePath path = FilePath::FromUTF8Unsafe(fname);
817 scoped_ptr<base::File> f(new base::File(
818 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
819 if (!f->IsValid()) {
820 *result = NULL;
821 RecordOSError(kNewLogger, f->error_details());
822 return MakeIOError(fname, "Unable to create log file", kNewLogger,
823 f->error_details());
824 } else {
825 *result = new leveldb::ChromiumLogger(f.release());
826 return Status::OK();
830 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
831 leveldb::SequentialFile** result) {
832 FilePath path = FilePath::FromUTF8Unsafe(fname);
833 scoped_ptr<base::File> f(
834 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
835 if (!f->IsValid()) {
836 *result = NULL;
837 RecordOSError(kNewSequentialFile, f->error_details());
838 return MakeIOError(fname, "Unable to create sequential file",
839 kNewSequentialFile, f->error_details());
840 } else {
841 *result = new ChromiumSequentialFile(fname, f.release(), this);
842 return Status::OK();
846 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
847 #if defined(OS_POSIX)
848 GetMaxFDHistogram(type)->Add(base::GetMaxFds());
849 #elif defined(OS_WIN)
850 // Windows is only limited by available memory
851 #else
852 #error "Need to determine limit to open files for this OS"
853 #endif
856 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
857 leveldb::RandomAccessFile** result) {
858 int flags = base::File::FLAG_READ | base::File::FLAG_OPEN;
859 base::File file(FilePath::FromUTF8Unsafe(fname), flags);
860 if (file.IsValid()) {
861 *result = new ChromiumRandomAccessFile(fname, file.Pass(), this);
862 RecordOpenFilesLimit("Success");
863 return Status::OK();
865 base::File::Error error_code = file.error_details();
866 if (error_code == base::File::FILE_ERROR_TOO_MANY_OPENED)
867 RecordOpenFilesLimit("TooManyOpened");
868 else
869 RecordOpenFilesLimit("OtherError");
870 *result = NULL;
871 RecordOSError(kNewRandomAccessFile, error_code);
872 return MakeIOError(fname, FileErrorString(error_code), kNewRandomAccessFile,
873 error_code);
876 Status ChromiumEnv::NewWritableFile(const std::string& fname,
877 leveldb::WritableFile** result) {
878 *result = NULL;
879 FilePath path = FilePath::FromUTF8Unsafe(fname);
880 scoped_ptr<base::File> f(new base::File(
881 path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE));
882 if (!f->IsValid()) {
883 RecordErrorAt(kNewWritableFile);
884 return MakeIOError(fname, "Unable to create writable file",
885 kNewWritableFile, f->error_details());
886 } else {
887 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
888 return Status::OK();
892 Status ChromiumEnv::NewAppendableFile(const std::string& fname,
893 leveldb::WritableFile** result) {
894 *result = NULL;
895 FilePath path = FilePath::FromUTF8Unsafe(fname);
896 scoped_ptr<base::File> f(new base::File(
897 path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND));
898 if (!f->IsValid()) {
899 RecordErrorAt(kNewAppendableFile);
900 return MakeIOError(fname, "Unable to create appendable file",
901 kNewAppendableFile, f->error_details());
903 *result = new ChromiumWritableFile(fname, f.release(), this, make_backup_);
904 return Status::OK();
907 uint64_t ChromiumEnv::NowMicros() {
908 return base::TimeTicks::Now().ToInternalValue();
911 void ChromiumEnv::SleepForMicroseconds(int micros) {
912 // Round up to the next millisecond.
913 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
916 void ChromiumEnv::RecordErrorAt(MethodID method) const {
917 GetMethodIOErrorHistogram()->Add(method);
920 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
921 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
924 void ChromiumEnv::RecordOSError(MethodID method,
925 base::File::Error error) const {
926 DCHECK_LT(error, 0);
927 RecordErrorAt(method);
928 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
931 void ChromiumEnv::RecordBackupResult(bool result) const {
932 std::string uma_name(name_);
933 uma_name.append(".TableBackup");
934 base::BooleanHistogram::FactoryGet(
935 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
938 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
939 int limit) const {
940 std::string uma_name;
941 base::StringAppendF(&uma_name, "%s.%s", uma_ioerror_base_name_.c_str(),
942 MethodIDToString(method));
943 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
944 base::Histogram::kUmaTargetedHistogramFlag);
947 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
948 std::string uma_name(name_);
949 uma_name.append(".IOError");
950 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
951 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
954 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
955 const std::string& type) const {
956 std::string uma_name(name_);
957 uma_name.append(".MaxFDs.").append(type);
958 // These numbers make each bucket twice as large as the previous bucket.
959 const int kFirstEntry = 1;
960 const int kLastEntry = 65536;
961 const int kNumBuckets = 18;
962 return base::Histogram::FactoryGet(
963 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
964 base::Histogram::kUmaTargetedHistogramFlag);
967 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
968 std::string uma_name(name_);
969 uma_name.append(".LockFileAncestorsNotFound");
970 const int kMin = 1;
971 const int kMax = 10;
972 const int kNumBuckets = 11;
973 return base::LinearHistogram::FactoryGet(
974 uma_name, kMin, kMax, kNumBuckets,
975 base::Histogram::kUmaTargetedHistogramFlag);
978 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
979 std::string uma_name(name_);
980 // TODO(dgrogan): This is probably not the best way to concatenate strings.
981 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
983 const int kBucketSizeMillis = 25;
984 // Add 2, 1 for each of the buckets <1 and >max.
985 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
986 return base::Histogram::FactoryTimeGet(
987 uma_name, base::TimeDelta::FromMilliseconds(1),
988 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
989 kNumBuckets,
990 base::Histogram::kUmaTargetedHistogramFlag);
993 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
994 MethodID method) const {
995 std::string uma_name(name_);
996 uma_name.append(".RetryRecoveredFromErrorIn")
997 .append(MethodIDToString(method));
998 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
999 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
1002 class Thread : public base::PlatformThread::Delegate {
1003 public:
1004 Thread(void (*function)(void* arg), void* arg)
1005 : function_(function), arg_(arg) {
1006 base::PlatformThreadHandle handle;
1007 bool success = base::PlatformThread::Create(0, this, &handle);
1008 DCHECK(success);
1010 virtual ~Thread() {}
1011 void ThreadMain() override {
1012 (*function_)(arg_);
1013 delete this;
1016 private:
1017 void (*function_)(void* arg);
1018 void* arg_;
1021 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
1022 mu_.Acquire();
1024 // Start background thread if necessary
1025 if (!started_bgthread_) {
1026 started_bgthread_ = true;
1027 StartThread(&ChromiumEnv::BGThreadWrapper, this);
1030 // If the queue is currently empty, the background thread may currently be
1031 // waiting.
1032 if (queue_.empty()) {
1033 bgsignal_.Signal();
1036 // Add to priority queue
1037 queue_.push_back(BGItem());
1038 queue_.back().function = function;
1039 queue_.back().arg = arg;
1041 mu_.Release();
1044 void ChromiumEnv::BGThread() {
1045 base::PlatformThread::SetName(name_.c_str());
1047 while (true) {
1048 // Wait until there is an item that is ready to run
1049 mu_.Acquire();
1050 while (queue_.empty()) {
1051 bgsignal_.Wait();
1054 void (*function)(void*) = queue_.front().function;
1055 void* arg = queue_.front().arg;
1056 queue_.pop_front();
1058 mu_.Release();
1059 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1060 (*function)(arg);
1064 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
1065 new Thread(function, arg); // Will self-delete.
1068 } // namespace leveldb_env
1070 namespace leveldb {
1072 Env* IDBEnv() {
1073 return leveldb_env::idb_env.Pointer();
1076 Env* Env::Default() {
1077 return leveldb_env::default_env.Pointer();
1080 } // namespace leveldb