Home | History | Annotate | Download | only in leveldatabase
      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 <fcntl.h>
     41 #include <sys/resource.h>
     42 #include <sys/time.h>
     43 #endif
     44 
     45 using namespace leveldb;
     46 
     47 namespace leveldb_env {
     48 
     49 namespace {
     50 
     51 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
     52     defined(OS_OPENBSD)
     53 // The following are glibc-specific
     54 
     55 size_t fread_wrapper(void *ptr, size_t size, size_t n, FILE *file) {
     56   return fread(ptr, size, n, file);
     57 }
     58 
     59 size_t fwrite_wrapper(const void *ptr, size_t size, size_t n, FILE *file) {
     60   return fwrite(ptr, size, n, file);
     61 }
     62 
     63 int fflush_wrapper(FILE *file) {
     64   return fflush(file);
     65 }
     66 
     67 #if !defined(OS_ANDROID)
     68 int fdatasync(int fildes) {
     69 #if defined(OS_WIN)
     70   return _commit(fildes);
     71 #else
     72   return HANDLE_EINTR(fsync(fildes));
     73 #endif
     74 }
     75 #endif
     76 
     77 #else
     78 
     79 class TryToLockFILE {
     80   // This class should be deleted if it doesn't turn up anything useful after
     81   // going to stable (chrome 29).
     82  public:
     83   TryToLockFILE(FILE* file) : file_to_unlock_(NULL) {
     84     if (ftrylockfile(file) == 0)
     85       file_to_unlock_ = file;
     86     else
     87       UMA_HISTOGRAM_BOOLEAN("LevelDBEnv.All.SafeThreadAccess", false);
     88   }
     89   ~TryToLockFILE() {
     90     if (file_to_unlock_)
     91       funlockfile(file_to_unlock_);
     92   }
     93 
     94  private:
     95   FILE* file_to_unlock_;
     96 };
     97 
     98 size_t fread_wrapper(void *ptr, size_t size, size_t n, FILE *file) {
     99   TryToLockFILE lock(file);
    100   return fread_unlocked(ptr, size, n, file);
    101 }
    102 
    103 size_t fwrite_wrapper(const void *ptr, size_t size, size_t n, FILE *file) {
    104   TryToLockFILE lock(file);
    105   return fwrite_unlocked(ptr, size, n, file);
    106 }
    107 
    108 int fflush_wrapper(FILE *file) {
    109   TryToLockFILE lock(file);
    110   return fflush_unlocked(file);
    111 }
    112 
    113 #endif
    114 
    115 // Wide-char safe fopen wrapper.
    116 FILE* fopen_internal(const char* fname, const char* mode) {
    117 #if defined(OS_WIN)
    118   return _wfopen(UTF8ToUTF16(fname).c_str(), ASCIIToUTF16(mode).c_str());
    119 #else
    120   return fopen(fname, mode);
    121 #endif
    122 }
    123 
    124 base::FilePath CreateFilePath(const std::string& file_path) {
    125 #if defined(OS_WIN)
    126   return base::FilePath(UTF8ToUTF16(file_path));
    127 #else
    128   return base::FilePath(file_path);
    129 #endif
    130 }
    131 
    132 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
    133     = FILE_PATH_LITERAL("leveldb-test-");
    134 
    135 const char* PlatformFileErrorString(const ::base::PlatformFileError& error) {
    136   switch (error) {
    137     case ::base::PLATFORM_FILE_ERROR_FAILED:
    138       return "No further details.";
    139     case ::base::PLATFORM_FILE_ERROR_IN_USE:
    140       return "File currently in use.";
    141     case ::base::PLATFORM_FILE_ERROR_EXISTS:
    142       return "File already exists.";
    143     case ::base::PLATFORM_FILE_ERROR_NOT_FOUND:
    144       return "File not found.";
    145     case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
    146       return "Access denied.";
    147     case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED:
    148       return "Too many files open.";
    149     case ::base::PLATFORM_FILE_ERROR_NO_MEMORY:
    150       return "Out of memory.";
    151     case ::base::PLATFORM_FILE_ERROR_NO_SPACE:
    152       return "No space left on drive.";
    153     case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
    154       return "Not a directory.";
    155     case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
    156       return "Invalid operation.";
    157     case ::base::PLATFORM_FILE_ERROR_SECURITY:
    158       return "Security error.";
    159     case ::base::PLATFORM_FILE_ERROR_ABORT:
    160       return "File operation aborted.";
    161     case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE:
    162       return "The supplied path was not a file.";
    163     case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY:
    164       return "The file was not empty.";
    165     case ::base::PLATFORM_FILE_ERROR_INVALID_URL:
    166       return "Invalid URL.";
    167     case ::base::PLATFORM_FILE_ERROR_IO:
    168       return "OS or hardware error.";
    169     case ::base::PLATFORM_FILE_OK:
    170       return "OK.";
    171     case ::base::PLATFORM_FILE_ERROR_MAX:
    172       NOTREACHED();
    173   }
    174   NOTIMPLEMENTED();
    175   return "Unknown error.";
    176 }
    177 
    178 class ChromiumSequentialFile: public SequentialFile {
    179  private:
    180   std::string filename_;
    181   FILE* file_;
    182   const UMALogger* uma_logger_;
    183 
    184  public:
    185   ChromiumSequentialFile(const std::string& fname, FILE* f,
    186                          const UMALogger* uma_logger)
    187       : filename_(fname), file_(f), uma_logger_(uma_logger) { }
    188   virtual ~ChromiumSequentialFile() { fclose(file_); }
    189 
    190   virtual Status Read(size_t n, Slice* result, char* scratch) {
    191     Status s;
    192     size_t r = fread_wrapper(scratch, 1, n, file_);
    193     *result = Slice(scratch, r);
    194     if (r < n) {
    195       if (feof(file_)) {
    196         // We leave status as ok if we hit the end of the file
    197       } else {
    198         // A partial read with an error: return a non-ok status
    199         s = MakeIOError(filename_, strerror(errno), kSequentialFileRead, errno);
    200         uma_logger_->RecordErrorAt(kSequentialFileRead);
    201       }
    202     }
    203     return s;
    204   }
    205 
    206   virtual Status Skip(uint64_t n) {
    207     if (fseek(file_, n, SEEK_CUR)) {
    208       int saved_errno = errno;
    209       uma_logger_->RecordErrorAt(kSequentialFileSkip);
    210       return MakeIOError(
    211           filename_, strerror(saved_errno), kSequentialFileSkip, saved_errno);
    212     }
    213     return Status::OK();
    214   }
    215 };
    216 
    217 class ChromiumRandomAccessFile: public RandomAccessFile {
    218  private:
    219   std::string filename_;
    220   ::base::PlatformFile file_;
    221   const UMALogger* uma_logger_;
    222 
    223  public:
    224   ChromiumRandomAccessFile(const std::string& fname, ::base::PlatformFile file,
    225                            const UMALogger* uma_logger)
    226       : filename_(fname), file_(file), uma_logger_(uma_logger) { }
    227   virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_); }
    228 
    229   virtual Status Read(uint64_t offset, size_t n, Slice* result,
    230                       char* scratch) const {
    231     Status s;
    232     int r = ::base::ReadPlatformFile(file_, offset, scratch, n);
    233     *result = Slice(scratch, (r < 0) ? 0 : r);
    234     if (r < 0) {
    235       // An error: return a non-ok status
    236       s = MakeIOError(
    237           filename_, "Could not perform read", kRandomAccessFileRead);
    238       uma_logger_->RecordErrorAt(kRandomAccessFileRead);
    239     }
    240     return s;
    241   }
    242 };
    243 
    244 class ChromiumFileLock : public FileLock {
    245  public:
    246   ::base::PlatformFile file_;
    247 };
    248 
    249 class Retrier {
    250  public:
    251   Retrier(MethodID method, RetrierProvider* provider)
    252       : start_(base::TimeTicks::Now()),
    253         limit_(start_ + base::TimeDelta::FromMilliseconds(
    254                             provider->MaxRetryTimeMillis())),
    255         last_(start_),
    256         time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
    257         success_(true),
    258         method_(method),
    259         last_error_(base::PLATFORM_FILE_OK),
    260         provider_(provider) {}
    261   ~Retrier() {
    262     if (success_) {
    263       provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
    264       if (last_error_ != base::PLATFORM_FILE_OK) {
    265         DCHECK(last_error_ < 0);
    266         provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
    267       }
    268     }
    269   }
    270   bool ShouldKeepTrying(base::PlatformFileError last_error) {
    271     DCHECK_NE(last_error, base::PLATFORM_FILE_OK);
    272     last_error_ = last_error;
    273     if (last_ < limit_) {
    274       base::PlatformThread::Sleep(time_to_sleep_);
    275       last_ = base::TimeTicks::Now();
    276       return true;
    277     }
    278     success_ = false;
    279     return false;
    280   }
    281 
    282  private:
    283   base::TimeTicks start_;
    284   base::TimeTicks limit_;
    285   base::TimeTicks last_;
    286   base::TimeDelta time_to_sleep_;
    287   bool success_;
    288   MethodID method_;
    289   base::PlatformFileError last_error_;
    290   RetrierProvider* provider_;
    291 };
    292 
    293 class IDBEnv : public ChromiumEnv {
    294  public:
    295   IDBEnv() : ChromiumEnv() { name_ = "LevelDBEnv.IDB"; }
    296 };
    297 
    298 ::base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
    299 
    300 ::base::LazyInstance<ChromiumEnv>::Leaky default_env =
    301     LAZY_INSTANCE_INITIALIZER;
    302 
    303 }  // unnamed namespace
    304 
    305 const char* MethodIDToString(MethodID method) {
    306   switch (method) {
    307     case kSequentialFileRead:
    308       return "SequentialFileRead";
    309     case kSequentialFileSkip:
    310       return "SequentialFileSkip";
    311     case kRandomAccessFileRead:
    312       return "RandomAccessFileRead";
    313     case kWritableFileAppend:
    314       return "WritableFileAppend";
    315     case kWritableFileClose:
    316       return "WritableFileClose";
    317     case kWritableFileFlush:
    318       return "WritableFileFlush";
    319     case kWritableFileSync:
    320       return "WritableFileSync";
    321     case kNewSequentialFile:
    322       return "NewSequentialFile";
    323     case kNewRandomAccessFile:
    324       return "NewRandomAccessFile";
    325     case kNewWritableFile:
    326       return "NewWritableFile";
    327     case kDeleteFile:
    328       return "DeleteFile";
    329     case kCreateDir:
    330       return "CreateDir";
    331     case kDeleteDir:
    332       return "DeleteDir";
    333     case kGetFileSize:
    334       return "GetFileSize";
    335     case kRenameFile:
    336       return "RenameFile";
    337     case kLockFile:
    338       return "LockFile";
    339     case kUnlockFile:
    340       return "UnlockFile";
    341     case kGetTestDirectory:
    342       return "GetTestDirectory";
    343     case kNewLogger:
    344       return "NewLogger";
    345     case kSyncParent:
    346       return "SyncParent";
    347     case kNumEntries:
    348       NOTREACHED();
    349       return "kNumEntries";
    350   }
    351   NOTREACHED();
    352   return "Unknown";
    353 }
    354 
    355 Status MakeIOError(Slice filename,
    356                    const char* message,
    357                    MethodID method,
    358                    int saved_errno) {
    359   char buf[512];
    360   snprintf(buf,
    361            sizeof(buf),
    362            "%s (ChromeMethodErrno: %d::%s::%d)",
    363            message,
    364            method,
    365            MethodIDToString(method),
    366            saved_errno);
    367   return Status::IOError(filename, buf);
    368 }
    369 
    370 Status MakeIOError(Slice filename,
    371                    const char* message,
    372                    MethodID method,
    373                    base::PlatformFileError error) {
    374   DCHECK(error < 0);
    375   char buf[512];
    376   snprintf(buf,
    377            sizeof(buf),
    378            "%s (ChromeMethodPFE: %d::%s::%d)",
    379            message,
    380            method,
    381            MethodIDToString(method),
    382            -error);
    383   return Status::IOError(filename, buf);
    384 }
    385 
    386 Status MakeIOError(Slice filename, const char* message, MethodID method) {
    387   char buf[512];
    388   snprintf(buf,
    389            sizeof(buf),
    390            "%s (ChromeMethodOnly: %d::%s)",
    391            message,
    392            method,
    393            MethodIDToString(method));
    394   return Status::IOError(filename, buf);
    395 }
    396 
    397 ErrorParsingResult ParseMethodAndError(const char* string,
    398                                        MethodID* method_param,
    399                                        int* error) {
    400   int method;
    401   if (RE2::PartialMatch(string, "ChromeMethodOnly: (\\d+)", &method)) {
    402     *method_param = static_cast<MethodID>(method);
    403     return METHOD_ONLY;
    404   }
    405   if (RE2::PartialMatch(
    406           string, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method, error)) {
    407     *error = -*error;
    408     *method_param = static_cast<MethodID>(method);
    409     return METHOD_AND_PFE;
    410   }
    411   if (RE2::PartialMatch(
    412           string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) {
    413     *method_param = static_cast<MethodID>(method);
    414     return METHOD_AND_ERRNO;
    415   }
    416   return NONE;
    417 }
    418 
    419 std::string FilePathToString(const base::FilePath& file_path) {
    420 #if defined(OS_WIN)
    421   return UTF16ToUTF8(file_path.value());
    422 #else
    423   return file_path.value();
    424 #endif
    425 }
    426 
    427 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
    428                                            FILE* f,
    429                                            const UMALogger* uma_logger,
    430                                            WriteTracker* tracker)
    431     : filename_(fname), file_(f), uma_logger_(uma_logger), tracker_(tracker) {
    432   base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
    433   is_manifest_ =
    434       FilePathToString(path.BaseName()).find("MANIFEST") !=
    435       std::string::npos;
    436   if (!is_manifest_)
    437     tracker_->DidCreateNewFile(filename_);
    438   parent_dir_ = FilePathToString(CreateFilePath(fname).DirName());
    439 }
    440 
    441 ChromiumWritableFile::~ChromiumWritableFile() {
    442   if (file_ != NULL) {
    443     // Ignoring any potential errors
    444     fclose(file_);
    445   }
    446 }
    447 
    448 Status ChromiumWritableFile::SyncParent() {
    449   Status s;
    450 #if !defined(OS_WIN)
    451   TRACE_EVENT0("leveldb", "SyncParent");
    452 
    453   int parent_fd =
    454       HANDLE_EINTR(open(parent_dir_.c_str(), O_RDONLY));
    455   if (parent_fd < 0)
    456     return MakeIOError(parent_dir_, strerror(errno), kSyncParent);
    457   if (HANDLE_EINTR(fsync(parent_fd)) != 0) {
    458     s = MakeIOError(parent_dir_, strerror(errno), kSyncParent);
    459   };
    460   HANDLE_EINTR(close(parent_fd));
    461 #endif
    462   return s;
    463 }
    464 
    465 Status ChromiumWritableFile::Append(const Slice& data) {
    466   if (is_manifest_ && tracker_->DoesDirNeedSync(filename_)) {
    467     Status s = SyncParent();
    468     if (!s.ok())
    469       return s;
    470     tracker_->DidSyncDir(filename_);
    471   }
    472 
    473   size_t r = fwrite_wrapper(data.data(), 1, data.size(), file_);
    474   if (r != data.size()) {
    475     int saved_errno = errno;
    476     uma_logger_->RecordOSError(kWritableFileAppend, saved_errno);
    477     return MakeIOError(
    478         filename_, strerror(saved_errno), kWritableFileAppend, saved_errno);
    479   }
    480   return Status::OK();
    481 }
    482 
    483 Status ChromiumWritableFile::Close() {
    484   Status result;
    485   if (fclose(file_) != 0) {
    486     result = MakeIOError(filename_, strerror(errno), kWritableFileClose, errno);
    487     uma_logger_->RecordErrorAt(kWritableFileClose);
    488   }
    489   file_ = NULL;
    490   return result;
    491 }
    492 
    493 Status ChromiumWritableFile::Flush() {
    494   Status result;
    495   if (HANDLE_EINTR(fflush_wrapper(file_))) {
    496     int saved_errno = errno;
    497     result = MakeIOError(
    498         filename_, strerror(saved_errno), kWritableFileFlush, saved_errno);
    499     uma_logger_->RecordOSError(kWritableFileFlush, saved_errno);
    500   }
    501   return result;
    502 }
    503 
    504 Status ChromiumWritableFile::Sync() {
    505   TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
    506   Status result;
    507   int error = 0;
    508 
    509   if (HANDLE_EINTR(fflush_wrapper(file_)))
    510     error = errno;
    511   // Sync even if fflush gave an error; perhaps the data actually got out,
    512   // even though something went wrong.
    513   if (fdatasync(fileno(file_)) && !error)
    514     error = errno;
    515   // Report the first error we found.
    516   if (error) {
    517     result = MakeIOError(filename_, strerror(error), kWritableFileSync, error);
    518     uma_logger_->RecordErrorAt(kWritableFileSync);
    519   }
    520   return result;
    521 }
    522 
    523 ChromiumEnv::ChromiumEnv()
    524     : name_("LevelDBEnv"),
    525       bgsignal_(&mu_),
    526       started_bgthread_(false),
    527       kMaxRetryTimeMillis(1000) {
    528 }
    529 
    530 ChromiumEnv::~ChromiumEnv() {
    531   // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
    532   // ensure that behavior isn't accidentally changed, but there's an instance in
    533   // a unit test that is deleted.
    534 }
    535 
    536 Status ChromiumEnv::NewSequentialFile(const std::string& fname,
    537                                       SequentialFile** result) {
    538   FILE* f = fopen_internal(fname.c_str(), "rb");
    539   if (f == NULL) {
    540     *result = NULL;
    541     int saved_errno = errno;
    542     RecordOSError(kNewSequentialFile, saved_errno);
    543     return MakeIOError(
    544         fname, strerror(saved_errno), kNewSequentialFile, saved_errno);
    545   } else {
    546     *result = new ChromiumSequentialFile(fname, f, this);
    547     return Status::OK();
    548   }
    549 }
    550 
    551 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
    552 #if defined(OS_POSIX)
    553   struct rlimit nofile;
    554   if (getrlimit(RLIMIT_NOFILE, &nofile))
    555     return;
    556   GetMaxFDHistogram(type)->Add(nofile.rlim_cur);
    557 #endif
    558 }
    559 
    560 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
    561                                         RandomAccessFile** result) {
    562   int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
    563   bool created;
    564   ::base::PlatformFileError error_code;
    565   ::base::PlatformFile file = ::base::CreatePlatformFile(
    566       CreateFilePath(fname), flags, &created, &error_code);
    567   if (error_code == ::base::PLATFORM_FILE_OK) {
    568     *result = new ChromiumRandomAccessFile(fname, file, this);
    569     RecordOpenFilesLimit("Success");
    570     return Status::OK();
    571   }
    572   if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED)
    573     RecordOpenFilesLimit("TooManyOpened");
    574   else
    575     RecordOpenFilesLimit("OtherError");
    576   *result = NULL;
    577   RecordOSError(kNewRandomAccessFile, error_code);
    578   return MakeIOError(fname,
    579                      PlatformFileErrorString(error_code),
    580                      kNewRandomAccessFile,
    581                      error_code);
    582 }
    583 
    584 Status ChromiumEnv::NewWritableFile(const std::string& fname,
    585                                     WritableFile** result) {
    586   *result = NULL;
    587   FILE* f = fopen_internal(fname.c_str(), "wb");
    588   if (f == NULL) {
    589     int saved_errno = errno;
    590     RecordErrorAt(kNewWritableFile);
    591     return MakeIOError(
    592         fname, strerror(saved_errno), kNewWritableFile, saved_errno);
    593   } else {
    594     *result = new ChromiumWritableFile(fname, f, this, this);
    595     return Status::OK();
    596   }
    597 }
    598 
    599 bool ChromiumEnv::FileExists(const std::string& fname) {
    600   return ::base::PathExists(CreateFilePath(fname));
    601 }
    602 
    603 Status ChromiumEnv::GetChildren(const std::string& dir,
    604                                 std::vector<std::string>* result) {
    605   result->clear();
    606   base::FileEnumerator iter(
    607       CreateFilePath(dir), false, base::FileEnumerator::FILES);
    608   base::FilePath current = iter.Next();
    609   while (!current.empty()) {
    610     result->push_back(FilePathToString(current.BaseName()));
    611     current = iter.Next();
    612   }
    613   // TODO(jorlow): Unfortunately, the FileEnumerator swallows errors, so
    614   //               we'll always return OK. Maybe manually check for error
    615   //               conditions like the file not existing?
    616   return Status::OK();
    617 }
    618 
    619 Status ChromiumEnv::DeleteFile(const std::string& fname) {
    620   Status result;
    621   // TODO(jorlow): Should we assert this is a file?
    622   if (!::base::DeleteFile(CreateFilePath(fname), false)) {
    623     result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
    624     RecordErrorAt(kDeleteFile);
    625   }
    626   return result;
    627 }
    628 
    629 Status ChromiumEnv::CreateDir(const std::string& name) {
    630   Status result;
    631   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    632   Retrier retrier(kCreateDir, this);
    633   do {
    634     if (::file_util::CreateDirectoryAndGetError(CreateFilePath(name), &error))
    635       return result;
    636   } while (retrier.ShouldKeepTrying(error));
    637   result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
    638   RecordOSError(kCreateDir, error);
    639   return result;
    640 }
    641 
    642 Status ChromiumEnv::DeleteDir(const std::string& name) {
    643   Status result;
    644   // TODO(jorlow): Should we assert this is a directory?
    645   if (!::base::DeleteFile(CreateFilePath(name), false)) {
    646     result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
    647     RecordErrorAt(kDeleteDir);
    648   }
    649   return result;
    650 }
    651 
    652 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
    653   Status s;
    654   int64_t signed_size;
    655   if (!::file_util::GetFileSize(CreateFilePath(fname), &signed_size)) {
    656     *size = 0;
    657     s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
    658     RecordErrorAt(kGetFileSize);
    659   } else {
    660     *size = static_cast<uint64_t>(signed_size);
    661   }
    662   return s;
    663 }
    664 
    665 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
    666   Status result;
    667   base::FilePath src_file_path = CreateFilePath(src);
    668   if (!::base::PathExists(src_file_path))
    669     return result;
    670   base::FilePath destination = CreateFilePath(dst);
    671 
    672   Retrier retrier(kRenameFile, this);
    673   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    674   do {
    675     if (base::ReplaceFile(src_file_path, destination, &error))
    676       return result;
    677   } while (retrier.ShouldKeepTrying(error));
    678 
    679   DCHECK(error != base::PLATFORM_FILE_OK);
    680   RecordOSError(kRenameFile, error);
    681   char buf[100];
    682   snprintf(buf,
    683            sizeof(buf),
    684            "Could not rename file: %s",
    685            PlatformFileErrorString(error));
    686   return MakeIOError(src, buf, kRenameFile, error);
    687 }
    688 
    689 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
    690   *lock = NULL;
    691   Status result;
    692   int flags = ::base::PLATFORM_FILE_OPEN_ALWAYS |
    693               ::base::PLATFORM_FILE_READ |
    694               ::base::PLATFORM_FILE_WRITE |
    695               ::base::PLATFORM_FILE_EXCLUSIVE_READ |
    696               ::base::PLATFORM_FILE_EXCLUSIVE_WRITE;
    697   bool created;
    698   ::base::PlatformFileError error_code;
    699   ::base::PlatformFile file;
    700   Retrier retrier(kLockFile, this);
    701   do {
    702     file = ::base::CreatePlatformFile(
    703         CreateFilePath(fname), flags, &created, &error_code);
    704   } while (error_code != ::base::PLATFORM_FILE_OK &&
    705            retrier.ShouldKeepTrying(error_code));
    706 
    707   if (error_code == ::base::PLATFORM_FILE_ERROR_NOT_FOUND) {
    708     ::base::FilePath parent = CreateFilePath(fname).DirName();
    709     ::base::FilePath last_parent;
    710     int num_missing_ancestors = 0;
    711     do {
    712       if (base::DirectoryExists(parent))
    713         break;
    714       ++num_missing_ancestors;
    715       last_parent = parent;
    716       parent = parent.DirName();
    717     } while (parent != last_parent);
    718     RecordLockFileAncestors(num_missing_ancestors);
    719   }
    720 
    721   if (error_code != ::base::PLATFORM_FILE_OK) {
    722     result = MakeIOError(
    723         fname, PlatformFileErrorString(error_code), kLockFile, error_code);
    724     RecordOSError(kLockFile, error_code);
    725   } else {
    726     ChromiumFileLock* my_lock = new ChromiumFileLock;
    727     my_lock->file_ = file;
    728     *lock = my_lock;
    729   }
    730   return result;
    731 }
    732 
    733 Status ChromiumEnv::UnlockFile(FileLock* lock) {
    734   ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
    735   Status result;
    736   if (!::base::ClosePlatformFile(my_lock->file_)) {
    737     result = MakeIOError("Could not close lock file.", "", kUnlockFile);
    738     RecordErrorAt(kUnlockFile);
    739   }
    740   delete my_lock;
    741   return result;
    742 }
    743 
    744 Status ChromiumEnv::GetTestDirectory(std::string* path) {
    745   mu_.Acquire();
    746   if (test_directory_.empty()) {
    747     if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
    748                                              &test_directory_)) {
    749       mu_.Release();
    750       RecordErrorAt(kGetTestDirectory);
    751       return MakeIOError(
    752           "Could not create temp directory.", "", kGetTestDirectory);
    753     }
    754   }
    755   *path = FilePathToString(test_directory_);
    756   mu_.Release();
    757   return Status::OK();
    758 }
    759 
    760 Status ChromiumEnv::NewLogger(const std::string& fname, Logger** result) {
    761   FILE* f = fopen_internal(fname.c_str(), "w");
    762   if (f == NULL) {
    763     *result = NULL;
    764     int saved_errno = errno;
    765     RecordOSError(kNewLogger, saved_errno);
    766     return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
    767   } else {
    768     *result = new ChromiumLogger(f);
    769     return Status::OK();
    770   }
    771 }
    772 
    773 uint64_t ChromiumEnv::NowMicros() {
    774   return ::base::TimeTicks::Now().ToInternalValue();
    775 }
    776 
    777 void ChromiumEnv::SleepForMicroseconds(int micros) {
    778   // Round up to the next millisecond.
    779   ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
    780 }
    781 
    782 void ChromiumEnv::RecordErrorAt(MethodID method) const {
    783   GetMethodIOErrorHistogram()->Add(method);
    784 }
    785 
    786 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
    787   GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
    788 }
    789 
    790 void ChromiumEnv::RecordOSError(MethodID method,
    791                                 base::PlatformFileError error) const {
    792   DCHECK(error < 0);
    793   RecordErrorAt(method);
    794   GetOSErrorHistogram(method, -base::PLATFORM_FILE_ERROR_MAX)->Add(-error);
    795 }
    796 
    797 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
    798   DCHECK(error > 0);
    799   RecordErrorAt(method);
    800   GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
    801 }
    802 
    803 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
    804                                                       int limit) const {
    805   std::string uma_name(name_);
    806   // TODO(dgrogan): This is probably not the best way to concatenate strings.
    807   uma_name.append(".IOError.").append(MethodIDToString(method));
    808   return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
    809       base::Histogram::kUmaTargetedHistogramFlag);
    810 }
    811 
    812 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
    813   std::string uma_name(name_);
    814   uma_name.append(".IOError");
    815   return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
    816       kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
    817 }
    818 
    819 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
    820     const std::string& type) const {
    821   std::string uma_name(name_);
    822   uma_name.append(".MaxFDs.").append(type);
    823   // These numbers make each bucket twice as large as the previous bucket.
    824   const int kFirstEntry = 1;
    825   const int kLastEntry = 65536;
    826   const int kNumBuckets = 18;
    827   return base::Histogram::FactoryGet(
    828       uma_name, kFirstEntry, kLastEntry, kNumBuckets,
    829       base::Histogram::kUmaTargetedHistogramFlag);
    830 }
    831 
    832 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
    833   std::string uma_name(name_);
    834   uma_name.append(".LockFileAncestorsNotFound");
    835   const int kMin = 1;
    836   const int kMax = 10;
    837   const int kNumBuckets = 11;
    838   return base::LinearHistogram::FactoryGet(
    839       uma_name, kMin, kMax, kNumBuckets,
    840       base::Histogram::kUmaTargetedHistogramFlag);
    841 }
    842 
    843 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
    844   std::string uma_name(name_);
    845   // TODO(dgrogan): This is probably not the best way to concatenate strings.
    846   uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
    847 
    848   const int kBucketSizeMillis = 25;
    849   // Add 2, 1 for each of the buckets <1 and >max.
    850   const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
    851   return base::Histogram::FactoryTimeGet(
    852       uma_name, base::TimeDelta::FromMilliseconds(1),
    853       base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
    854       kNumBuckets,
    855       base::Histogram::kUmaTargetedHistogramFlag);
    856 }
    857 
    858 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
    859     MethodID method) const {
    860   std::string uma_name(name_);
    861   uma_name.append(".RetryRecoveredFromErrorIn")
    862       .append(MethodIDToString(method));
    863   return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
    864       kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
    865 }
    866 
    867 class Thread : public ::base::PlatformThread::Delegate {
    868  public:
    869   Thread(void (*function)(void* arg), void* arg)
    870       : function_(function), arg_(arg) {
    871     ::base::PlatformThreadHandle handle;
    872     bool success = ::base::PlatformThread::Create(0, this, &handle);
    873     DCHECK(success);
    874   }
    875   virtual ~Thread() {}
    876   virtual void ThreadMain() {
    877     (*function_)(arg_);
    878     delete this;
    879   }
    880 
    881  private:
    882   void (*function_)(void* arg);
    883   void* arg_;
    884 };
    885 
    886 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
    887   mu_.Acquire();
    888 
    889   // Start background thread if necessary
    890   if (!started_bgthread_) {
    891     started_bgthread_ = true;
    892     StartThread(&ChromiumEnv::BGThreadWrapper, this);
    893   }
    894 
    895   // If the queue is currently empty, the background thread may currently be
    896   // waiting.
    897   if (queue_.empty()) {
    898     bgsignal_.Signal();
    899   }
    900 
    901   // Add to priority queue
    902   queue_.push_back(BGItem());
    903   queue_.back().function = function;
    904   queue_.back().arg = arg;
    905 
    906   mu_.Release();
    907 }
    908 
    909 void ChromiumEnv::BGThread() {
    910   base::PlatformThread::SetName(name_.c_str());
    911 
    912   while (true) {
    913     // Wait until there is an item that is ready to run
    914     mu_.Acquire();
    915     while (queue_.empty()) {
    916       bgsignal_.Wait();
    917     }
    918 
    919     void (*function)(void*) = queue_.front().function;
    920     void* arg = queue_.front().arg;
    921     queue_.pop_front();
    922 
    923     mu_.Release();
    924     TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
    925     (*function)(arg);
    926   }
    927 }
    928 
    929 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
    930   new Thread(function, arg); // Will self-delete.
    931 }
    932 
    933 static std::string GetDirName(const std::string& filename) {
    934   base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
    935   return FilePathToString(file.DirName());
    936 }
    937 
    938 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
    939   base::AutoLock auto_lock(map_lock_);
    940   needs_sync_map_[GetDirName(filename)] = true;
    941 }
    942 
    943 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
    944   base::AutoLock auto_lock(map_lock_);
    945   return needs_sync_map_.find(GetDirName(filename)) != needs_sync_map_.end();
    946 }
    947 
    948 void ChromiumEnv::DidSyncDir(const std::string& filename) {
    949   base::AutoLock auto_lock(map_lock_);
    950   needs_sync_map_.erase(GetDirName(filename));
    951 }
    952 
    953 }  // namespace leveldb_env
    954 
    955 namespace leveldb {
    956 
    957 Env* IDBEnv() {
    958   return leveldb_env::idb_env.Pointer();
    959 }
    960 
    961 Env* Env::Default() {
    962   return leveldb_env::default_env.Pointer();
    963 }
    964 
    965 }  // namespace leveldb
    966 
    967