Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(477)

Unified Diff: sql/mojo/enable_mojo_fs.cc

Issue 1176653002: mandoline filesystem: add a sqlite3 vfs to proxy filesystem usage. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Merge with ToT and fix the test import. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: sql/mojo/enable_mojo_fs.cc
diff --git a/sql/mojo/enable_mojo_fs.cc b/sql/mojo/enable_mojo_fs.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4c0d9ad6840fe7648beb8cc88a3ae954c392fcce
--- /dev/null
+++ b/sql/mojo/enable_mojo_fs.cc
@@ -0,0 +1,433 @@
+// 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/enable_mojo_fs.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 {
+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;
+
+// Additional data that we store in the sqlite3_vfs's |pAppData| member.
Scott Hess - ex-Googler 2015/06/17 19:32:58 A weird thought just occurred to me. Could pAppDa
Elliot Glaysher 2015/06/17 21:59:34 Done.
+struct MojoVFSData {
+ // The default vfs at the time MojoVFS was installed. We use the to pass
+ // through things like randomness requests and per-platform sleep calls.
+ sqlite3_vfs* parent;
+
+ // When we initialize the subsystem, we are given a filesystem::Directory
+ // object, which is the root directory of a mojo:filesystem. All access to
+ // various files are specified from this root directory.
+ filesystem::DirectoryPtr root_directory;
+};
+
+sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs) {
+ return reinterpret_cast<MojoVFSData*>(mojo_vfs->pAppData)->parent;
+}
+
+filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs) {
+ return reinterpret_cast<MojoVFSData*>(mojo_vfs->pAppData)->root_directory;
+}
Scott Hess - ex-Googler 2015/06/17 19:32:58 Why is this reinterpret_cast<> and GetDirectory()
Elliot Glaysher 2015/06/17 21:59:35 You can static_cast a void pointer to anything. Yo
+
+// 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 mojo::InterfacePtr;
+ GetFSFile(file).~InterfacePtr<filesystem::File>();
+ 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() == static_cast<size_t>(size)) {
+ memcpy(buffer, &mojo_data.front(), mojo_data.size());
+ return SQLITE_OK;
+ }
+
+ // We didn't read the entire buffer. We need to copy the data AND zero fill
+ // the rest of the buffer.
+ if (mojo_data.size())
+ memcpy(buffer, &mojo_data.front(), mojo_data.size());
Scott Hess - ex-Googler 2015/06/17 19:32:58 Pull this out for sharing before the full-read tes
Elliot Glaysher 2015/06/17 21:59:34 Done, but we still have to check mojo_data.size()
Scott Hess - ex-Googler 2015/06/19 20:48:01 Acknowledged.
+ 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.
Scott Hess - ex-Googler 2015/06/17 19:32:58 Yeah, this sucks. It would probably make more sen
+ 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) {
+ CHECK(!mojo_vfs.pAppData);
+
+ MojoVFSData* data = new MojoVFSData;
+ data->parent = sqlite3_vfs_find(NULL);
+ data->root_directory = root_directory.Pass();
+ mojo_vfs.pAppData = data;
+
+ mojo_vfs.mxPathname = data->parent->mxPathname;
+
+ CHECK(sqlite3_vfs_register(&mojo_vfs, 1) == SQLITE_OK);
+}
+
+ScopedMojoFilesystemVFS::~ScopedMojoFilesystemVFS() {
+ CHECK(mojo_vfs.pAppData);
+
+ MojoVFSData* data = static_cast<MojoVFSData*>(mojo_vfs.pAppData);
+ sqlite3_vfs* previous_fs = data->parent;
+ delete data;
+ mojo_vfs.pAppData = nullptr;
+
+ CHECK(sqlite3_vfs_unregister(&mojo_vfs) == SQLITE_OK);
+ CHECK(sqlite3_vfs_register(previous_fs, 1) == SQLITE_OK);
Scott Hess - ex-Googler 2015/06/17 19:32:58 Suggest you restore the registration setup before
Elliot Glaysher 2015/06/17 21:59:35 Done.
+}
+
+filesystem::DirectoryPtr& ScopedMojoFilesystemVFS::GetDirectory() {
+ return static_cast<MojoVFSData*>(mojo_vfs.pAppData)->root_directory;
Scott Hess - ex-Googler 2015/06/17 19:32:58 Maybe this should be: return GetRootDirectory(&m
+}
+
+} // namespace sql

Powered by Google App Engine
This is Rietveld 408576698