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