OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "sql/mojo/enable_mojo_fs.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/rand_util.h" | |
9 #include "components/filesystem/public/interfaces/file.mojom.h" | |
10 #include "components/filesystem/public/interfaces/file_system.mojom.h" | |
11 #include "components/filesystem/public/interfaces/types.mojom.h" | |
12 #include "mojo/public/cpp/bindings/lib/template_util.h" | |
13 #include "mojo/util/capture_util.h" | |
14 #include "third_party/sqlite/sqlite3.h" | |
15 | |
16 using mojo::Capture; | |
17 | |
18 namespace sql { | |
19 namespace { | |
20 | |
21 // Implementation of the sqlite3 Mojo proxying vfs. | |
22 // | |
23 // This is a bunch of C callback objects which transparently proxy sqlite3's | |
24 // filesystem reads/writes over the mojo:filesystem service. The main | |
25 // entrypoint is sqlite3_mojovfs(), which proxies all the file open/delete/etc | |
26 // operations. mojo:filesystem has support for passing a raw file descriptor | |
27 // over the IPC barrier, and most of the implementation of sqlite3_io_methods | |
28 // is derived from the default sqlite3 unix VFS and operates on the raw file | |
29 // descriptors. | |
30 | |
31 const int kMaxPathName = 512; | |
32 | |
33 // 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.
| |
34 struct MojoVFSData { | |
35 // The default vfs at the time MojoVFS was installed. We use the to pass | |
36 // through things like randomness requests and per-platform sleep calls. | |
37 sqlite3_vfs* parent; | |
38 | |
39 // When we initialize the subsystem, we are given a filesystem::Directory | |
40 // object, which is the root directory of a mojo:filesystem. All access to | |
41 // various files are specified from this root directory. | |
42 filesystem::DirectoryPtr root_directory; | |
43 }; | |
44 | |
45 sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs) { | |
46 return reinterpret_cast<MojoVFSData*>(mojo_vfs->pAppData)->parent; | |
47 } | |
48 | |
49 filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs) { | |
50 return reinterpret_cast<MojoVFSData*>(mojo_vfs->pAppData)->root_directory; | |
51 } | |
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
| |
52 | |
53 // A struct which extends the base sqlite3_file to also hold on to a file | |
54 // pipe. We reinterpret_cast our sqlite3_file structs to this struct | |
55 // instead. This is "safe" because this struct is really just a slab of | |
56 // malloced memory, of which we tell sqlite how large we want with szOsFile. | |
57 struct MojoVFSFile { | |
58 // The "vtable" of our sqlite3_file "subclass". | |
59 sqlite3_file base; | |
60 | |
61 // We keep an open pipe to the File object to keep it from cleaning itself | |
62 // up. | |
63 filesystem::FilePtr file_ptr; | |
64 }; | |
65 | |
66 filesystem::FilePtr& GetFSFile(sqlite3_file* vfs_file) { | |
67 return reinterpret_cast<MojoVFSFile*>(vfs_file)->file_ptr; | |
68 } | |
69 | |
70 int MojoVFSClose(sqlite3_file* file) { | |
71 DVLOG(1) << "MojoVFSClose(*)"; | |
72 using mojo::InterfacePtr; | |
73 GetFSFile(file).~InterfacePtr<filesystem::File>(); | |
74 return SQLITE_OK; | |
75 } | |
76 | |
77 int MojoVFSRead(sqlite3_file* sql_file, | |
78 void* buffer, | |
79 int size, | |
80 sqlite3_int64 offset) { | |
81 DVLOG(1) << "MojoVFSRead (" << size << " @ " << offset << ")"; | |
82 filesystem::FileError error = filesystem::FILE_ERROR_FAILED; | |
83 mojo::Array<uint8_t> mojo_data; | |
84 GetFSFile(sql_file)->Read(size, offset, filesystem::WHENCE_FROM_BEGIN, | |
85 Capture(&error, &mojo_data)); | |
86 GetFSFile(sql_file).WaitForIncomingResponse(); | |
87 | |
88 if (error != filesystem::FILE_ERROR_OK) { | |
89 // TODO(erg): Better implementation here. | |
90 NOTIMPLEMENTED(); | |
91 return SQLITE_IOERR_READ; | |
92 } | |
93 | |
94 if (mojo_data.size() == static_cast<size_t>(size)) { | |
95 memcpy(buffer, &mojo_data.front(), mojo_data.size()); | |
96 return SQLITE_OK; | |
97 } | |
98 | |
99 // We didn't read the entire buffer. We need to copy the data AND zero fill | |
100 // the rest of the buffer. | |
101 if (mojo_data.size()) | |
102 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.
| |
103 memset(reinterpret_cast<char*>(buffer) + mojo_data.size(), 0, | |
104 size - mojo_data.size()); | |
105 | |
106 return SQLITE_IOERR_SHORT_READ; | |
107 } | |
108 | |
109 int MojoVFSWrite(sqlite3_file* sql_file, | |
110 const void* buffer, | |
111 int size, | |
112 sqlite_int64 offset) { | |
113 DVLOG(1) << "MojoVFSWrite(*, " << size << ", " << offset << ")"; | |
114 mojo::Array<uint8_t> mojo_data(size); | |
115 memcpy(&mojo_data.front(), buffer, size); | |
116 | |
117 filesystem::FileError error = filesystem::FILE_ERROR_FAILED; | |
118 uint32_t num_bytes_written = 0; | |
119 GetFSFile(sql_file)->Write(mojo_data.Pass(), offset, | |
120 filesystem::WHENCE_FROM_BEGIN, | |
121 Capture(&error, &num_bytes_written)); | |
122 GetFSFile(sql_file).WaitForIncomingResponse(); | |
123 if (error != filesystem::FILE_ERROR_OK) { | |
124 // TODO(erg): Better implementation here. | |
125 NOTIMPLEMENTED(); | |
126 return SQLITE_IOERR_WRITE; | |
127 } | |
128 if (num_bytes_written != static_cast<uint32_t>(size)) { | |
129 NOTIMPLEMENTED(); | |
130 return SQLITE_IOERR_WRITE; | |
131 } | |
132 | |
133 return SQLITE_OK; | |
134 } | |
135 | |
136 int MojoVFSTruncate(sqlite3_file* sql_file, sqlite_int64 size) { | |
137 DVLOG(1) << "MojoVFSTruncate(*, " << size << ")"; | |
138 filesystem::FileError error = filesystem::FILE_ERROR_FAILED; | |
139 GetFSFile(sql_file)->Truncate(size, Capture(&error)); | |
140 GetFSFile(sql_file).WaitForIncomingResponse(); | |
141 if (error != filesystem::FILE_ERROR_OK) { | |
142 // TODO(erg): Better implementation here. | |
143 NOTIMPLEMENTED(); | |
144 return SQLITE_IOERR_TRUNCATE; | |
145 } | |
146 | |
147 return SQLITE_OK; | |
148 } | |
149 | |
150 int MojoVFSSync(sqlite3_file* sql_file, int flags) { | |
151 DVLOG(1) << "MojoVFSSync(*, " << flags << ")"; | |
152 filesystem::FileError error = filesystem::FILE_ERROR_FAILED; | |
153 GetFSFile(sql_file)->Flush(Capture(&error)); | |
154 GetFSFile(sql_file).WaitForIncomingResponse(); | |
155 if (error != filesystem::FILE_ERROR_OK) { | |
156 // TODO(erg): Better implementation here. | |
157 NOTIMPLEMENTED(); | |
158 return SQLITE_IOERR_FSYNC; | |
159 } | |
160 | |
161 return SQLITE_OK; | |
162 } | |
163 | |
164 int MojoVFSFileSize(sqlite3_file* sql_file, sqlite_int64* size) { | |
165 DVLOG(1) << "MojoVFSFileSize(*)"; | |
166 | |
167 filesystem::FileError err = filesystem::FILE_ERROR_FAILED; | |
168 filesystem::FileInformationPtr file_info; | |
169 GetFSFile(sql_file)->Stat(Capture(&err, &file_info)); | |
170 GetFSFile(sql_file).WaitForIncomingResponse(); | |
171 | |
172 if (err != filesystem::FILE_ERROR_OK) { | |
173 // TODO(erg): Better implementation here. | |
174 NOTIMPLEMENTED(); | |
175 return SQLITE_IOERR_FSTAT; | |
176 } | |
177 | |
178 *size = file_info->size; | |
179 return SQLITE_OK; | |
180 } | |
181 | |
182 // TODO(erg): The current base::File interface isn't sufficient to handle | |
183 // sqlite's locking primitives, which are done on byte ranges in the file. (See | |
184 // "File Locking Notes" in sqlite3.c.) | |
185 int MojoVFSLock(sqlite3_file* pFile, int eLock) { | |
186 DVLOG(1) << "MojoVFSLock(*, " << eLock << ")"; | |
187 return SQLITE_OK; | |
188 } | |
189 int MojoVFSUnlock(sqlite3_file* pFile, int eLock) { | |
190 DVLOG(1) << "MojoVFSUnlock(*, " << eLock << ")"; | |
191 return SQLITE_OK; | |
192 } | |
193 int MojoVFSCheckReservedLock(sqlite3_file* pFile, int* pResOut) { | |
194 DVLOG(1) << "MojoVFSCheckReservedLock(*)"; | |
195 *pResOut = 0; | |
196 return SQLITE_OK; | |
197 } | |
198 | |
199 // TODO(erg): This is the minimal implementation to get a few tests passing; | |
200 // lots more needs to be done here. | |
201 int MojoVFSFileControl(sqlite3_file* pFile, int op, void* pArg) { | |
202 DVLOG(1) << "MojoVFSFileControl(*, " << op << ", *)"; | |
203 if (op == SQLITE_FCNTL_PRAGMA) { | |
204 // Returning NOTFOUND tells sqlite that we aren't doing any processing. | |
205 return SQLITE_NOTFOUND; | |
206 } | |
207 | |
208 return SQLITE_OK; | |
209 } | |
210 | |
211 int MojoVFSSectorSize(sqlite3_file* pFile) { | |
212 DVLOG(1) << "MojoVFSSectorSize(*)"; | |
213 // Use the default sector size. | |
214 return 0; | |
215 } | |
216 | |
217 int MojoVFSDeviceCharacteristics(sqlite3_file* pFile) { | |
218 DVLOG(1) << "MojoVFSDeviceCharacteristics(*)"; | |
219 NOTIMPLEMENTED(); | |
220 return 0; | |
221 } | |
222 | |
223 static sqlite3_io_methods mojo_vfs_io_methods = { | |
224 1, /* iVersion */ | |
225 MojoVFSClose, /* xClose */ | |
226 MojoVFSRead, /* xRead */ | |
227 MojoVFSWrite, /* xWrite */ | |
228 MojoVFSTruncate, /* xTruncate */ | |
229 MojoVFSSync, /* xSync */ | |
230 MojoVFSFileSize, /* xFileSize */ | |
231 MojoVFSLock, /* xLock */ | |
232 MojoVFSUnlock, /* xUnlock */ | |
233 MojoVFSCheckReservedLock, /* xCheckReservedLock */ | |
234 MojoVFSFileControl, /* xFileControl */ | |
235 MojoVFSSectorSize, /* xSectorSize */ | |
236 MojoVFSDeviceCharacteristics, /* xDeviceCharacteristics */ | |
237 }; | |
238 | |
239 int MojoVFSOpen(sqlite3_vfs* mojo_vfs, | |
240 const char* name, | |
241 sqlite3_file* file, | |
242 int flags, | |
243 int* pOutFlags) { | |
244 DVLOG(1) << "MojoVFSOpen(*, " << name << ", *, " << flags << ")"; | |
245 int open_flags = 0; | |
246 if (flags & SQLITE_OPEN_EXCLUSIVE) { | |
247 DCHECK(flags & SQLITE_OPEN_CREATE); | |
248 open_flags = filesystem::kFlagCreate; | |
249 } else if (flags & SQLITE_OPEN_CREATE) { | |
250 DCHECK(flags & SQLITE_OPEN_READWRITE); | |
251 open_flags = filesystem::kFlagOpenAlways; | |
252 } else { | |
253 open_flags = filesystem::kFlagOpen; | |
254 } | |
255 open_flags |= filesystem::kFlagRead; | |
256 if (flags & SQLITE_OPEN_READWRITE) | |
257 open_flags |= filesystem::kFlagWrite; | |
258 if (flags & SQLITE_OPEN_DELETEONCLOSE) | |
259 open_flags |= filesystem::kDeleteOnClose; | |
260 | |
261 // Grab the incoming file | |
262 filesystem::FilePtr file_ptr; | |
263 filesystem::FileError error = filesystem::FILE_ERROR_FAILED; | |
264 GetRootDirectory(mojo_vfs)->OpenFile(mojo::String(name), GetProxy(&file_ptr), | |
265 open_flags, Capture(&error)); | |
266 GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); | |
267 if (error != filesystem::FILE_ERROR_OK) { | |
268 // TODO(erg): Translate more of the mojo error codes into sqlite error | |
269 // codes. | |
270 return SQLITE_CANTOPEN; | |
271 } | |
272 | |
273 // Set the method table so we can be closed (and run the manual dtor call to | |
274 // match the following placement news). | |
275 file->pMethods = &mojo_vfs_io_methods; | |
276 | |
277 // |file| is actually a malloced buffer of size szOsFile. This means that we | |
278 // need to manually use placement new to construct the C++ object which owns | |
279 // the pipe to our file. | |
280 new (&GetFSFile(file)) filesystem::FilePtr(file_ptr.Pass()); | |
281 | |
282 return SQLITE_OK; | |
283 } | |
284 | |
285 int MojoVFSDelete(sqlite3_vfs* mojo_vfs, const char* filename, int sync_dir) { | |
286 DVLOG(1) << "MojoVFSDelete(*, " << filename << ", " << sync_dir << ")"; | |
287 // TODO(erg): The default windows sqlite VFS has retry code to work around | |
288 // antivirus software keeping files open. We'll probably have to do something | |
289 // 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
| |
290 filesystem::FileError error = filesystem::FILE_ERROR_FAILED; | |
291 GetRootDirectory(mojo_vfs)->Delete(filename, 0, Capture(&error)); | |
292 GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); | |
293 | |
294 if (error == filesystem::FILE_ERROR_OK && sync_dir) { | |
295 GetRootDirectory(mojo_vfs)->Flush(Capture(&error)); | |
296 GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); | |
297 } | |
298 | |
299 return error == filesystem::FILE_ERROR_OK ? SQLITE_OK : SQLITE_IOERR_DELETE; | |
300 } | |
301 | |
302 int MojoVFSAccess(sqlite3_vfs* mojo_vfs, | |
303 const char* filename, | |
304 int flags, | |
305 int* result) { | |
306 DVLOG(1) << "MojoVFSAccess(*, " << filename << ", " << flags << ", *)"; | |
307 filesystem::FileError error = filesystem::FILE_ERROR_FAILED; | |
308 | |
309 if (flags == SQLITE_ACCESS_READWRITE || flags == SQLITE_ACCESS_READ) { | |
310 bool is_writable = false; | |
311 GetRootDirectory(mojo_vfs) | |
312 ->IsWritable(filename, Capture(&error, &is_writable)); | |
313 GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); | |
314 *result = is_writable; | |
315 return SQLITE_OK; | |
316 } | |
317 | |
318 if (flags == SQLITE_ACCESS_EXISTS) { | |
319 bool exists = false; | |
320 GetRootDirectory(mojo_vfs)->Exists(filename, Capture(&error, &exists)); | |
321 GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); | |
322 *result = exists; | |
323 return SQLITE_OK; | |
324 } | |
325 | |
326 return SQLITE_IOERR; | |
327 } | |
328 | |
329 int MojoVFSFullPathname(sqlite3_vfs* mojo_vfs, | |
330 const char* relative_path, | |
331 int absolute_path_size, | |
332 char* absolute_path) { | |
333 // The sandboxed process doesn't need to know the absolute path of the file. | |
334 sqlite3_snprintf(absolute_path_size, absolute_path, "%s", relative_path); | |
335 return SQLITE_OK; | |
336 } | |
337 | |
338 // Don't let SQLite dynamically load things. (If we are using the | |
339 // mojo:filesystem proxying VFS, then it's highly likely that we are sandboxed | |
340 // and that any attempt to dlopen() a shared object is folly.) | |
341 void* MojoVFSDlOpen(sqlite3_vfs*, const char*) { | |
342 return 0; | |
343 } | |
344 | |
345 void MojoVFSDlError(sqlite3_vfs*, int buf_size, char* error_msg) { | |
346 sqlite3_snprintf(buf_size, error_msg, "Dynamic loading not supported"); | |
347 } | |
348 | |
349 void (*MojoVFSDlSym(sqlite3_vfs*, void*, const char*))(void) { | |
350 return 0; | |
351 } | |
352 | |
353 void MojoVFSDlClose(sqlite3_vfs*, void*) { | |
354 return; | |
355 } | |
356 | |
357 int MojoVFSRandomness(sqlite3_vfs* mojo_vfs, int size, char* out) { | |
358 base::RandBytes(out, size); | |
359 return size; | |
360 } | |
361 | |
362 // Proxy the rest of the calls down to the OS specific handler. | |
363 int MojoVFSSleep(sqlite3_vfs* mojo_vfs, int micro) { | |
364 return GetParentVFS(mojo_vfs)->xSleep(GetParentVFS(mojo_vfs), micro); | |
365 } | |
366 | |
367 int MojoVFSCurrentTime(sqlite3_vfs* mojo_vfs, double* time) { | |
368 return GetParentVFS(mojo_vfs)->xCurrentTime(GetParentVFS(mojo_vfs), time); | |
369 } | |
370 | |
371 int MojoVFSGetLastError(sqlite3_vfs* mojo_vfs, int a, char* b) { | |
372 return 0; | |
373 } | |
374 | |
375 int MojoVFSCurrentTimeInt64(sqlite3_vfs* mojo_vfs, sqlite3_int64* out) { | |
376 return GetParentVFS(mojo_vfs)->xCurrentTimeInt64(GetParentVFS(mojo_vfs), out); | |
377 } | |
378 | |
379 static sqlite3_vfs mojo_vfs = { | |
380 1, /* iVersion */ | |
381 sizeof(MojoVFSFile), /* szOsFile */ | |
382 kMaxPathName, /* mxPathname */ | |
383 0, /* pNext */ | |
384 "mojo", /* zName */ | |
385 0, /* pAppData */ | |
386 MojoVFSOpen, /* xOpen */ | |
387 MojoVFSDelete, /* xDelete */ | |
388 MojoVFSAccess, /* xAccess */ | |
389 MojoVFSFullPathname, /* xFullPathname */ | |
390 MojoVFSDlOpen, /* xDlOpen */ | |
391 MojoVFSDlError, /* xDlError */ | |
392 MojoVFSDlSym, /* xDlSym */ | |
393 MojoVFSDlClose, /* xDlClose */ | |
394 MojoVFSRandomness, /* xRandomness */ | |
395 MojoVFSSleep, /* xSleep */ | |
396 MojoVFSCurrentTime, /* xCurrentTime */ | |
397 MojoVFSGetLastError, /* xGetLastError */ | |
398 MojoVFSCurrentTimeInt64 /* xCurrentTimeInt64 */ | |
399 }; | |
400 | |
401 } // namespace | |
402 | |
403 ScopedMojoFilesystemVFS::ScopedMojoFilesystemVFS( | |
404 filesystem::DirectoryPtr root_directory) { | |
405 CHECK(!mojo_vfs.pAppData); | |
406 | |
407 MojoVFSData* data = new MojoVFSData; | |
408 data->parent = sqlite3_vfs_find(NULL); | |
409 data->root_directory = root_directory.Pass(); | |
410 mojo_vfs.pAppData = data; | |
411 | |
412 mojo_vfs.mxPathname = data->parent->mxPathname; | |
413 | |
414 CHECK(sqlite3_vfs_register(&mojo_vfs, 1) == SQLITE_OK); | |
415 } | |
416 | |
417 ScopedMojoFilesystemVFS::~ScopedMojoFilesystemVFS() { | |
418 CHECK(mojo_vfs.pAppData); | |
419 | |
420 MojoVFSData* data = static_cast<MojoVFSData*>(mojo_vfs.pAppData); | |
421 sqlite3_vfs* previous_fs = data->parent; | |
422 delete data; | |
423 mojo_vfs.pAppData = nullptr; | |
424 | |
425 CHECK(sqlite3_vfs_unregister(&mojo_vfs) == SQLITE_OK); | |
426 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.
| |
427 } | |
428 | |
429 filesystem::DirectoryPtr& ScopedMojoFilesystemVFS::GetDirectory() { | |
430 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
| |
431 } | |
432 | |
433 } // namespace sql | |
OLD | NEW |