Merge upstream changes into Ledger's version of leveldb.

Updates our version of leveldb and makes necessary changes to BUILD.gn
to make it build.

LE-523 That starts the transition, but doesn't include changes to our
implementation of env.h

Change-Id: I27b2163aa96e7eef16ca90a64ca3f89942e1428f
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000..ca06958
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,282 @@
+# Copyright 2016 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/package.gni")
+
+package("leveldb_tests") {
+  testonly = true
+  deprecated_system_image = true
+
+  deps = [
+    ":leveldb_tests_bin",
+  ]
+
+  binaries = [ {
+        name = "leveldb_tests"
+      } ]
+}
+
+defines = [
+  "LEVELDB_PLATFORM_POSIX=1",
+  "LEVELDB_IS_BIG_ENDIAN=0",
+  "HAVE_SNAPPY=1",
+  "HAVE_CRC32C=0",
+]
+
+config("leveldb_config") {
+  include_dirs = [
+    ".",
+    "include",
+  ]
+}
+
+config("leveldb_warnings_config") {
+  cflags = [
+    "-Wall",
+    "-Werror",
+    "-Wno-sign-compare",
+    "-Wno-unused-variable",
+  ]
+}
+
+static_library("leveldb") {
+  sources = [
+    "db/builder.cc",
+    "db/builder.h",
+    "db/db_impl.cc",
+    "db/db_impl.h",
+    "db/db_iter.cc",
+    "db/db_iter.h",
+    "db/dbformat.cc",
+    "db/dbformat.h",
+    "db/dumpfile.cc",
+    "db/filename.cc",
+    "db/filename.h",
+    "db/log_format.h",
+    "db/log_reader.cc",
+    "db/log_reader.h",
+    "db/log_writer.cc",
+    "db/log_writer.h",
+    "db/memtable.cc",
+    "db/memtable.h",
+    "db/repair.cc",
+    "db/skiplist.h",
+    "db/snapshot.h",
+    "db/table_cache.cc",
+    "db/table_cache.h",
+    "db/version_edit.cc",
+    "db/version_edit.h",
+    "db/version_set.cc",
+    "db/version_set.h",
+    "db/write_batch.cc",
+    "db/write_batch_internal.h",
+    "helpers/memenv/memenv.cc",
+    "helpers/memenv/memenv.h",
+    "include/leveldb/cache.h",
+    "include/leveldb/comparator.h",
+    "include/leveldb/db.h",
+    "include/leveldb/dumpfile.h",
+    "include/leveldb/env.h",
+    "include/leveldb/export.h",
+    "include/leveldb/filter_policy.h",
+    "include/leveldb/iterator.h",
+    "include/leveldb/options.h",
+    "include/leveldb/slice.h",
+    "include/leveldb/status.h",
+    "include/leveldb/table.h",
+    "include/leveldb/table_builder.h",
+    "include/leveldb/write_batch.h",
+    "port/port.h",
+    "port/port_stdxx.h",
+    "table/block.cc",
+    "table/block.h",
+    "table/block_builder.cc",
+    "table/block_builder.h",
+    "table/filter_block.cc",
+    "table/filter_block.h",
+    "table/format.cc",
+    "table/format.h",
+    "table/iterator.cc",
+    "table/iterator_wrapper.h",
+    "table/merger.cc",
+    "table/merger.h",
+    "table/table.cc",
+    "table/table_builder.cc",
+    "table/two_level_iterator.cc",
+    "table/two_level_iterator.h",
+    "util/arena.cc",
+    "util/arena.h",
+    "util/bloom.cc",
+    "util/cache.cc",
+    "util/coding.cc",
+    "util/coding.h",
+    "util/comparator.cc",
+    "util/crc32c.cc",
+    "util/crc32c.h",
+    "util/env.cc",
+    "util/env_fuchsia.cc",
+    "util/env_fuchsia.h",
+    "util/filter_policy.cc",
+    "util/hash.cc",
+    "util/hash.h",
+    "util/logging.cc",
+    "util/logging.h",
+    "util/mutexlock.h",
+    "util/options.cc",
+    "util/random.h",
+    "util/status.cc",
+  ]
+
+  public_deps = [
+    "//third_party/re2",
+    "//third_party/snappy",
+  ]
+
+  public_configs = [ ":leveldb_config" ]
+
+  configs -= [ "//build/config:default_warnings" ]
+  configs += [ ":leveldb_warnings_config" ]
+}
+
+source_set("leveldb_testutil") {
+  sources = [
+    "util/histogram.cc",
+    "util/histogram.h",
+    "util/testharness.cc",
+    "util/testharness.h",
+    "util/testutil.cc",
+    "util/testutil.h",
+  ]
+
+  public_deps = [
+    ":leveldb",
+  ]
+
+  configs -= [ "//build/config:default_warnings" ]
+  configs += [ ":leveldb_warnings_config" ]
+}
+
+# These leveldb *_test.cc files all define their own identical main function which
+# just calls the same RunAllTests helper. Having this many different binaries is
+# really difficult to deal with in our setup, so we glob them into one binary
+# called leveldb_tests. To do this we need to give each file a different name
+# for its main symbol which we'll ignore. We do this with the magic and horror
+# of a GN template to generate distinct defines for each file.
+
+template("leveldb_test_source_wrapper") {
+  assert(defined(invoker.test), "Must define test")
+
+  source_set(target_name) {
+    testonly = true
+
+    sources = [
+      invoker.test,
+    ]
+
+    deps = [
+      ":leveldb_testutil",
+      "//third_party/googletest:gtest",
+    ]
+
+    cflags = [ "-Dmain=fake_main_${target_name}" ]
+
+    configs -= [ "//build/config:default_warnings" ]
+    configs += [ ":leveldb_warnings_config" ]
+  }
+}
+
+leveldb_test_source_wrapper("corruption_test") {
+  test = "db/corruption_test.cc"
+}
+leveldb_test_source_wrapper("dbformat_test") {
+  test = "db/dbformat_test.cc"
+}
+leveldb_test_source_wrapper("filename_test") {
+  test = "db/filename_test.cc"
+}
+leveldb_test_source_wrapper("log_test") {
+  test = "db/log_test.cc"
+}
+leveldb_test_source_wrapper("skiplist_test") {
+  test = "db/skiplist_test.cc"
+}
+leveldb_test_source_wrapper("version_edit_test") {
+  test = "db/version_edit_test.cc"
+}
+leveldb_test_source_wrapper("write_batch_test") {
+  test = "db/write_batch_test.cc"
+}
+leveldb_test_source_wrapper("filter_block_test") {
+  test = "table/filter_block_test.cc"
+}
+leveldb_test_source_wrapper("table_test") {
+  test = "table/table_test.cc"
+}
+leveldb_test_source_wrapper("arena_test") {
+  test = "util/arena_test.cc"
+}
+leveldb_test_source_wrapper("bloom_test") {
+  test = "util/bloom_test.cc"
+}
+leveldb_test_source_wrapper("cache_test") {
+  test = "util/cache_test.cc"
+}
+leveldb_test_source_wrapper("crc32c_test") {
+  test = "util/crc32c_test.cc"
+}
+
+
+group("leveldb_test_sources") {
+  testonly = true
+
+  deps = [
+    ":arena_test",
+    ":bloom_test",
+    ":cache_test",
+    ":corruption_test",
+    ":crc32c_test",
+    ":dbformat_test",
+    ":filename_test",
+    ":filter_block_test",
+    ":log_test",
+    ":skiplist_test",
+    ":table_test",
+    ":version_edit_test",
+    ":write_batch_test",
+  ]
+}
+
+executable("leveldb_tests_bin") {
+  output_name = "leveldb_tests"
+  testonly = true
+
+  sources = [
+    # Use the actual main from env_test.cc (arbitrarily chosen).
+    # The rest of the test sources come from :leveldb_test_sources
+    "util/env_test.cc",
+  ]
+
+  deps = [
+    ":leveldb_test_sources",
+    ":leveldb_testutil",
+    "//third_party/googletest:gtest",
+  ]
+
+  configs -= [ "//build/config:default_warnings" ]
+  configs += [ ":leveldb_warnings_config" ]
+}
+
+executable("leveldb_db_bench") {
+  testonly = true
+
+  sources = [
+    "db/db_bench.cc",
+  ]
+
+  deps = [
+    ":leveldb_testutil",
+  ]
+}
+
+# TODO: Figure out "db/db_test.cc",
diff --git a/README.fuchsia b/README.fuchsia
new file mode 100644
index 0000000..a46c5bb
--- /dev/null
+++ b/README.fuchsia
@@ -0,0 +1,9 @@
+Name: LevelDB
+License: BSD
+License File: LICENSE
+Upstream Git: https://github.com/google/leveldb
+Description:
+
+LevelDB is a fast key-value storage library written at Google that
+provides an ordered mapping from string keys to string values.
+
diff --git a/db/write_batch_test.cc b/db/write_batch_test.cc
index 8d38023..a9cefff 100644
--- a/db/write_batch_test.cc
+++ b/db/write_batch_test.cc
@@ -54,7 +54,7 @@
 
 class WriteBatchTest { };
 
