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/mojo_vfs.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 |
| 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 |