| Index: sql/mojo/mojo_vfs.cc
 | 
| diff --git a/sql/mojo/mojo_vfs.cc b/sql/mojo/mojo_vfs.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..6e38af9704032a15b0cf1bbd329e6c4da212c560
 | 
| --- /dev/null
 | 
| +++ b/sql/mojo/mojo_vfs.cc
 | 
| @@ -0,0 +1,413 @@
 | 
| +// Copyright 2015 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "sql/mojo/mojo_vfs.h"
 | 
| +
 | 
| +#include "base/logging.h"
 | 
| +#include "base/rand_util.h"
 | 
| +#include "components/filesystem/public/interfaces/file.mojom.h"
 | 
| +#include "components/filesystem/public/interfaces/file_system.mojom.h"
 | 
| +#include "components/filesystem/public/interfaces/types.mojom.h"
 | 
| +#include "mojo/public/cpp/bindings/lib/template_util.h"
 | 
| +#include "mojo/util/capture_util.h"
 | 
| +#include "third_party/sqlite/sqlite3.h"
 | 
| +
 | 
| +using mojo::Capture;
 | 
| +
 | 
| +namespace sql {
 | 
| +
 | 
| +sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs) {
 | 
| +  return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)->parent_;
 | 
| +}
 | 
| +
 | 
| +filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs) {
 | 
| +  return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)->
 | 
| +      root_directory_;
 | 
| +}
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +// Implementation of the sqlite3 Mojo proxying vfs.
 | 
| +//
 | 
| +// This is a bunch of C callback objects which transparently proxy sqlite3's
 | 
| +// filesystem reads/writes over the mojo:filesystem service. The main
 | 
| +// entrypoint is sqlite3_mojovfs(), which proxies all the file open/delete/etc
 | 
| +// operations. mojo:filesystem has support for passing a raw file descriptor
 | 
| +// over the IPC barrier, and most of the implementation of sqlite3_io_methods
 | 
| +// is derived from the default sqlite3 unix VFS and operates on the raw file
 | 
| +// descriptors.
 | 
| +
 | 
| +const int kMaxPathName = 512;
 | 
| +
 | 
| +// A struct which extends the base sqlite3_file to also hold on to a file
 | 
| +// pipe. We reinterpret_cast our sqlite3_file structs to this struct
 | 
| +// instead. This is "safe" because this struct is really just a slab of
 | 
| +// malloced memory, of which we tell sqlite how large we want with szOsFile.
 | 
| +struct MojoVFSFile {
 | 
| +  // The "vtable" of our sqlite3_file "subclass".
 | 
| +  sqlite3_file base;
 | 
| +
 | 
| +  // We keep an open pipe to the File object to keep it from cleaning itself
 | 
| +  // up.
 | 
| +  filesystem::FilePtr file_ptr;
 | 
| +};
 | 
| +
 | 
