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(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;
101		return score * (maxFactor
102			/ std::min(maxFactor,
103				std::max((int32)1, index.index->CountEntries())));
104	}
105
106	static type_code IndexGetType(Index& index)
107	{
108		return index.index->Type();
109	}
110
111	static int32 IndexGetKeySize(Index& index)
112	{
113		return index.index->KeyLength();
114	}
115
116	static IndexIterator* IndexCreateIterator(Index& index)
117	{
118		IndexIterator* iterator = new(std::nothrow) IndexIterator(index.index);
119		if (iterator == NULL)
120			return NULL;
121
122		if (!index.index->GetIterator(*iterator)) {
123			delete iterator;
124			return NULL;
125		}
126
127		return iterator;
128	}
129
130	// IndexIterator interface
131
132	static void IndexIteratorDelete(IndexIterator* indexIterator)
133	{
134		delete indexIterator;
135	}
136
137	static status_t IndexIteratorFind(IndexIterator* indexIterator,
138		const void* value, size_t size)
139	{
140		if (!indexIterator->index->Find(value, size, *indexIterator))
141			return B_ENTRY_NOT_FOUND;
142
143		return B_OK;
144	}
145
146	static status_t IndexIteratorGetNextEntry(IndexIterator* indexIterator,
147		void* value, size_t* _valueLength, size_t bufferSize, Entry** _entry)
148	{
149		Node* node = indexIterator->Next(value, _valueLength);
150		if (node == NULL)
151			return B_ENTRY_NOT_FOUND;
152
153		*_entry = node;
154		return B_OK;
155	}
156
157	static void IndexIteratorSuspend(IndexIterator* indexIterator)
158	{
159		indexIterator->Suspend();
160	}
161
162	static void IndexIteratorResume(IndexIterator* indexIterator)
163	{
164		indexIterator->Resume();
165	}
166
167	// Node interface
168
169	static const off_t NodeGetSize(Node* node)
170	{
171		return node->FileSize();
172	}
173
174	static time_t NodeGetLastModifiedTime(Node* node)
175	{
176		return node->ModifiedTime().tv_sec;
177	}
178
179	static status_t NodeGetAttribute(Node* node, const char* attribute,
180		void* buffer, size_t* _size, int32* _type)
181	{
182		// TODO: Creating a cookie is quite a bit of overhead.
183		AttributeCookie* cookie;
184		status_t error = node->OpenAttribute(attribute, O_RDONLY, 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