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"
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
)->
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
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.
49 // The "vtable" of our sqlite3_file "subclass".
52 // We keep an open pipe to the File object to keep it from cleaning itself
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 GetFSFile(file
).~FilePtr();
68 int MojoVFSRead(sqlite3_file
* sql_file
,
71 sqlite3_int64 offset
) {
72 DVLOG(1) << "MojoVFSRead (" << size
<< " @ " << offset
<< ")";
73 filesystem::FileError error
= filesystem::FILE_ERROR_FAILED
;
74 mojo::Array
<uint8_t> mojo_data
;
75 GetFSFile(sql_file
)->Read(size
, offset
, filesystem::WHENCE_FROM_BEGIN
,
76 Capture(&error
, &mojo_data
));
77 GetFSFile(sql_file
).WaitForIncomingResponse();
79 if (error
!= filesystem::FILE_ERROR_OK
) {
80 // TODO(erg): Better implementation here.
82 return SQLITE_IOERR_READ
;
86 memcpy(buffer
, &mojo_data
.front(), mojo_data
.size());
87 if (mojo_data
.size() == static_cast<size_t>(size
))
90 // We didn't read the entire buffer. Fill the rest of the buffer with zeros.
91 memset(reinterpret_cast<char*>(buffer
) + mojo_data
.size(), 0,
92 size
- mojo_data
.size());
94 return SQLITE_IOERR_SHORT_READ
;
97 int MojoVFSWrite(sqlite3_file
* sql_file
,
100 sqlite_int64 offset
) {
101 DVLOG(1) << "MojoVFSWrite(*, " << size
<< ", " << offset
<< ")";
102 mojo::Array
<uint8_t> mojo_data(size
);
103 memcpy(&mojo_data
.front(), buffer
, size
);
105 filesystem::FileError error
= filesystem::FILE_ERROR_FAILED
;
106 uint32_t num_bytes_written
= 0;
107 GetFSFile(sql_file
)->Write(mojo_data
.Pass(), offset
,
108 filesystem::WHENCE_FROM_BEGIN
,
109 Capture(&error
, &num_bytes_written
));
110 GetFSFile(sql_file
).WaitForIncomingResponse();
111 if (error
!= filesystem::FILE_ERROR_OK
) {
112 // TODO(erg): Better implementation here.
114 return SQLITE_IOERR_WRITE
;
116 if (num_bytes_written
!= static_cast<uint32_t>(size
)) {
118 return SQLITE_IOERR_WRITE
;
124 int MojoVFSTruncate(sqlite3_file
* sql_file
, sqlite_int64 size
) {
125 DVLOG(1) << "MojoVFSTruncate(*, " << size
<< ")";
126 filesystem::FileError error
= filesystem::FILE_ERROR_FAILED
;
127 GetFSFile(sql_file
)->Truncate(size
, Capture(&error
));
128 GetFSFile(sql_file
).WaitForIncomingResponse();
129 if (error
!= filesystem::FILE_ERROR_OK
) {
130 // TODO(erg): Better implementation here.
132 return SQLITE_IOERR_TRUNCATE
;
138 int MojoVFSSync(sqlite3_file
* sql_file
, int flags
) {
139 DVLOG(1) << "MojoVFSSync(*, " << flags
<< ")";
140 filesystem::FileError error
= filesystem::FILE_ERROR_FAILED
;
141 GetFSFile(sql_file
)->Flush(Capture(&error
));
142 GetFSFile(sql_file
).WaitForIncomingResponse();
143 if (error
!= filesystem::FILE_ERROR_OK
) {
144 // TODO(erg): Better implementation here.
146 return SQLITE_IOERR_FSYNC
;
152 int MojoVFSFileSize(sqlite3_file
* sql_file
, sqlite_int64
* size
) {
153 DVLOG(1) << "MojoVFSFileSize(*)";
155 filesystem::FileError err
= filesystem::FILE_ERROR_FAILED
;
156 filesystem::FileInformationPtr file_info
;
157 GetFSFile(sql_file
)->Stat(Capture(&err
, &file_info
));
158 GetFSFile(sql_file
).WaitForIncomingResponse();
160 if (err
!= filesystem::FILE_ERROR_OK
) {
161 // TODO(erg): Better implementation here.
163 return SQLITE_IOERR_FSTAT
;
166 *size
= file_info
->size
;
170 // TODO(erg): The current base::File interface isn't sufficient to handle
171 // sqlite's locking primitives, which are done on byte ranges in the file. (See
172 // "File Locking Notes" in sqlite3.c.)
173 int MojoVFSLock(sqlite3_file
* pFile
, int eLock
) {
174 DVLOG(1) << "MojoVFSLock(*, " << eLock
<< ")";
177 int MojoVFSUnlock(sqlite3_file
* pFile
, int eLock
) {
178 DVLOG(1) << "MojoVFSUnlock(*, " << eLock
<< ")";
181 int MojoVFSCheckReservedLock(sqlite3_file
* pFile
, int* pResOut
) {
182 DVLOG(1) << "MojoVFSCheckReservedLock(*)";
187 // TODO(erg): This is the minimal implementation to get a few tests passing;
188 // lots more needs to be done here.
189 int MojoVFSFileControl(sqlite3_file
* pFile
, int op
, void* pArg
) {
190 DVLOG(1) << "MojoVFSFileControl(*, " << op
<< ", *)";
191 if (op
== SQLITE_FCNTL_PRAGMA
) {
192 // Returning NOTFOUND tells sqlite that we aren't doing any processing.
193 return SQLITE_NOTFOUND
;
199 int MojoVFSSectorSize(sqlite3_file
* pFile
) {
200 DVLOG(1) << "MojoVFSSectorSize(*)";
201 // Use the default sector size.
205 int MojoVFSDeviceCharacteristics(sqlite3_file
* pFile
) {
206 DVLOG(1) << "MojoVFSDeviceCharacteristics(*)";
207 // TODO(erg): Figure out what to return here. (This function is super spammy,
208 // so not leaving a NOTIMPLEMENTED().)
212 static sqlite3_io_methods mojo_vfs_io_methods
= {
214 MojoVFSClose
, /* xClose */
215 MojoVFSRead
, /* xRead */
216 MojoVFSWrite
, /* xWrite */
217 MojoVFSTruncate
, /* xTruncate */
218 MojoVFSSync
, /* xSync */
219 MojoVFSFileSize
, /* xFileSize */
220 MojoVFSLock
, /* xLock */
221 MojoVFSUnlock
, /* xUnlock */
222 MojoVFSCheckReservedLock
, /* xCheckReservedLock */
223 MojoVFSFileControl
, /* xFileControl */
224 MojoVFSSectorSize
, /* xSectorSize */
225 MojoVFSDeviceCharacteristics
, /* xDeviceCharacteristics */
228 int MojoVFSOpen(sqlite3_vfs
* mojo_vfs
,
233 DVLOG(1) << "MojoVFSOpen(*, " << name
<< ", *, " << flags
<< ")";
235 if (flags
& SQLITE_OPEN_EXCLUSIVE
) {
236 DCHECK(flags
& SQLITE_OPEN_CREATE
);
237 open_flags
= filesystem::kFlagCreate
;
238 } else if (flags
& SQLITE_OPEN_CREATE
) {
239 DCHECK(flags
& SQLITE_OPEN_READWRITE
);
240 open_flags
= filesystem::kFlagOpenAlways
;
242 open_flags
= filesystem::kFlagOpen
;
244 open_flags
|= filesystem::kFlagRead
;
245 if (flags
& SQLITE_OPEN_READWRITE
)
246 open_flags
|= filesystem::kFlagWrite
;
247 if (flags
& SQLITE_OPEN_DELETEONCLOSE
)
248 open_flags
|= filesystem::kDeleteOnClose
;
250 mojo::String mojo_name
;
252 // Don't let callers open the pattern of our temporary databases. When we
253 // open with a null name and SQLITE_OPEN_DELETEONCLOSE, we unlink the
254 // database after we open it. If we create a database here, close it
255 // normally, and then open the same file through the other path, we could
256 // delete the database.
257 CHECK(strncmp("Temp_", name
, 5) != 0);
260 DCHECK(flags
& SQLITE_OPEN_DELETEONCLOSE
);
261 static int temp_number
= 0;
262 mojo_name
= base::StringPrintf("Temp_%d.db", temp_number
++);
265 // Grab the incoming file
266 filesystem::FilePtr file_ptr
;
267 filesystem::FileError error
= filesystem::FILE_ERROR_FAILED
;
268 GetRootDirectory(mojo_vfs
)->OpenFile(mojo_name
, GetProxy(&file_ptr
),
269 open_flags
, Capture(&error
));
270 GetRootDirectory(mojo_vfs
).WaitForIncomingResponse();
271 if (error
!= filesystem::FILE_ERROR_OK
) {
272 // TODO(erg): Translate more of the mojo error codes into sqlite error
274 return SQLITE_CANTOPEN
;
277 // Set the method table so we can be closed (and run the manual dtor call to
278 // match the following placement news).
279 file
->pMethods
= &mojo_vfs_io_methods
;
281 // |file| is actually a malloced buffer of size szOsFile. This means that we
282 // need to manually use placement new to construct the C++ object which owns
283 // the pipe to our file.
284 new (&GetFSFile(file
)) filesystem::FilePtr(file_ptr
.Pass());
289 int MojoVFSDelete(sqlite3_vfs
* mojo_vfs
, const char* filename
, int sync_dir
) {
290 DVLOG(1) << "MojoVFSDelete(*, " << filename
<< ", " << sync_dir
<< ")";
291 // TODO(erg): The default windows sqlite VFS has retry code to work around
292 // antivirus software keeping files open. We'll probably have to do something
293 // like that in the far future if we ever support Windows.
294 filesystem::FileError error
= filesystem::FILE_ERROR_FAILED
;
295 GetRootDirectory(mojo_vfs
)->Delete(filename
, 0, Capture(&error
));
296 GetRootDirectory(mojo_vfs
).WaitForIncomingResponse();
298 if (error
== filesystem::FILE_ERROR_OK
&& sync_dir
) {
299 GetRootDirectory(mojo_vfs
)->Flush(Capture(&error
));
300 GetRootDirectory(mojo_vfs
).WaitForIncomingResponse();
303 return error
== filesystem::FILE_ERROR_OK
? SQLITE_OK
: SQLITE_IOERR_DELETE
;
306 int MojoVFSAccess(sqlite3_vfs
* mojo_vfs
,
307 const char* filename
,
310 DVLOG(1) << "MojoVFSAccess(*, " << filename
<< ", " << flags
<< ", *)";
311 filesystem::FileError error
= filesystem::FILE_ERROR_FAILED
;
313 if (flags
== SQLITE_ACCESS_READWRITE
|| flags
== SQLITE_ACCESS_READ
) {
314 bool is_writable
= false;
315 GetRootDirectory(mojo_vfs
)
316 ->IsWritable(filename
, Capture(&error
, &is_writable
));
317 GetRootDirectory(mojo_vfs
).WaitForIncomingResponse();
318 *result
= is_writable
;
322 if (flags
== SQLITE_ACCESS_EXISTS
) {
324 GetRootDirectory(mojo_vfs
)->Exists(filename
, Capture(&error
, &exists
));
325 GetRootDirectory(mojo_vfs
).WaitForIncomingResponse();
333 int MojoVFSFullPathname(sqlite3_vfs
* mojo_vfs
,
334 const char* relative_path
,
335 int absolute_path_size
,
336 char* absolute_path
) {
337 // The sandboxed process doesn't need to know the absolute path of the file.
338 sqlite3_snprintf(absolute_path_size
, absolute_path
, "%s", relative_path
);
342 // Don't let SQLite dynamically load things. (If we are using the
343 // mojo:filesystem proxying VFS, then it's highly likely that we are sandboxed
344 // and that any attempt to dlopen() a shared object is folly.)
345 void* MojoVFSDlOpen(sqlite3_vfs
*, const char*) {
349 void MojoVFSDlError(sqlite3_vfs
*, int buf_size
, char* error_msg
) {
350 sqlite3_snprintf(buf_size
, error_msg
, "Dynamic loading not supported");
353 void (*MojoVFSDlSym(sqlite3_vfs
*, void*, const char*))(void) {
357 void MojoVFSDlClose(sqlite3_vfs
*, void*) {
361 int MojoVFSRandomness(sqlite3_vfs
* mojo_vfs
, int size
, char* out
) {
362 base::RandBytes(out
, size
);
366 // Proxy the rest of the calls down to the OS specific handler.
367 int MojoVFSSleep(sqlite3_vfs
* mojo_vfs
, int micro
) {
368 return GetParentVFS(mojo_vfs
)->xSleep(GetParentVFS(mojo_vfs
), micro
);
371 int MojoVFSCurrentTime(sqlite3_vfs
* mojo_vfs
, double* time
) {
372 return GetParentVFS(mojo_vfs
)->xCurrentTime(GetParentVFS(mojo_vfs
), time
);
375 int MojoVFSGetLastError(sqlite3_vfs
* mojo_vfs
, int a
, char* b
) {
379 int MojoVFSCurrentTimeInt64(sqlite3_vfs
* mojo_vfs
, sqlite3_int64
* out
) {
380 return GetParentVFS(mojo_vfs
)->xCurrentTimeInt64(GetParentVFS(mojo_vfs
), out
);
383 static sqlite3_vfs mojo_vfs
= {
385 sizeof(MojoVFSFile
), /* szOsFile */
386 kMaxPathName
, /* mxPathname */
390 MojoVFSOpen
, /* xOpen */
391 MojoVFSDelete
, /* xDelete */
392 MojoVFSAccess
, /* xAccess */
393 MojoVFSFullPathname
, /* xFullPathname */
394 MojoVFSDlOpen
, /* xDlOpen */
395 MojoVFSDlError
, /* xDlError */
396 MojoVFSDlSym
, /* xDlSym */
397 MojoVFSDlClose
, /* xDlClose */
398 MojoVFSRandomness
, /* xRandomness */
399 MojoVFSSleep
, /* xSleep */
400 MojoVFSCurrentTime
, /* xCurrentTime */
401 MojoVFSGetLastError
, /* xGetLastError */
402 MojoVFSCurrentTimeInt64
/* xCurrentTimeInt64 */
407 ScopedMojoFilesystemVFS::ScopedMojoFilesystemVFS(
408 filesystem::DirectoryPtr root_directory
)
409 : parent_(sqlite3_vfs_find(NULL
)),
410 root_directory_(root_directory
.Pass()) {
411 CHECK(!mojo_vfs
.pAppData
);
412 mojo_vfs
.pAppData
= this;
413 mojo_vfs
.mxPathname
= parent_
->mxPathname
;
415 CHECK(sqlite3_vfs_register(&mojo_vfs
, 1) == SQLITE_OK
);
418 ScopedMojoFilesystemVFS::~ScopedMojoFilesystemVFS() {
419 CHECK(mojo_vfs
.pAppData
);
420 mojo_vfs
.pAppData
= nullptr;
422 CHECK(sqlite3_vfs_register(parent_
, 1) == SQLITE_OK
);
423 CHECK(sqlite3_vfs_unregister(&mojo_vfs
) == SQLITE_OK
);
426 filesystem::DirectoryPtr
& ScopedMojoFilesystemVFS::GetDirectory() {
427 return root_directory_
;