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