Infobar material design refresh: bg color
[chromium-blink-merge.git] / sql / mojo / mojo_vfs.cc
blob8fc6da73b837a40e8d50a8324dbba70d5f86238e
1 // Copyright 2015 The Chromium 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.
5 #include "sql/mojo/mojo_vfs.h"
7 #include "base/logging.h"
8 #include "base/rand_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "components/filesystem/public/interfaces/file.mojom.h"
11 #include "components/filesystem/public/interfaces/file_system.mojom.h"
12 #include "components/filesystem/public/interfaces/types.mojom.h"
13 #include "mojo/public/cpp/bindings/lib/template_util.h"
14 #include "mojo/util/capture_util.h"
15 #include "third_party/sqlite/sqlite3.h"
17 using mojo::Capture;
19 namespace sql {
21 sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs) {
22 return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)->parent_;
25 filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs) {
26 return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)->
27 root_directory_;
30 namespace {
32 // Implementation of the sqlite3 Mojo proxying vfs.
34 // This is a bunch of C callback objects which transparently proxy sqlite3's
35 // filesystem reads/writes over the mojo:filesystem service. The main
36 // entrypoint is sqlite3_mojovfs(), which proxies all the file open/delete/etc
37 // operations. mojo:filesystem has support for passing a raw file descriptor
38 // over the IPC barrier, and most of the implementation of sqlite3_io_methods
39 // is derived from the default sqlite3 unix VFS and operates on the raw file
40 // descriptors.
42 const int kMaxPathName = 512;
44 // A struct which extends the base sqlite3_file to also hold on to a file
45 // pipe. We reinterpret_cast our sqlite3_file structs to this struct
46 // instead. This is "safe" because this struct is really just a slab of
47 // malloced memory, of which we tell sqlite how large we want with szOsFile.
48 struct MojoVFSFile {
49 // The "vtable" of our sqlite3_file "subclass".
50 sqlite3_file base;
52 // We keep an open pipe to the File object to keep it from cleaning itself
53 // up.
54 filesystem::FilePtr file_ptr;
57 filesystem::FilePtr& GetFSFile(sqlite3_file* vfs_file) {
58 return reinterpret_cast<MojoVFSFile*>(vfs_file)->file_ptr;
61 int MojoVFSClose(sqlite3_file* file) {
62 DVLOG(1) << "MojoVFSClose(*)";
63 using filesystem::FilePtr;
64 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
65 // Must call File::Close explicitly instead of just deleting the file, since
66 // otherwise we wouldn't have an object to wait on.
67 GetFSFile(file)->Close(mojo::Capture(&error));
68 GetFSFile(file).WaitForIncomingResponse();
69 GetFSFile(file).~FilePtr();
70 return SQLITE_OK;
73 int MojoVFSRead(sqlite3_file* sql_file,
74 void* buffer,
75 int size,
76 sqlite3_int64 offset) {
77 DVLOG(1) << "MojoVFSRead (" << size << " @ " << offset << ")";
78 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
79 mojo::Array<uint8_t> mojo_data;
80 GetFSFile(sql_file)->Read(size, offset, filesystem::WHENCE_FROM_BEGIN,
81 Capture(&error, &mojo_data));
82 GetFSFile(sql_file).WaitForIncomingResponse();
84 if (error != filesystem::FILE_ERROR_OK) {
85 // TODO(erg): Better implementation here.
86 NOTIMPLEMENTED();
87 return SQLITE_IOERR_READ;
90 if (mojo_data.size())
91 memcpy(buffer, &mojo_data.front(), mojo_data.size());
92 if (mojo_data.size() == static_cast<size_t>(size))
93 return SQLITE_OK;
95 // We didn't read the entire buffer. Fill the rest of the buffer with zeros.
96 memset(reinterpret_cast<char*>(buffer) + mojo_data.size(), 0,
97 size - mojo_data.size());
99 return SQLITE_IOERR_SHORT_READ;
102 int MojoVFSWrite(sqlite3_file* sql_file,
103 const void* buffer,
104 int size,
105 sqlite_int64 offset) {
106 DVLOG(1) << "MojoVFSWrite(*, " << size << ", " << offset << ")";
107 mojo::Array<uint8_t> mojo_data(size);
108 memcpy(&mojo_data.front(), buffer, size);
110 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
111 uint32_t num_bytes_written = 0;
112 GetFSFile(sql_file)->Write(mojo_data.Pass(), offset,
113 filesystem::WHENCE_FROM_BEGIN,
114 Capture(&error, &num_bytes_written));
115 GetFSFile(sql_file).WaitForIncomingResponse();
116 if (error != filesystem::FILE_ERROR_OK) {
117 // TODO(erg): Better implementation here.
118 NOTIMPLEMENTED();
119 return SQLITE_IOERR_WRITE;
121 if (num_bytes_written != static_cast<uint32_t>(size)) {
122 NOTIMPLEMENTED();
123 return SQLITE_IOERR_WRITE;
126 return SQLITE_OK;
129 int MojoVFSTruncate(sqlite3_file* sql_file, sqlite_int64 size) {
130 DVLOG(1) << "MojoVFSTruncate(*, " << size << ")";
131 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
132 GetFSFile(sql_file)->Truncate(size, Capture(&error));
133 GetFSFile(sql_file).WaitForIncomingResponse();
134 if (error != filesystem::FILE_ERROR_OK) {
135 // TODO(erg): Better implementation here.
136 NOTIMPLEMENTED();
137 return SQLITE_IOERR_TRUNCATE;
140 return SQLITE_OK;
143 int MojoVFSSync(sqlite3_file* sql_file, int flags) {
144 DVLOG(1) << "MojoVFSSync(*, " << flags << ")";
145 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
146 GetFSFile(sql_file)->Flush(Capture(&error));
147 GetFSFile(sql_file).WaitForIncomingResponse();
148 if (error != filesystem::FILE_ERROR_OK) {
149 // TODO(erg): Better implementation here.
150 NOTIMPLEMENTED();
151 return SQLITE_IOERR_FSYNC;
154 return SQLITE_OK;
157 int MojoVFSFileSize(sqlite3_file* sql_file, sqlite_int64* size) {
158 DVLOG(1) << "MojoVFSFileSize(*)";
160 filesystem::FileError err = filesystem::FILE_ERROR_FAILED;
161 filesystem::FileInformationPtr file_info;
162 GetFSFile(sql_file)->Stat(Capture(&err, &file_info));
163 GetFSFile(sql_file).WaitForIncomingResponse();
165 if (err != filesystem::FILE_ERROR_OK) {
166 // TODO(erg): Better implementation here.
167 NOTIMPLEMENTED();
168 return SQLITE_IOERR_FSTAT;
171 *size = file_info->size;
172 return SQLITE_OK;
175 // TODO(erg): The current base::File interface isn't sufficient to handle
176 // sqlite's locking primitives, which are done on byte ranges in the file. (See
177 // "File Locking Notes" in sqlite3.c.)
178 int MojoVFSLock(sqlite3_file* pFile, int eLock) {
179 DVLOG(1) << "MojoVFSLock(*, " << eLock << ")";
180 return SQLITE_OK;
182 int MojoVFSUnlock(sqlite3_file* pFile, int eLock) {
183 DVLOG(1) << "MojoVFSUnlock(*, " << eLock << ")";
184 return SQLITE_OK;
186 int MojoVFSCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
187 DVLOG(1) << "MojoVFSCheckReservedLock(*)";
188 *pResOut = 0;
189 return SQLITE_OK;
192 // TODO(erg): This is the minimal implementation to get a few tests passing;
193 // lots more needs to be done here.
194 int MojoVFSFileControl(sqlite3_file* pFile, int op, void* pArg) {
195 DVLOG(1) << "MojoVFSFileControl(*, " << op << ", *)";
196 if (op == SQLITE_FCNTL_PRAGMA) {
197 // Returning NOTFOUND tells sqlite that we aren't doing any processing.
198 return SQLITE_NOTFOUND;
201 return SQLITE_OK;
204 int MojoVFSSectorSize(sqlite3_file* pFile) {
205 DVLOG(1) << "MojoVFSSectorSize(*)";
206 // Use the default sector size.
207 return 0;
210 int MojoVFSDeviceCharacteristics(sqlite3_file* pFile) {
211 DVLOG(1) << "MojoVFSDeviceCharacteristics(*)";
212 // TODO(erg): Figure out what to return here. (This function is super spammy,
213 // so not leaving a NOTIMPLEMENTED().)
214 return 0;
217 static sqlite3_io_methods mojo_vfs_io_methods = {
218 1, /* iVersion */
219 MojoVFSClose, /* xClose */
220 MojoVFSRead, /* xRead */
221 MojoVFSWrite, /* xWrite */
222 MojoVFSTruncate, /* xTruncate */
223 MojoVFSSync, /* xSync */
224 MojoVFSFileSize, /* xFileSize */
225 MojoVFSLock, /* xLock */
226 MojoVFSUnlock, /* xUnlock */
227 MojoVFSCheckReservedLock, /* xCheckReservedLock */
228 MojoVFSFileControl, /* xFileControl */
229 MojoVFSSectorSize, /* xSectorSize */
230 MojoVFSDeviceCharacteristics, /* xDeviceCharacteristics */
233 int MojoVFSOpen(sqlite3_vfs* mojo_vfs,
234 const char* name,
235 sqlite3_file* file,
236 int flags,
237 int* pOutFlags) {
238 DVLOG(1) << "MojoVFSOpen(*, " << name << ", *, " << flags << ")";
239 int open_flags = 0;
240 if (flags & SQLITE_OPEN_EXCLUSIVE) {
241 DCHECK(flags & SQLITE_OPEN_CREATE);
242 open_flags = filesystem::kFlagCreate;
243 } else if (flags & SQLITE_OPEN_CREATE) {
244 DCHECK(flags & SQLITE_OPEN_READWRITE);
245 open_flags = filesystem::kFlagOpenAlways;
246 } else {
247 open_flags = filesystem::kFlagOpen;
249 open_flags |= filesystem::kFlagRead;
250 if (flags & SQLITE_OPEN_READWRITE)
251 open_flags |= filesystem::kFlagWrite;
252 if (flags & SQLITE_OPEN_DELETEONCLOSE)
253 open_flags |= filesystem::kDeleteOnClose;
255 mojo::String mojo_name;
256 if (name) {
257 // Don't let callers open the pattern of our temporary databases. When we
258 // open with a null name and SQLITE_OPEN_DELETEONCLOSE, we unlink the
259 // database after we open it. If we create a database here, close it
260 // normally, and then open the same file through the other path, we could
261 // delete the database.
262 CHECK(strncmp("Temp_", name, 5) != 0);
263 mojo_name = name;
264 } else {
265 DCHECK(flags & SQLITE_OPEN_DELETEONCLOSE);
266 static int temp_number = 0;
267 mojo_name = base::StringPrintf("Temp_%d.db", temp_number++);
270 // Grab the incoming file
271 filesystem::FilePtr file_ptr;
272 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
273 GetRootDirectory(mojo_vfs)->OpenFile(mojo_name, GetProxy(&file_ptr),
274 open_flags, Capture(&error));
275 GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
276 if (error != filesystem::FILE_ERROR_OK) {
277 // TODO(erg): Translate more of the mojo error codes into sqlite error
278 // codes.
279 return SQLITE_CANTOPEN;
282 // Set the method table so we can be closed (and run the manual dtor call to
283 // match the following placement news).
284 file->pMethods = &mojo_vfs_io_methods;
286 // |file| is actually a malloced buffer of size szOsFile. This means that we
287 // need to manually use placement new to construct the C++ object which owns
288 // the pipe to our file.
289 new (&GetFSFile(file)) filesystem::FilePtr(file_ptr.Pass());
291 return SQLITE_OK;
294 int MojoVFSDelete(sqlite3_vfs* mojo_vfs, const char* filename, int sync_dir) {
295 DVLOG(1) << "MojoVFSDelete(*, " << filename << ", " << sync_dir << ")";
296 // TODO(erg): The default windows sqlite VFS has retry code to work around
297 // antivirus software keeping files open. We'll probably have to do something
298 // like that in the far future if we ever support Windows.
299 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
300 GetRootDirectory(mojo_vfs)->Delete(filename, 0, Capture(&error));
301 GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
303 if (error == filesystem::FILE_ERROR_OK && sync_dir) {
304 GetRootDirectory(mojo_vfs)->Flush(Capture(&error));
305 GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
308 return error == filesystem::FILE_ERROR_OK ? SQLITE_OK : SQLITE_IOERR_DELETE;
311 int MojoVFSAccess(sqlite3_vfs* mojo_vfs,
312 const char* filename,
313 int flags,
314 int* result) {
315 DVLOG(1) << "MojoVFSAccess(*, " << filename << ", " << flags << ", *)";
316 filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
318 if (flags == SQLITE_ACCESS_READWRITE || flags == SQLITE_ACCESS_READ) {
319 bool is_writable = false;
320 GetRootDirectory(mojo_vfs)
321 ->IsWritable(filename, Capture(&error, &is_writable));
322 GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
323 *result = is_writable;
324 return SQLITE_OK;
327 if (flags == SQLITE_ACCESS_EXISTS) {
328 bool exists = false;
329 GetRootDirectory(mojo_vfs)->Exists(filename, Capture(&error, &exists));
330 GetRootDirectory(mojo_vfs).WaitForIncomingResponse();
331 *result = exists;
332 return SQLITE_OK;
335 return SQLITE_IOERR;
338 int MojoVFSFullPathname(sqlite3_vfs* mojo_vfs,
339 const char* relative_path,
340 int absolute_path_size,
341 char* absolute_path) {
342 // The sandboxed process doesn't need to know the absolute path of the file.
343 sqlite3_snprintf(absolute_path_size, absolute_path, "%s", relative_path);
344 return SQLITE_OK;
347 // Don't let SQLite dynamically load things. (If we are using the
348 // mojo:filesystem proxying VFS, then it's highly likely that we are sandboxed
349 // and that any attempt to dlopen() a shared object is folly.)
350 void* MojoVFSDlOpen(sqlite3_vfs*, const char*) {
351 return 0;
354 void MojoVFSDlError(sqlite3_vfs*, int buf_size, char* error_msg) {
355 sqlite3_snprintf(buf_size, error_msg, "Dynamic loading not supported");
358 void (*MojoVFSDlSym(sqlite3_vfs*, void*, const char*))(void) {
359 return 0;
362 void MojoVFSDlClose(sqlite3_vfs*, void*) {
363 return;
366 int MojoVFSRandomness(sqlite3_vfs* mojo_vfs, int size, char* out) {
367 base::RandBytes(out, size);
368 return size;
371 // Proxy the rest of the calls down to the OS specific handler.
372 int MojoVFSSleep(sqlite3_vfs* mojo_vfs, int micro) {
373 return GetParentVFS(mojo_vfs)->xSleep(GetParentVFS(mojo_vfs), micro);
376 int MojoVFSCurrentTime(sqlite3_vfs* mojo_vfs, double* time) {
377 return GetParentVFS(mojo_vfs)->xCurrentTime(GetParentVFS(mojo_vfs), time);
380 int MojoVFSGetLastError(sqlite3_vfs* mojo_vfs, int a, char* b) {
381 return 0;
384 int MojoVFSCurrentTimeInt64(sqlite3_vfs* mojo_vfs, sqlite3_int64* out) {
385 return GetParentVFS(mojo_vfs)->xCurrentTimeInt64(GetParentVFS(mojo_vfs), out);
388 static sqlite3_vfs mojo_vfs = {
389 1, /* iVersion */
390 sizeof(MojoVFSFile), /* szOsFile */
391 kMaxPathName, /* mxPathname */
392 0, /* pNext */
393 "mojo", /* zName */
394 0, /* pAppData */
395 MojoVFSOpen, /* xOpen */
396 MojoVFSDelete, /* xDelete */
397 MojoVFSAccess, /* xAccess */
398 MojoVFSFullPathname, /* xFullPathname */
399 MojoVFSDlOpen, /* xDlOpen */
400 MojoVFSDlError, /* xDlError */
401 MojoVFSDlSym, /* xDlSym */
402 MojoVFSDlClose, /* xDlClose */
403 MojoVFSRandomness, /* xRandomness */
404 MojoVFSSleep, /* xSleep */
405 MojoVFSCurrentTime, /* xCurrentTime */
406 MojoVFSGetLastError, /* xGetLastError */
407 MojoVFSCurrentTimeInt64 /* xCurrentTimeInt64 */
410 } // namespace
412 ScopedMojoFilesystemVFS::ScopedMojoFilesystemVFS(
413 filesystem::DirectoryPtr root_directory)
414 : parent_(sqlite3_vfs_find(NULL)),
415 root_directory_(root_directory.Pass()) {
416 CHECK(!mojo_vfs.pAppData);
417 mojo_vfs.pAppData = this;
418 mojo_vfs.mxPathname = parent_->mxPathname;
420 CHECK(sqlite3_vfs_register(&mojo_vfs, 1) == SQLITE_OK);
423 ScopedMojoFilesystemVFS::~ScopedMojoFilesystemVFS() {
424 CHECK(mojo_vfs.pAppData);
425 mojo_vfs.pAppData = nullptr;
427 CHECK(sqlite3_vfs_register(parent_, 1) == SQLITE_OK);
428 CHECK(sqlite3_vfs_unregister(&mojo_vfs) == SQLITE_OK);
431 filesystem::DirectoryPtr& ScopedMojoFilesystemVFS::GetDirectory() {
432 return root_directory_;
435 } // namespace sql