-TEST(WriteBatchTest, Empty) {
+TEST(WriteBatchTest, WriteBatchEmpty) {
   WriteBatch batch;
   ASSERT_EQ("", PrintContents(&batch));
   ASSERT_EQ(0, WriteBatchInternal::Count(&batch));
diff --git a/table/table_test.cc b/table/table_test.cc
index e47db3d..e8be2cc 100644
--- a/table/table_test.cc
+++ b/table/table_test.cc
@@ -645,7 +645,7 @@
 };
 
 // Test empty table/block.
-TEST(Harness, Empty) {
+TEST(Harness, HarnessEmpty) {
   for (int i = 0; i < kNumTestArgs; i++) {
     Init(kTestArgList[i]);
     Random rnd(test::RandomSeed() + 1);
diff --git a/util/arena_test.cc b/util/arena_test.cc
index 58e870e..7842fd9 100644
--- a/util/arena_test.cc
+++ b/util/arena_test.cc
@@ -11,11 +11,11 @@
 
 class ArenaTest { };
 
-TEST(ArenaTest, Empty) {
+TEST(ArenaTest, ArenaTestEmpty) {
   Arena arena;
 }
 
-TEST(ArenaTest, Simple) {
+TEST(ArenaTest, ArenaTestSimple) {
   std::vector<std::pair<size_t, char*> > allocated;
   Arena arena;
   const int N = 100000;
diff --git a/util/env_fuchsia.cc b/util/env_fuchsia.cc
new file mode 100644
index 0000000..138e720
--- /dev/null
+++ b/util/env_fuchsia.cc
@@ -0,0 +1,642 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <deque>
+#include <set>
+#include "leveldb/env.h"
+#include "leveldb/slice.h"
+#include "port/port.h"
+#include "util/logging.h"
+#include "util/mutexlock.h"
+
+namespace leveldb {
+
+namespace {
+
+// Equivalent modes for open to fopen "r", "w" and "a" modes respectively.
+constexpr int kReadMode = O_RDONLY;
+constexpr int kWriteMode = O_WRONLY | O_CREAT | O_TRUNC;
+constexpr int kAppendMode = O_WRONLY | O_CREAT | O_APPEND;
+
+DIR* OpenDirAt(int root_fd, const char* path) {
+  int fd = openat(root_fd, path, kReadMode);
+  if (fd < 0) {
+    return nullptr;
+  }
+  return fdopendir(fd);
+}
+
+FILE* FOpenAt(int root_fd, const char*  path, int mode) {
+  const char* fopen_mode = nullptr;
+  switch (mode) {
+    case kReadMode:
+      fopen_mode = "r";
+      break;
+    case kWriteMode:
+      fopen_mode = "w";
+      break;
+    case kAppendMode:
+      fopen_mode = "a";
+      break;
+    default:
+      abort();
+  }
+  int fd = openat(root_fd, path, mode);
+  if (fd < 0) {
+    return nullptr;
+  }
+  return fdopen(fd, fopen_mode);
+}
+
+class FuchsiaLogger : public Logger {
+ private:
+  FILE* file_;
+  uint64_t (*gettid_)();  // Return the thread id for the current thread
+ public:
+  FuchsiaLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { }
+  virtual ~FuchsiaLogger() {
+    fclose(file_);
+  }
+  virtual void Logv(const char* format, va_list ap) {
+    const uint64_t thread_id = (*gettid_)();
+
+    // We try twice: the first time with a fixed-size stack allocated buffer,
+    // and the second time with a much larger dynamically allocated buffer.
+    char buffer[500];
+    for (int iter = 0; iter < 2; iter++) {
+      char* base;
+      int bufsize;
+      if (iter == 0) {
+        bufsize = sizeof(buffer);
+        base = buffer;
+      } else {
+        bufsize = 30000;
+        base = new char[bufsize];
+      }
+      char* p = base;
+      char* limit = base + bufsize;
+
+      struct timeval now_tv;
+      gettimeofday(&now_tv, NULL);
+      p += snprintf(p, limit - p,
+                    "%06d %llx ",
+                    static_cast<int>(now_tv.tv_usec),
+                    static_cast<long long unsigned int>(thread_id));
+
+      // Print the message
+      if (p < limit) {
+        va_list backup_ap;
+        va_copy(backup_ap, ap);
+        p += vsnprintf(p, limit - p, format, backup_ap);
+        va_end(backup_ap);
+      }
+
+      // Truncate to available space if necessary
+      if (p >= limit) {
+        if (iter == 0) {
+          continue;       // Try again with larger buffer
+        } else {
+          p = limit - 1;
+        }
+      }
+
+      // Add newline if necessary
+      if (p == base || p[-1] != '\n') {
+        *p++ = '\n';
+      }
+
+      assert(p <= limit);
+      fwrite(base, 1, p - base, file_);
+      fflush(file_);
+      if (base != buffer) {
+        delete[] base;
+      }
+      break;
+    }
+  }
+};
+
+static Status IOError(const std::string& context, int err_number) {
+  return Status::IOError(context, strerror(err_number));
+}
+
+class FuchsiaSequentialFile: public SequentialFile {
+ private:
+  std::string filename_;
+  FILE* file_;
+
+ public:
+  FuchsiaSequentialFile(const std::string& fname, FILE* f)
+      : filename_(fname), file_(f) { }
+  virtual ~FuchsiaSequentialFile() { fclose(file_); }
+
+  virtual Status Read(size_t n, Slice* result, char* scratch) {
+    Status s;
+    size_t r = fread_unlocked(scratch, 1, n, file_);
+    *result = Slice(scratch, r);
+    if (r < n) {
+      if (feof(file_)) {
+        // We leave status as ok if we hit the end of the file
+      } else {
+        // A partial read with an error: return a non-ok status
+        s = IOError(filename_, errno);
+      }
+    }
+    return s;
+  }
+
+  virtual Status Skip(uint64_t n) {
+    if (fseek(file_, n, SEEK_CUR)) {
+      return IOError(filename_, errno);
+    }
+    return Status::OK();
+  }
+};
+
+// This is typically less efficient that mmap-ing the file, but file-backed mmap
+// is not currently available on Fuchsia.
+// TODO(ppi): when mmaping files is possible of Fuchsia, bring in mmap-based
+// impl.
+class FuchsiaRandomAccessFile: public RandomAccessFile {
+ private:
+  std::string filename_;
+  int fd_;
+
+ public:
+  FuchsiaRandomAccessFile(const std::string& fname, int fd)
+      : filename_(fname), fd_(fd) { }
+  virtual ~FuchsiaRandomAccessFile() { close(fd_); }
+
+  virtual Status Read(uint64_t offset, size_t n, Slice* result,
+                      char* scratch) const {
+    Status s;
+    ssize_t r = pread(fd_, scratch, n, static_cast<off_t>(offset));
+    *result = Slice(scratch, (r < 0) ? 0 : r);
+    if (r < 0) {
+      // An error: return a non-ok status
+      s = IOError(filename_, errno);
+    }
+    return s;
+  }
+};
+
+class FuchsiaWritableFile : public WritableFile {
+ private:
+  int root_fd_;
+  std::string filename_;
+  FILE* file_;
+
+ public:
+  FuchsiaWritableFile(int root_fd, const std::string& fname, FILE* f)
+      : root_fd_(root_fd), filename_(fname), file_(f) { }
+
+  ~FuchsiaWritableFile() {
+    if (file_ != NULL) {
+      // Ignoring any potential errors
+      fclose(file_);
+    }
+  }
+
+  virtual Status Append(const Slice& data) {
+    size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);
+    if (r != data.size()) {
+      return IOError(filename_, errno);
+    }
+    return Status::OK();
+  }
+
+  virtual Status Close() {
+    Status result;
+    if (fclose(file_) != 0) {
+      result = IOError(filename_, errno);
+    }
+    file_ = NULL;
+    return result;
+  }
+
+  virtual Status Flush() {
+    if (fflush_unlocked(file_) != 0) {
+      return IOError(filename_, errno);
+    }
+    return Status::OK();
+  }
+
+  Status SyncDirIfManifest() {
+    const char* f = filename_.c_str();
+    const char* sep = strrchr(f, '/');
+    Slice basename;
+    std::string dir;
+    if (sep == NULL) {
+      dir = ".";
+      basename = f;
+    } else {
+      dir = std::string(f, sep - f);
+      basename = sep + 1;
+    }
+    Status s;
+    if (basename.starts_with("MANIFEST")) {
+      int fd = openat(root_fd_, dir.c_str(), kReadMode);
+      if (fd < 0) {
+        s = IOError(dir, errno);
+      } else {
+        if (fsync(fd) < 0) {
+          s = IOError(dir, errno);
+        }
+        close(fd);
+      }
+    }
+    return s;
+  }
+
+  virtual Status Sync() {
+    // Ensure new files referred to by the manifest are in the filesystem.
+    Status s = SyncDirIfManifest();
+    if (!s.ok()) {
+      return s;
+    }
+    if (fflush_unlocked(file_) != 0 ||
+        fdatasync(fileno(file_)) != 0) {
+      s = Status::IOError(filename_, strerror(errno));
+    }
+    return s;
+  }
+};
+
+class FuchsiaFileLock : public FileLock {
+ public:
+  std::string name_;
+};
+
+// Set of locked files. This is used to guard against multiple uses from one
+// process.
+class LockTable {
+ private:
+  port::Mutex mu_;
+  std::set<std::string> locked_files_;
+ public:
+  bool Insert(const std::string& fname) {
+    MutexLock l(&mu_);
+    return locked_files_.insert(fname).second;
+  }
+  void Remove(const std::string& fname) {
+    MutexLock l(&mu_);
+    locked_files_.erase(fname);
+  }
+};
+
+class FuchsiaEnv : public Env {
+ public:
+  FuchsiaEnv(int root_fd);
+
+  virtual ~FuchsiaEnv() {
+    if (this == Env::Default()) {
+      char msg[] = "Destroying Env::Default()\n";
+      fwrite(msg, 1, sizeof(msg), stderr);
+      abort();
+    }
+  }
+
+  virtual Status NewSequentialFile(const std::string& fname,
+                                   SequentialFile** result) {
+    FILE* f = FOpenAt(root_fd_, fname.c_str(), kReadMode);
+    if (f == NULL) {
+      *result = NULL;
+      return IOError(fname, errno);
+    } else {
+      *result = new FuchsiaSequentialFile(fname, f);
+      return Status::OK();
+    }
+  }
+
+  virtual Status NewRandomAccessFile(const std::string& fname,
+                                     RandomAccessFile** result) {
+    *result = NULL;
+    Status s;
+    int fd = openat(root_fd_, fname.c_str(), kReadMode);
+    if (fd < 0) {
+      s = IOError(fname, errno);
+    } else {
+      *result = new FuchsiaRandomAccessFile(fname, fd);
+    }
+    return s;
+  }
+
+  virtual Status NewWritableFile(const std::string& fname,
+                                 WritableFile** result) {
+    Status s;
+    FILE* f = FOpenAt(root_fd_, fname.c_str(), kWriteMode);
+    if (f == NULL) {
+      *result = NULL;
+      s = IOError(fname, errno);
+    } else {
+      *result = new FuchsiaWritableFile(root_fd_, fname, f);
+    }
+    return s;
+  }
+
+  virtual Status NewAppendableFile(const std::string& fname,
+                                   WritableFile** result) {
+    Status s;
+    FILE* f = FOpenAt(root_fd_, fname.c_str(), kAppendMode);
+    if (f == NULL) {
+      *result = NULL;
+      s = IOError(fname, errno);
+    } else {
+      *result = new FuchsiaWritableFile(root_fd_, fname, f);
+    }
+    return s;
+  }
+
+  virtual bool FileExists(const std::string& fname) {
+    return faccessat(root_fd_, fname.c_str(), F_OK, 0) == 0;
+  }
+
+  virtual Status GetChildren(const std::string& dir,
+                             std::vector<std::string>* result) {
+    result->clear();
+    DIR* d = OpenDirAt(root_fd_, dir.c_str());
+    if (d == NULL) {
+      return IOError(dir, errno);
+    }
+    struct dirent* entry;
+    while ((entry = readdir(d)) != NULL) {
+      result->push_back(entry->d_name);
+    }
+    closedir(d);
+    return Status::OK();
+  }
+
+  virtual Status DeleteFile(const std::string& fname) {
+    Status result;
+    if (unlinkat(root_fd_, fname.c_str(), 0) != 0) {
+      result = IOError(fname, errno);
+    }
+    return result;
+  }
+
+  virtual Status CreateDir(const std::string& name) {
+    Status result;
+    if (mkdirat(root_fd_, name.c_str(), 0755) != 0) {
+      result = IOError(name, errno);
+    }
+    return result;
+  }
+
+  virtual Status DeleteDir(const std::string& name) {
+    Status result;
+    if (unlinkat(root_fd_, name.c_str(), AT_REMOVEDIR) != 0) {
+      result = IOError(name, errno);
+    }
+    return result;
+  }
+
+  virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
+    Status s;
+    struct stat sbuf;
+    if (fstatat(root_fd_, fname.c_str(), &sbuf, 0) != 0) {
+      *size = 0;
+      s = IOError(fname, errno);
+    } else {
+      *size = sbuf.st_size;
+    }
+    return s;
+  }
+
+  virtual Status RenameFile(const std::string& src, const std::string& target) {
+    Status result;
+    if (renameat(root_fd_, src.c_str(), root_fd_, target.c_str()) != 0) {
+      result = IOError(src, errno);
+    }
+    return result;
+  }
+
+  // Our implementation uses the |locks_| table to guard against creating
+  // multiple databases backed by the same file within one process. This does
+  // not guard against multiple processes using the same file concurrently.
+  virtual Status LockFile(const std::string& fname, FileLock** lock) {
+    *lock = NULL;
+    Status result;
+    if (!locks_.Insert(fname)) {
+      result = Status::IOError("lock " + fname, "already held by process");
+    } else {
+      FuchsiaFileLock* my_lock = new FuchsiaFileLock;
+      my_lock->name_ = fname;
+      *lock = my_lock;
+    }
+    return result;
+  }
+
+  virtual Status UnlockFile(FileLock* lock) {
+    FuchsiaFileLock* my_lock = reinterpret_cast<FuchsiaFileLock*>(lock);
+    Status result;
+    locks_.Remove(my_lock->name_);
+    delete my_lock;
+    return result;
+  }
+
+  virtual void Schedule(void (*function)(void*), void* arg);
+
+  virtual void StartThread(void (*function)(void* arg), void* arg);
+
+  virtual Status GetTestDirectory(std::string* result) {
+    const char* env = getenv("TEST_TMPDIR");
+    if (env && env[0] != '\0') {
+      *result = env;
+    } else {
+      char buf[100];
+      snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid()));
+      *result = buf;
+    }
+    // Directory may already exist
+    CreateDir(*result);
+    return Status::OK();
+  }
+
+  static uint64_t gettid() {
+    pthread_t tid = pthread_self();
+    uint64_t thread_id = 0;
+    memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid)));
+    return thread_id;
+  }
+
+  virtual Status NewLogger(const std::string& fname, Logger** result) {
+    FILE* f = FOpenAt(root_fd_, fname.c_str(), kWriteMode);
+    if (f == NULL) {
+      *result = NULL;
+      return IOError(fname, errno);
+    } else {
+      *result = new FuchsiaLogger(f, &FuchsiaEnv::gettid);
+      return Status::OK();
+    }
+  }
+
+  virtual uint64_t NowMicros() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
+  }
+
+  virtual void SleepForMicroseconds(int micros) {
+    usleep(micros);
+  }
+
+ private:
+  void PthreadCall(const char* label, int result) {
+    if (result != 0) {
+      fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
+      abort();
+    }
+  }
+
+  // BGThread() is the body of the background thread
+  void BGThread();
+  static void* BGThreadWrapper(void* arg) {
+    reinterpret_cast<FuchsiaEnv*>(arg)->BGThread();
+    return NULL;
+  }
+
+  int root_fd_;
+
+  pthread_mutex_t mu_;
+  pthread_cond_t bgsignal_;
+  pthread_t bgthread_;
+  bool started_bgthread_;
+
+  // Entry per Schedule() call
+  struct BGItem { void* arg; void (*function)(void*); };
+  typedef std::deque<BGItem> BGQueue;
+  BGQueue queue_;
+
+  LockTable locks_;
+};
+
+FuchsiaEnv::FuchsiaEnv(int root_fd) : root_fd_(root_fd), started_bgthread_(false) {
+  if (root_fd_ != AT_FDCWD && root_fd_ < 0) {
+    abort();
+  }
+  PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL));
+  PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL));
+}
+
+void FuchsiaEnv::Schedule(void (*function)(void*), void* arg) {
+  PthreadCall("lock", pthread_mutex_lock(&mu_));
+
+  // Start background thread if necessary
+  if (!started_bgthread_) {
+    started_bgthread_ = true;
+    PthreadCall(
+        "create thread",
+        pthread_create(&bgthread_, NULL,  &FuchsiaEnv::BGThreadWrapper, this));
+  }
+
+  // If the queue is currently empty, the background thread may currently be
+  // waiting.
+  if (queue_.empty()) {
+    PthreadCall("signal", pthread_cond_signal(&bgsignal_));
+  }
+
+  // Add to priority queue
+  queue_.push_back(BGItem());
+  queue_.back().function = function;
+  queue_.back().arg = arg;
+
+  PthreadCall("unlock", pthread_mutex_unlock(&mu_));
+}
+
+void FuchsiaEnv::BGThread() {
+  while (true) {
+    // Wait until there is an item that is ready to run
+    PthreadCall("lock", pthread_mutex_lock(&mu_));
+    while (queue_.empty()) {
+      PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_));
+    }
+
+    void (*function)(void*) = queue_.front().function;
+    void* arg = queue_.front().arg;
+    queue_.pop_front();
+
+    PthreadCall("unlock", pthread_mutex_unlock(&mu_));
+    (*function)(arg);
+  }
+}
+
+namespace {
+struct StartThreadState {
+  void (*user_function)(void*);
+  void* arg;
+};
+}
+static void* StartThreadWrapper(void* arg) {
+  StartThreadState* state = reinterpret_cast<StartThreadState*>(arg);
+  state->user_function(state->arg);
+  delete state;
+  return NULL;
+}
+
+void FuchsiaEnv::StartThread(void (*function)(void* arg), void* arg) {
+  pthread_t t;
+  StartThreadState* state = new StartThreadState;
+  state->user_function = function;
+  state->arg = arg;
+  PthreadCall("start thread",
+              pthread_create(&t, NULL,  &StartThreadWrapper, state));
+}
+
+}  // namespace
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static Env* default_env;
+
+static void InitDefaultEnv() { default_env = new FuchsiaEnv(AT_FDCWD); }
+
+Env* Env::Default() {
+  pthread_once(&once, InitDefaultEnv);
+  return default_env;
+}
+
+std::unique_ptr<Env> MakeFuchsiaEnv(int root_fd) {
+  return std::make_unique<FuchsiaEnv>(root_fd);
+}
+
+}  // namespace leveldb
diff --git a/util/env_fuchsia.h b/util/env_fuchsia.h
new file mode 100644
index 0000000..906dc19
--- /dev/null
+++ b/util/env_fuchsia.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <memory>
+#include "leveldb/env.h"
+
+namespace leveldb {
+
+// Creates a new environment for fuchsia. The database paths will consider
+// |root_fd| as root. This method will not take ownsership of |root_fd| that
+// must stay open until the environment is destructed.
+std::unique_ptr<Env> MakeFuchsiaEnv(int root_fd);
+
+}  // namespace leveldb