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