| +filesystem::FilePtr& GetFSFile(sqlite3_file* vfs_file) {
 | 
| +  return reinterpret_cast<MojoVFSFile*>(vfs_file)->file_ptr;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSClose(sqlite3_file* file) {
 | 
| +  DVLOG(1) << "MojoVFSClose(*)";
 | 
| +  using filesystem::FilePtr;
 | 
| +  GetFSFile(file).~FilePtr();
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSRead(sqlite3_file* sql_file,
 | 
| +                void* buffer,
 | 
| +                int size,
 | 
| +                sqlite3_int64 offset) {
 | 
| +  DVLOG(1) << "MojoVFSRead (" << size << " @ " << offset << ")";
 | 
| +  filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
 | 
| +  mojo::Array<uint8_t> mojo_data;
 | 
| +  GetFSFile(sql_file)->Read(size, offset, filesystem::WHENCE_FROM_BEGIN,
 | 
| +                            Capture(&error, &mojo_data));
 | 
| +  GetFSFile(sql_file).WaitForIncomingResponse();
 | 
| +
 | 
| +  if (error != filesystem::FILE_ERROR_OK) {
 | 
| +    // TODO(erg): Better implementation here.
 | 
| +    NOTIMPLEMENTED();
 | 
| +    return SQLITE_IOERR_READ;
 | 
| +  }
 | 
| +
 | 
| +  if (mojo_data.size())
 | 
| +    memcpy(buffer, &mojo_data.front(), mojo_data.size());
 | 
| +  if (mojo_data.size() == static_cast<size_t>(size))
 | 
| +    return SQLITE_OK;
 | 
| +
 | 
| +  // We didn't read the entire buffer. Fill the rest of the buffer with zeros.
 | 
| +  memset(reinterpret_cast<char*>(buffer) + mojo_data.size(), 0,
 | 
| +         size - mojo_data.size());
 | 
| +
 | 
| +  return SQLITE_IOERR_SHORT_READ;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSWrite(sqlite3_file* sql_file,
 | 
| +                 const void* buffer,
 | 
| +                 int size,
 | 
| +                 sqlite_int64 offset) {
 | 
| +  DVLOG(1) << "MojoVFSWrite(*, " << size << ", " << offset << ")";
 | 
| +  mojo::Array<uint8_t> mojo_data(size);
 | 
| +  memcpy(&mojo_data.front(), buffer, size);
 | 
| +
 | 
| +  filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
 | 
| +  uint32_t num_bytes_written = 0;
 | 
| +  GetFSFile(sql_file)->Write(mojo_data.Pass(), offset,
 | 
| +                             filesystem::WHENCE_FROM_BEGIN,
 | 
| +                             Capture(&error, &num_bytes_written));
 | 
| +  GetFSFile(sql_file).WaitForIncomingResponse();
 | 
| +  if (error != filesystem::FILE_ERROR_OK) {
 | 
| +    // TODO(erg): Better implementation here.
 | 
| +    NOTIMPLEMENTED();
 | 
| +    return SQLITE_IOERR_WRITE;
 | 
| +  }
 | 
| +  if (num_bytes_written != static_cast<uint32_t>(size)) {
 | 
| +    NOTIMPLEMENTED();
 | 
| +    return SQLITE_IOERR_WRITE;
 | 
| +  }
 | 
| +
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSTruncate(sqlite3_file* sql_file, sqlite_int64 size) {
 | 
| +  DVLOG(1) << "MojoVFSTruncate(*, " << size << ")";
 | 
| +  filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
 | 
| +  GetFSFile(sql_file)->Truncate(size, Capture(&error));
 | 
| +  GetFSFile(sql_file).WaitForIncomingResponse();
 | 
| +  if (error != filesystem::FILE_ERROR_OK) {
 | 
| +    // TODO(erg): Better implementation here.
 | 
| +    NOTIMPLEMENTED();
 | 
| +    return SQLITE_IOERR_TRUNCATE;
 | 
| +  }
 | 
| +
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSSync(sqlite3_file* sql_file, int flags) {
 | 
| +  DVLOG(1) << "MojoVFSSync(*, " << flags << ")";
 | 
| +  filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
 | 
| +  GetFSFile(sql_file)->Flush(Capture(&error));
 | 
| +  GetFSFile(sql_file).WaitForIncomingResponse();
 | 
| +  if (error != filesystem::FILE_ERROR_OK) {
 | 
| +    // TODO(erg): Better implementation here.
 | 
| +    NOTIMPLEMENTED();
 | 
| +    return SQLITE_IOERR_FSYNC;
 | 
| +  }
 | 
| +
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSFileSize(sqlite3_file* sql_file, sqlite_int64* size) {
 | 
| +  DVLOG(1) << "MojoVFSFileSize(*)";
 | 
| +
 | 
| +  filesystem::FileError err = filesystem::FILE_ERROR_FAILED;
 | 
| +  filesystem::FileInformationPtr file_info;
 | 
| +  GetFSFile(sql_file)->Stat(Capture(&err, &file_info));
 | 
| +  GetFSFile(sql_file).WaitForIncomingResponse();
 | 
| +
 | 
| +  if (err != filesystem::FILE_ERROR_OK) {
 | 
| +    // TODO(erg): Better implementation here.
 | 
| +    NOTIMPLEMENTED();
 | 
| +    return SQLITE_IOERR_FSTAT;
 | 
| +  }
 | 
| +
 | 
| +  *size = file_info->size;
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +// TODO(erg): The current base::File interface isn't sufficient to handle
 | 
| +// sqlite's locking primitives, which are done on byte ranges in the file. (See
 | 
| +// "File Locking Notes" in sqlite3.c.)
 | 
| +int MojoVFSLock(sqlite3_file* pFile, int eLock) {
 | 
| +  DVLOG(1) << "MojoVFSLock(*, " << eLock << ")";
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +int MojoVFSUnlock(sqlite3_file* pFile, int eLock) {
 | 
| +  DVLOG(1) << "MojoVFSUnlock(*, " << eLock << ")";
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +int MojoVFSCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
 | 
| +  DVLOG(1) << "MojoVFSCheckReservedLock(*)";
 | 
| +  *pResOut = 0;
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +// TODO(erg): This is the minimal implementation to get a few tests passing;
 | 
| +// lots more needs to be done here.
 | 
| +int MojoVFSFileControl(sqlite3_file* pFile, int op, void* pArg) {
 | 
| +  DVLOG(1) << "MojoVFSFileControl(*, " << op << ", *)";
 | 
| +  if (op == SQLITE_FCNTL_PRAGMA) {
 | 
| +    // Returning NOTFOUND tells sqlite that we aren't doing any processing.
 | 
| +    return SQLITE_NOTFOUND;
 | 
| +  }
 | 
| +
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSSectorSize(sqlite3_file* pFile) {
 | 
| +  DVLOG(1) << "MojoVFSSectorSize(*)";
 | 
| +  // Use the default sector size.
 | 
| +  return 0;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSDeviceCharacteristics(sqlite3_file* pFile) {
 | 
| +  DVLOG(1) << "MojoVFSDeviceCharacteristics(*)";
 | 
| +  NOTIMPLEMENTED();
 | 
| +  return 0;
 | 
| +}
 | 
| +
 | 
| +static sqlite3_io_methods mojo_vfs_io_methods = {
 | 
| +    1,                            /* iVersion */
 | 
| +    MojoVFSClose,                 /* xClose */
 | 
| +    MojoVFSRead,                  /* xRead */
 | 
| +    MojoVFSWrite,                 /* xWrite */
 | 
| +    MojoVFSTruncate,              /* xTruncate */
 | 
| +    MojoVFSSync,                  /* xSync */
 | 
| +    MojoVFSFileSize,              /* xFileSize */
 | 
| +    MojoVFSLock,                  /* xLock */
 | 
| +    MojoVFSUnlock,                /* xUnlock */
 | 
| +    MojoVFSCheckReservedLock,     /* xCheckReservedLock */
 | 
| +    MojoVFSFileControl,           /* xFileControl */
 | 
| +    MojoVFSSectorSize,            /* xSectorSize */
 | 
| +    MojoVFSDeviceCharacteristics, /* xDeviceCharacteristics */
 | 
| +};
 | 
| +
 | 
| +int MojoVFSOpen(sqlite3_vfs* mojo_vfs,
 | 
| +                const char* name,
 | 
| +                sqlite3_file* file,
 | 
| +                int flags,
 | 
| +                int* pOutFlags) {
 | 
| +  DVLOG(1) << "MojoVFSOpen(*, " << name << ", *, " << flags << ")";
 | 
| +  int open_flags = 0;
 | 
| +  if (flags & SQLITE_OPEN_EXCLUSIVE) {
 | 
| +    DCHECK(flags & SQLITE_OPEN_CREATE);
 | 
| +    open_flags = filesystem::kFlagCreate;
 | 
| +  } else if (flags & SQLITE_OPEN_CREATE) {
 | 
| +    DCHECK(flags & SQLITE_OPEN_READWRITE);
 | 
| +    open_flags = filesystem::kFlagOpenAlways;
 | 
| +  } else {
 | 
| +    open_flags = filesystem::kFlagOpen;
 | 
| +  }
 | 
| +  open_flags |= filesystem::kFlagRead;
 | 
| +  if (flags & SQLITE_OPEN_READWRITE)
 | 
| +    open_flags |= filesystem::kFlagWrite;
 | 
| +  if (flags & SQLITE_OPEN_DELETEONCLOSE)
 | 
| +    open_flags |= filesystem::kDeleteOnClose;
 | 
| +
 | 
| +  // Grab the incoming file
 | 
| +  filesystem::FilePtr file_ptr;
 | 
| +  filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
 | 
| +  GetRootDirectory(mojo_vfs)->OpenFile(mojo::String(name), GetProxy(&file_ptr),
 | 
| +                                       open_flags, Capture(&error));
 | 
| +  GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
 | 
| +  if (error != filesystem::FILE_ERROR_OK) {
 | 
| +    // TODO(erg): Translate more of the mojo error codes into sqlite error
 | 
| +    // codes.
 | 
| +    return SQLITE_CANTOPEN;
 | 
| +  }
 | 
| +
 | 
| +  // Set the method table so we can be closed (and run the manual dtor call to
 | 
| +  // match the following placement news).
 | 
| +  file->pMethods = &mojo_vfs_io_methods;
 | 
| +
 | 
| +  // |file| is actually a malloced buffer of size szOsFile. This means that we
 | 
| +  // need to manually use placement new to construct the C++ object which owns
 | 
| +  // the pipe to our file.
 | 
| +  new (&GetFSFile(file)) filesystem::FilePtr(file_ptr.Pass());
 | 
| +
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSDelete(sqlite3_vfs* mojo_vfs, const char* filename, int sync_dir) {
 | 
| +  DVLOG(1) << "MojoVFSDelete(*, " << filename << ", " << sync_dir << ")";
 | 
| +  // TODO(erg): The default windows sqlite VFS has retry code to work around
 | 
| +  // antivirus software keeping files open. We'll probably have to do something
 | 
| +  // like that in the far future if we ever support Windows.
 | 
| +  filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
 | 
| +  GetRootDirectory(mojo_vfs)->Delete(filename, 0, Capture(&error));
 | 
| +  GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
 | 
| +
 | 
| +  if (error == filesystem::FILE_ERROR_OK && sync_dir) {
 | 
| +    GetRootDirectory(mojo_vfs)->Flush(Capture(&error));
 | 
| +    GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
 | 
| +  }
 | 
| +
 | 
| +  return error == filesystem::FILE_ERROR_OK ? SQLITE_OK : SQLITE_IOERR_DELETE;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSAccess(sqlite3_vfs* mojo_vfs,
 | 
| +                  const char* filename,
 | 
| +                  int flags,
 | 
| +                  int* result) {
 | 
| +  DVLOG(1) << "MojoVFSAccess(*, " << filename << ", " << flags << ", *)";
 | 
| +  filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
 | 
| +
 | 
| +  if (flags == SQLITE_ACCESS_READWRITE || flags == SQLITE_ACCESS_READ) {
 | 
| +    bool is_writable = false;
 | 
| +    GetRootDirectory(mojo_vfs)
 | 
| +        ->IsWritable(filename, Capture(&error, &is_writable));
 | 
| +    GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
 | 
| +    *result = is_writable;
 | 
| +    return SQLITE_OK;
 | 
| +  }
 | 
| +
 | 
| +  if (flags == SQLITE_ACCESS_EXISTS) {
 | 
| +    bool exists = false;
 | 
| +    GetRootDirectory(mojo_vfs)->Exists(filename, Capture(&error, &exists));
 | 
| +    GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
 | 
| +    *result = exists;
 | 
| +    return SQLITE_OK;
 | 
| +  }
 | 
| +
 | 
| +  return SQLITE_IOERR;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSFullPathname(sqlite3_vfs* mojo_vfs,
 | 
| +                        const char* relative_path,
 | 
| +                        int absolute_path_size,
 | 
| +                        char* absolute_path) {
 | 
| +  // The sandboxed process doesn't need to know the absolute path of the file.
 | 
| +  sqlite3_snprintf(absolute_path_size, absolute_path, "%s", relative_path);
 | 
| +  return SQLITE_OK;
 | 
| +}
 | 
| +
 | 
| +// Don't let SQLite dynamically load things. (If we are using the
 | 
| +// mojo:filesystem proxying VFS, then it's highly likely that we are sandboxed
 | 
| +// and that any attempt to dlopen() a shared object is folly.)
 | 
| +void* MojoVFSDlOpen(sqlite3_vfs*, const char*) {
 | 
| +  return 0;
 | 
| +}
 | 
| +
 | 
| +void MojoVFSDlError(sqlite3_vfs*, int buf_size, char* error_msg) {
 | 
| +  sqlite3_snprintf(buf_size, error_msg, "Dynamic loading not supported");
 | 
| +}
 | 
| +
 | 
| +void (*MojoVFSDlSym(sqlite3_vfs*, void*, const char*))(void) {
 | 
| +  return 0;
 | 
| +}
 | 
| +
 | 
| +void MojoVFSDlClose(sqlite3_vfs*, void*) {
 | 
| +  return;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSRandomness(sqlite3_vfs* mojo_vfs, int size, char* out) {
 | 
| +  base::RandBytes(out, size);
 | 
| +  return size;
 | 
| +}
 | 
| +
 | 
| +// Proxy the rest of the calls down to the OS specific handler.
 | 
| +int MojoVFSSleep(sqlite3_vfs* mojo_vfs, int micro) {
 | 
| +  return GetParentVFS(mojo_vfs)->xSleep(GetParentVFS(mojo_vfs), micro);
 | 
| +}
 | 
| +
 | 
| +int MojoVFSCurrentTime(sqlite3_vfs* mojo_vfs, double* time) {
 | 
| +  return GetParentVFS(mojo_vfs)->xCurrentTime(GetParentVFS(mojo_vfs), time);
 | 
| +}
 | 
| +
 | 
| +int MojoVFSGetLastError(sqlite3_vfs* mojo_vfs, int a, char* b) {
 | 
| +  return 0;
 | 
| +}
 | 
| +
 | 
| +int MojoVFSCurrentTimeInt64(sqlite3_vfs* mojo_vfs, sqlite3_int64* out) {
 | 
| +  return GetParentVFS(mojo_vfs)->xCurrentTimeInt64(GetParentVFS(mojo_vfs), out);
 | 
| +}
 | 
| +
 | 
| +static sqlite3_vfs mojo_vfs = {
 | 
| +    1,                      /* iVersion */
 | 
| +    sizeof(MojoVFSFile),    /* szOsFile */
 | 
| +    kMaxPathName,           /* mxPathname */
 | 
| +    0,                      /* pNext */
 | 
| +    "mojo",                 /* zName */
 | 
| +    0,                      /* pAppData */
 | 
| +    MojoVFSOpen,            /* xOpen */
 | 
| +    MojoVFSDelete,          /* xDelete */
 | 
| +    MojoVFSAccess,          /* xAccess */
 | 
| +    MojoVFSFullPathname,    /* xFullPathname */
 | 
| +    MojoVFSDlOpen,          /* xDlOpen */
 | 
| +    MojoVFSDlError,         /* xDlError */
 | 
| +    MojoVFSDlSym,           /* xDlSym */
 | 
| +    MojoVFSDlClose,         /* xDlClose */
 | 
| +    MojoVFSRandomness,      /* xRandomness */
 | 
| +    MojoVFSSleep,           /* xSleep */
 | 
| +    MojoVFSCurrentTime,     /* xCurrentTime */
 | 
| +    MojoVFSGetLastError,    /* xGetLastError */
 | 
| +    MojoVFSCurrentTimeInt64 /* xCurrentTimeInt64 */
 | 
| +};
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +ScopedMojoFilesystemVFS::ScopedMojoFilesystemVFS(
 | 
| +    filesystem::DirectoryPtr root_directory)
 | 
| +    : parent_(sqlite3_vfs_find(NULL)),
 | 
| +      root_directory_(root_directory.Pass()) {
 | 
| +  CHECK(!mojo_vfs.pAppData);
 | 
| +  mojo_vfs.pAppData = this;
 | 
| +  mojo_vfs.mxPathname = parent_->mxPathname;
 | 
| +
 | 
| +  CHECK(sqlite3_vfs_register(&mojo_vfs, 1) == SQLITE_OK);
 | 
| +}
 | 
| +
 | 
| +ScopedMojoFilesystemVFS::~ScopedMojoFilesystemVFS() {
 | 
| +  CHECK(mojo_vfs.pAppData);
 | 
| +  mojo_vfs.pAppData = nullptr;
 | 
| +
 | 
| +  CHECK(sqlite3_vfs_register(parent_, 1) == SQLITE_OK);
 | 
| +  CHECK(sqlite3_vfs_unregister(&mojo_vfs) == SQLITE_OK);
 | 
| +}
 | 
| +
 | 
| +filesystem::DirectoryPtr& ScopedMojoFilesystemVFS::GetDirectory() {
 | 
| +  return root_directory_;
 | 
| +}
 | 
| +
 | 
| +}  // namespace sql
 | 
| 
 |