FileSystemModule.cpp (7029B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FileSystemModule.h" 8 9 #include "sqlite3.h" 10 #include "nsComponentManagerUtils.h" 11 #include "nsString.h" 12 #include "nsIDirectoryEnumerator.h" 13 #include "nsIFile.h" 14 15 namespace { 16 17 struct VirtualTableCursorBase { 18 VirtualTableCursorBase() { memset(&mBase, 0, sizeof(mBase)); } 19 20 sqlite3_vtab_cursor mBase; 21 }; 22 23 struct VirtualTableCursor : public VirtualTableCursorBase { 24 public: 25 VirtualTableCursor() : mRowId(-1) { mCurrentFileName.SetIsVoid(true); } 26 27 const nsString& DirectoryPath() const { return mDirectoryPath; } 28 29 const nsString& CurrentFileName() const { return mCurrentFileName; } 30 31 int64_t RowId() const { return mRowId; } 32 33 nsresult Init(const nsAString& aPath); 34 nsresult NextFile(); 35 36 private: 37 nsCOMPtr<nsIDirectoryEnumerator> mEntries; 38 39 nsString mDirectoryPath; 40 nsString mCurrentFileName; 41 42 int64_t mRowId; 43 }; 44 45 nsresult VirtualTableCursor::Init(const nsAString& aPath) { 46 nsCOMPtr<nsIFile> directory; 47 nsresult rv = (NS_NewLocalFile(aPath, getter_AddRefs(directory))); 48 NS_ENSURE_SUCCESS(rv, rv); 49 50 rv = directory->GetPath(mDirectoryPath); 51 NS_ENSURE_SUCCESS(rv, rv); 52 53 rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries)); 54 NS_ENSURE_SUCCESS(rv, rv); 55 56 rv = NextFile(); 57 NS_ENSURE_SUCCESS(rv, rv); 58 59 return NS_OK; 60 } 61 62 nsresult VirtualTableCursor::NextFile() { 63 bool hasMore; 64 nsresult rv = mEntries->HasMoreElements(&hasMore); 65 NS_ENSURE_SUCCESS(rv, rv); 66 67 if (!hasMore) { 68 mCurrentFileName.SetIsVoid(true); 69 return NS_OK; 70 } 71 72 nsCOMPtr<nsISupports> entry; 73 rv = mEntries->GetNext(getter_AddRefs(entry)); 74 NS_ENSURE_SUCCESS(rv, rv); 75 76 nsCOMPtr<nsIFile> file = do_QueryInterface(entry); 77 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); 78 79 rv = file->GetLeafName(mCurrentFileName); 80 NS_ENSURE_SUCCESS(rv, rv); 81 82 mRowId++; 83 84 return NS_OK; 85 } 86 87 int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv, 88 sqlite3_vtab** aVtab, char** aErr) { 89 static const char virtualTableSchema[] = 90 "CREATE TABLE fs (" 91 "name TEXT, " 92 "path TEXT" 93 ")"; 94 95 int rc = sqlite3_declare_vtab(aDB, virtualTableSchema); 96 if (rc != SQLITE_OK) { 97 return rc; 98 } 99 100 sqlite3_vtab* vt = new sqlite3_vtab(); 101 memset(vt, 0, sizeof(*vt)); 102 103 *aVtab = vt; 104 105 return SQLITE_OK; 106 } 107 108 int Disconnect(sqlite3_vtab* aVtab) { 109 delete aVtab; 110 111 return SQLITE_OK; 112 } 113 114 int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo) { 115 // Here we specify what index constraints we want to handle. That is, there 116 // might be some columns with particular constraints in which we can help 117 // SQLite narrow down the result set. 118 // 119 // For example, take the "path = x" where x is a directory. In this case, 120 // we can narrow our search to just this directory instead of the entire file 121 // system. This can be a significant optimization. So, we want to handle that 122 // constraint. To do so, we would look for two specific input conditions: 123 // 124 // 1. aInfo->aConstraint[i].iColumn == 1 125 // 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ 126 // 127 // The first states that the path column is being used in one of the input 128 // constraints and the second states that the constraint involves the equal 129 // operator. 130 // 131 // An even more specific search would be for name='xxx', in which case we 132 // can limit the search to a single file, if it exists. 133 // 134 // What we have to do here is look for all of our index searches and select 135 // the narrowest. We can only pick one, so obviously we want the one that 136 // is the most specific, which leads to the smallest result set. 137 138 for (int i = 0; i < aInfo->nConstraint; i++) { 139 if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) { 140 if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) { 141 aInfo->aConstraintUsage[i].argvIndex = 1; 142 } 143 break; 144 } 145 146 // TODO: handle single files (constrained also by the name column) 147 } 148 149 return SQLITE_OK; 150 } 151 152 int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor) { 153 VirtualTableCursor* cursor = new VirtualTableCursor(); 154 155 *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor); 156 157 return SQLITE_OK; 158 } 159 160 int Close(sqlite3_vtab_cursor* aCursor) { 161 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 162 163 delete cursor; 164 165 return SQLITE_OK; 166 } 167 168 int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr, 169 int aArgc, sqlite3_value** aArgv) { 170 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 171 172 if (aArgc <= 0) { 173 return SQLITE_OK; 174 } 175 176 const char16_t* value = 177 static_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0])); 178 179 nsDependentString path(value, 180 ::sqlite3_value_bytes16(aArgv[0]) / sizeof(char16_t)); 181 182 nsresult rv = cursor->Init(path); 183 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR); 184 185 return SQLITE_OK; 186 } 187 188 int Next(sqlite3_vtab_cursor* aCursor) { 189 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 190 191 nsresult rv = cursor->NextFile(); 192 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR); 193 194 return SQLITE_OK; 195 } 196 197 int Eof(sqlite3_vtab_cursor* aCursor) { 198 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 199 return cursor->CurrentFileName().IsVoid() ? 1 : 0; 200 } 201 202 int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext, 203 int aColumnIndex) { 204 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 205 206 switch (aColumnIndex) { 207 // name 208 case 0: { 209 const nsString& name = cursor->CurrentFileName(); 210 sqlite3_result_text16(aContext, name.get(), 211 name.Length() * sizeof(char16_t), SQLITE_TRANSIENT); 212 break; 213 } 214 215 // path 216 case 1: { 217 const nsString& path = cursor->DirectoryPath(); 218 sqlite3_result_text16(aContext, path.get(), 219 path.Length() * sizeof(char16_t), SQLITE_TRANSIENT); 220 break; 221 } 222 default: 223 MOZ_ASSERT_UNREACHABLE("Unsupported column!"); 224 } 225 226 return SQLITE_OK; 227 } 228 229 int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid) { 230 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 231 232 *aRowid = cursor->RowId(); 233 234 return SQLITE_OK; 235 } 236 237 } // namespace 238 239 namespace mozilla { 240 namespace storage { 241 242 int RegisterFileSystemModule(sqlite3* aDB, const char* aName) { 243 static sqlite3_module module = { 244 1, Connect, Connect, BestIndex, Disconnect, Disconnect, Open, 245 Close, Filter, Next, Eof, Column, RowId, nullptr, 246 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; 247 248 return sqlite3_create_module(aDB, aName, &module, nullptr); 249 } 250 251 } // namespace storage 252 } // namespace mozilla