1 /*
2 * Copyright 2010, Axel Dörfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
4 */
5
6
7 #include <QueryFile.h>
8
9 #include <fs_attr.h>
10 #include <Volume.h>
11 #include <VolumeRoster.h>
12
13 #include "tracker/MimeTypes.h"
14 #include "tracker/Utilities.h"
15
16
17 // TODO: add write support
18 // TODO: let Tracker use it?
19 // TODO: live query support?
20
21
22 const char* kAttrQueryString = "_trk/qrystr";
23 const char* kAttrQueryVolume = "_trk/qryvol1";
24
25
BQueryFile(const entry_ref & ref)26 BQueryFile::BQueryFile(const entry_ref& ref)
27 {
28 SetTo(ref);
29 }
30
31
BQueryFile(const BEntry & entry)32 BQueryFile::BQueryFile(const BEntry& entry)
33 {
34 SetTo(entry);
35 }
36
37
BQueryFile(const char * path)38 BQueryFile::BQueryFile(const char* path)
39 {
40 SetTo(path);
41 }
42
43
BQueryFile(BQuery & query)44 BQueryFile::BQueryFile(BQuery& query)
45 {
46 SetTo(query);
47 }
48
49
~BQueryFile()50 BQueryFile::~BQueryFile()
51 {
52 }
53
54
55 status_t
InitCheck() const56 BQueryFile::InitCheck() const
57 {
58 return fStatus;
59 }
60
61
62 status_t
SetTo(const entry_ref & ref)63 BQueryFile::SetTo(const entry_ref& ref)
64 {
65 Unset();
66
67 BNode node(&ref);
68 fStatus = node.InitCheck();
69 if (fStatus != B_OK)
70 return fStatus;
71
72 ssize_t bytesRead = node.ReadAttrString(kAttrQueryString, &fPredicate);
73 if (bytesRead < 0)
74 return fStatus = bytesRead;
75
76 bool searchAllVolumes = true;
77 attr_info info;
78 if (node.GetAttrInfo(kAttrQueryVolume, &info) == B_OK) {
79 void* buffer = malloc(info.size);
80 if (buffer == NULL)
81 return fStatus = B_NO_MEMORY;
82
83 BMessage message;
84 fStatus = message.Unflatten((const char*)buffer);
85 if (fStatus == B_OK) {
86 for (int32 index = 0; index < 100; index++) {
87 BVolume volume;
88 status_t status = BPrivate::MatchArchivedVolume(&volume,
89 &message, index);
90 if (status == B_OK) {
91 fStatus = AddVolume(volume);
92 if (fStatus != B_OK)
93 break;
94
95 searchAllVolumes = false;
96 } else if (status != B_DEV_BAD_DRIVE_NUM) {
97 // Volume doesn't seem to be mounted
98 fStatus = status;
99 break;
100 }
101 }
102 }
103
104 free(buffer);
105 }
106
107 if (searchAllVolumes) {
108 // add all volumes to query
109 BVolumeRoster roster;
110 BVolume volume;
111 while (roster.GetNextVolume(&volume) == B_OK) {
112 if (volume.IsPersistent() && volume.KnowsQuery())
113 AddVolume(volume);
114 }
115 }
116
117 return fStatus;
118 }
119
120
121 status_t
SetTo(const BEntry & entry)122 BQueryFile::SetTo(const BEntry& entry)
123 {
124 entry_ref ref;
125 fStatus = entry.GetRef(&ref);
126 if (fStatus != B_OK)
127 return fStatus;
128
129 return SetTo(ref);
130 }
131
132
133 status_t
SetTo(const char * path)134 BQueryFile::SetTo(const char* path)
135 {
136 entry_ref ref;
137 fStatus = get_ref_for_path(path, &ref);
138 if (fStatus != B_OK)
139 return fStatus;
140
141 return SetTo(ref);
142 }
143
144
145 status_t
SetTo(BQuery & query)146 BQueryFile::SetTo(BQuery& query)
147 {
148 Unset();
149
150 BString predicate;
151 query.GetPredicate(&predicate);
152
153 fStatus = SetPredicate(predicate.String());
154 if (fStatus != B_OK)
155 return fStatus;
156
157 return fStatus = AddVolume(query.TargetDevice());
158 }
159
160
161 void
Unset()162 BQueryFile::Unset()
163 {
164 fStatus = B_NO_INIT;
165 fCurrentVolumeIndex = -1;
166 fVolumes.MakeEmpty();
167 fQuery.Clear();
168 fPredicate = "";
169 }
170
171
172 status_t
SetPredicate(const char * predicate)173 BQueryFile::SetPredicate(const char* predicate)
174 {
175 fPredicate = predicate;
176 return B_OK;
177 }
178
179
180 status_t
AddVolume(const BVolume & volume)181 BQueryFile::AddVolume(const BVolume& volume)
182 {
183 return fVolumes.AddItem((void*)(addr_t)volume.Device()) ? B_OK : B_NO_MEMORY;
184 }
185
186
187 status_t
AddVolume(dev_t device)188 BQueryFile::AddVolume(dev_t device)
189 {
190 return fVolumes.AddItem((void*)(addr_t)device) ? B_OK : B_NO_MEMORY;
191 }
192
193
194 const char*
Predicate() const195 BQueryFile::Predicate() const
196 {
197 return fPredicate.String();
198 }
199
200
201 int32
CountVolumes() const202 BQueryFile::CountVolumes() const
203 {
204 return fVolumes.CountItems();
205 }
206
207
208 dev_t
VolumeAt(int32 index) const209 BQueryFile::VolumeAt(int32 index) const
210 {
211 if (index < 0 || index >= fVolumes.CountItems())
212 return -1;
213
214 return (dev_t)(addr_t)fVolumes.ItemAt(index);
215 }
216
217
218 status_t
WriteTo(const entry_ref & ref)219 BQueryFile::WriteTo(const entry_ref& ref)
220 {
221 // TODO: implement
222 return B_NOT_SUPPORTED;
223 }
224
225
226 status_t
WriteTo(const char * path)227 BQueryFile::WriteTo(const char* path)
228 {
229 entry_ref ref;
230 status_t status = get_ref_for_path(path, &ref);
231 if (status != B_OK)
232 return status;
233
234 return WriteTo(ref);
235 }
236
237
238 // #pragma mark - BEntryList implementation
239
240
241 status_t
GetNextEntry(BEntry * entry,bool traverse)242 BQueryFile::GetNextEntry(BEntry* entry, bool traverse)
243 {
244 if (fCurrentVolumeIndex == -1) {
245 // Start with first volume
246 fCurrentVolumeIndex = 0;
247
248 status_t status = _SetQuery(0);
249 if (status != B_OK)
250 return status;
251 }
252
253 status_t status = B_ENTRY_NOT_FOUND;
254
255 while (fCurrentVolumeIndex < CountVolumes()) {
256 status = fQuery.GetNextEntry(entry, traverse);
257 if (status != B_ENTRY_NOT_FOUND)
258 break;
259
260 // Continue with next volume, if any
261 status = _SetQuery(++fCurrentVolumeIndex);
262 }
263
264 return status;
265 }
266
267
268 status_t
GetNextRef(entry_ref * ref)269 BQueryFile::GetNextRef(entry_ref* ref)
270 {
271 if (fCurrentVolumeIndex == -1) {
272 // Start with first volume
273 fCurrentVolumeIndex = 0;
274
275 status_t status = _SetQuery(0);
276 if (status != B_OK)
277 return status;
278 }
279
280 status_t status = B_ENTRY_NOT_FOUND;
281
282 while (fCurrentVolumeIndex < CountVolumes()) {
283 status = fQuery.GetNextRef(ref);
284 if (status != B_ENTRY_NOT_FOUND)
285 break;
286
287 // Continue with next volume, if any
288 status = _SetQuery(++fCurrentVolumeIndex);
289 }
290
291 return status;
292 }
293
294
295 int32
GetNextDirents(struct dirent * buffer,size_t length,int32 count)296 BQueryFile::GetNextDirents(struct dirent* buffer, size_t length, int32 count)
297 {
298 if (fCurrentVolumeIndex == -1) {
299 // Start with first volume
300 fCurrentVolumeIndex = 0;
301
302 status_t status = _SetQuery(0);
303 if (status != B_OK)
304 return status;
305 }
306
307 status_t status = B_ENTRY_NOT_FOUND;
308
309 while (fCurrentVolumeIndex < CountVolumes()) {
310 status = fQuery.GetNextDirents(buffer, length, count);
311 if (status != B_ENTRY_NOT_FOUND)
312 break;
313
314 // Continue with next volume, if any
315 status = _SetQuery(++fCurrentVolumeIndex);
316 }
317
318 return status;
319 }
320
321
322 status_t
Rewind()323 BQueryFile::Rewind()
324 {
325 fCurrentVolumeIndex = -1;
326 return B_OK;
327 }
328
329
330 int32
CountEntries()331 BQueryFile::CountEntries()
332 {
333 // not supported
334 return -1;
335 }
336
337
338 /*static*/ const char*
MimeType()339 BQueryFile::MimeType()
340 {
341 return B_QUERY_MIMETYPE;
342 }
343
344
345 status_t
_SetQuery(int32 index)346 BQueryFile::_SetQuery(int32 index)
347 {
348 if (fCurrentVolumeIndex >= CountVolumes())
349 return B_ENTRY_NOT_FOUND;
350
351 BVolume volume(VolumeAt(fCurrentVolumeIndex));
352 fQuery.Clear();
353 fQuery.SetPredicate(fPredicate.String());
354 fQuery.SetVolume(&volume);
355
356 return fQuery.Fetch();
357 }
358