1/*
2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "Query.h"
8
9#include <file_systems/QueryParser.h>
10
11#include "AttributeCookie.h"
12#include "Directory.h"
13#include "Index.h"
14#include "Node.h"
15#include "Volume.h"
16
17
18// #pragma mark - QueryPolicy
19
20
21struct Query::QueryPolicy {
22	typedef Query Context;
23	typedef ::Node Entry;
24	typedef ::Node Node;
25
26	struct Index {
27		Query*		query;
28		::Index*	index;
29
30		Index(Context* context)
31			:
32			query(context)
33		{
34		}
35	};
36
37	struct IndexIterator : ::IndexIterator {
38		::Index*			index;
39
40		IndexIterator(::Index* index)
41			:
42			index(index)
43		{
44		}
45	};
46
47	static const int32 kMaxFileNameLength = B_FILE_NAME_LENGTH;
48
49	// Entry interface
50
51	static ino_t EntryGetParentID(Entry* entry)
52	{
53		return entry->Parent()->ID();
54	}
55
56	static Node* EntryGetNode(Entry* entry)
57	{
58		return entry;
59	}
60
61	static ino_t EntryGetNodeID(Entry* entry)
62	{
63		return entry->ID();
64	}
65
66	static ssize_t EntryGetName(Entry* entry, void* buffer, size_t bufferSize)
67	{
68		const char* name = entry->Name();
69		size_t nameLength = strlen(name);
70		if (nameLength >= bufferSize)
71			return B_BUFFER_OVERFLOW;
72
73		memcpy(buffer, name, nameLength + 1);
74		return nameLength + 1;
75	}
76
77	static const char* EntryGetNameNoCopy(Entry* entry, void* buffer,
78		size_t bufferSize)
79	{
80		return entry->Name();
81	}
82
83	// Index interface
84
85	static status_t IndexSetTo(Index& index, const char* attribute)
86	{
87		index.index = index.query->fVolume->FindIndex(StringKey(attribute));
88		return index.index != NULL ? B_OK : B_ENTRY_NOT_FOUND;
89	}
90
91	static void IndexUnset(Index& index)
92	{
93		index.index = NULL;
94	}
95
96	static int32 IndexGetWeightedScore(Index& index, int32 score)
97	{
98		// should be inversely proportional to the index size; max input score
99		// is 2048
100		static const int32 maxFactor = (1024 * 1024) - 1;
101		return score * (maxFactor /
102			std::min(maxFactor, std::max((int32)1, index.index->CountEntries())));
103	}
104
105	static type_code IndexGetType(Index& index)
106	{
107		return index.index->Type();
108	}
109
110	static int32 IndexGetKeySize(Index& index)
111	{
112		return index.index->KeyLength();
113	}
114
115	static IndexIterator* IndexCreateIterator(Index& index)
116	{
117		IndexIterator* iterator = new(std::nothrow) IndexIterator(index.index);
118		if (iterator == NULL)
119			return NULL;
120
121		if (!index.index->GetIterator(*iterator)) {
122			delete iterator;
123			return NULL;
124		}
125
126		return iterator;
127	}
128
129	// IndexIterator interface
130
131	static void IndexIteratorDelete(IndexIterator* indexIterator)
132	{
133		delete indexIterator;
134	}
135
136	static status_t IndexIteratorFind(IndexIterator* indexIterator,
137		const void* value, size_t size)
138	{
139		if (!indexIterator->index->Find(value, size, *indexIterator))
140			return B_ENTRY_NOT_FOUND;
141
142		return B_OK;
143	}
144
145	static status_t IndexIteratorGetNextEntry(IndexIterator* indexIterator,
146		void* value, size_t* _valueLength, size_t bufferSize, Entry** _entry)
147	{
148		Node* node = indexIterator->Next(value, _valueLength);
149		if (node == NULL)
150			return B_ENTRY_NOT_FOUND;
151
152		*_entry = node;
153		return B_OK;
154	}
155
156	static void IndexIteratorSuspend(IndexIterator* indexIterator)
157	{
158		indexIterator->Suspend();
159	}
160
161	static void IndexIteratorResume(IndexIterator* indexIterator)
162	{
163		indexIterator->Resume();
164	}
165
166	// Node interface
167
168	static const off_t NodeGetSize(Node* node)
169	{
170		return node->FileSize();
171	}
172
173	static time_t NodeGetLastModifiedTime(Node* node)
174	{
175		return node->ModifiedTime().tv_sec;
176	}
177
178	static status_t NodeGetAttribute(Node* node, const char* attribute,
179		void* buffer, size_t* _size, int32* _type)
180	{
181		// TODO: Creating a cookie is quite a bit of overhead.
182		AttributeCookie* cookie;
183		status_t error = node->OpenAttribute(StringKey(attribute), O_RDONLY,
184			cookie);
185		if (error != B_OK)
186			return error;
187
188		error = cookie->ReadAttribute(0, buffer, _size);
189
190		// also get the attribute type
191		if (error == B_OK) {
192			struct stat st;
193			error = cookie->ReadAttributeStat(&st);
194			if (error == B_OK)
195				*_type = st.st_type;
196		}
197
198		cookie->Close();
199		delete cookie;
200
201		return error;
202	}
203
204	static Entry* NodeGetFirstReferrer(Node* node)
205	{
206		return node;
207	}
208
209	static Entry* NodeGetNextReferrer(Node* node, Entry* entry)
210	{
211		return NULL;
212	}
213
214	// Volume interface
215
216	static dev_t ContextGetVolumeID(Context* context)
217	{
218		return context->fVolume->ID();
219	}
220};
221
222
223// #pragma mark - Query
224
225
226Query::Query(Volume* volume)
227	:
228	fVolume(volume),
229	fImpl(NULL)
230{
231}
232
233
234Query::~Query()
235{
236	if (fImpl != NULL) {
237		if ((fImpl->Flags() & B_LIVE_QUERY) != 0)
238			fVolume->RemoveQuery(this);
239
240		delete fImpl;
241	}
242}
243
244
245/*static*/ status_t
246Query::Create(Volume* volume, const char* queryString, uint32 flags,
247	port_id port, uint32 token, Query*& _query)
248{
249	Query* query = new(std::nothrow) Query(volume);
250	if (query == NULL)
251		return B_NO_MEMORY;
252
253	status_t error = query->_Init(queryString, flags, port, token);
254	if (error != B_OK) {
255		delete query;
256		return error;
257	}
258
259	_query = query;
260	return B_OK;
261}
262
263
264status_t
265Query::Rewind()
266{
267	return fImpl->Rewind();
268}
269
270
271status_t
272Query::GetNextEntry(struct dirent* entry, size_t size)
273{
274	return fImpl->GetNextEntry(entry, size);
275}
276
277
278void
279Query::LiveUpdate(Node* node, const char* attribute, int32 type,
280	const void* oldKey, size_t oldLength, const void* newKey, size_t newLength)
281{
282	fImpl->LiveUpdate(node, node, attribute, type, (const uint8*)oldKey,
283		oldLength, (const uint8*)newKey, newLength);
284}
285
286
287status_t
288Query::_Init(const char* queryString, uint32 flags, port_id port, uint32 token)
289{
290	status_t error = QueryImpl::Create(this, queryString, flags, port, token,
291		fImpl);
292	if (error != B_OK)
293		return error;
294
295	if ((fImpl->Flags() & B_LIVE_QUERY) != 0)
296		fVolume->AddQuery(this);
297
298	return B_OK;
299}
300