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
45using namespace leveldb;
46
47namespace leveldb_env {
48
49namespace {
50
51#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
52    defined(OS_OPENBSD)
53// The following are glibc-specific
54
55size_t fread_wrapper(void *ptr, size_t size, size_t n, FILE *file) {
56  return fread(ptr, size, n, file);
57}
58
59size_t fwrite_wrapper(const void *ptr, size_t size, size_t n, FILE *file) {
60  return fwrite(ptr, size, n, file);
61}
62
63int fflush_wrapper(FILE *file) {
64  return fflush(file);
65}
66
67#if !defined(OS_ANDROID)
68int 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
79class 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
98size_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
103size_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
108int fflush_wrapper(FILE *file) {
109  TryToLockFILE lock(file);
110  return fflush_unlocked(file);
111}
112
113#endif
114
115// Wide-char safe fopen wrapper.
116FILE* 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
124base::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
132static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
133    = FILE_PATH_LITERAL("leveldb-test-");
134
135const 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
178class 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
217class 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
244class ChromiumFileLock : public FileLock {
245 public:
246  ::base::PlatformFile file_;
247};
248
249class 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
293class 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
305const 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
355Status 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
370Status 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
386Status 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
397ErrorParsingResult 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
419std::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
427ChromiumWritableFile::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
441ChromiumWritableFile::~ChromiumWritableFile() {
442  if (file_ != NULL) {
443    // Ignoring any potential errors
444    fclose(file_);
445  }
446}
447
448Status 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
465Status 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
483Status 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
493Status 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
504Status 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
523ChromiumEnv::ChromiumEnv()
524    : name_("LevelDBEnv"),
525      bgsignal_(&mu_),
526      started_bgthread_(false),
527      kMaxRetryTimeMillis(1000) {
528}
529
530ChromiumEnv::~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
536Status 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
551void 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
560Status 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
584Status 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
599bool ChromiumEnv::FileExists(const std::string& fname) {
600  return ::base::PathExists(CreateFilePath(fname));
601}
602
603Status 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
619Status 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
629Status 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
642Status 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
652Status 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
665Status 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
689Status 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
733Status 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
744Status 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
760Status 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
773uint64_t ChromiumEnv::NowMicros() {
774  return ::base::TimeTicks::Now().ToInternalValue();
775}
776
777void ChromiumEnv::SleepForMicroseconds(int micros) {
778  // Round up to the next millisecond.
779  ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
780}
781
782void ChromiumEnv::RecordErrorAt(MethodID method) const {
783  GetMethodIOErrorHistogram()->Add(method);
784}
785
786void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
787  GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
788}
789
790void 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
797void ChromiumEnv::RecordOSError(MethodID method, int error) const {
798  DCHECK(error > 0);
799  RecordErrorAt(method);
800  GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
801}
802
803base::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
812base::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
819base::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
832base::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
843base::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
858base::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
867class 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
886void 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
909void 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
929void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
930  new Thread(function, arg); // Will self-delete.
931}
932
933static std::string GetDirName(const std::string& filename) {
934  base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
935  return FilePathToString(file.DirName());
936}
937
938void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
939  base::AutoLock auto_lock(map_lock_);
940  needs_sync_map_[GetDirName(filename)] = true;
941}
942
943bool 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
948void 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
955namespace leveldb {
956
957Env* IDBEnv() {
958  return leveldb_env::idb_env.Pointer();
959}
960
961Env* Env::Default() {
962  return leveldb_env::default_env.Pointer();
963}
964
965}  // namespace leveldb
966
967