1/*
2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2023, Haiku, Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "Query.h"
8
9#include "Directory.h"
10#include "Entry.h"
11#include "Node.h"
12#include "Volume.h"
13#include "Index.h"
14
15#include <file_systems/QueryParser.h>
16
17
18// #pragma mark - IndexIterator
19
20
21class IndexIterator {
22public:
23	IndexIterator(Index *index);
24
25	status_t Find(const uint8 *const key, size_t keyLength);
26	status_t Rewind();
27	status_t GetNextEntry(uint8 *buffer, size_t *keyLength, Entry **entry);
28
29	Index* GetIndex() const { return fIndex; }
30
31private:
32	Index				*fIndex;
33	IndexEntryIterator	fIterator;
34	bool				fInitialized;
35};
36
37
38IndexIterator::IndexIterator(Index *index)
39	: fIndex(index),
40	  fIterator(),
41	  fInitialized(false)
42{
43}
44
45
46status_t
47IndexIterator::Find(const uint8 *const key, size_t keyLength)
48{
49	status_t error = B_ENTRY_NOT_FOUND;
50	if (fIndex) {
51		// TODO: We actually don't want an exact Find() here, but rather a
52		// FindClose().
53		fInitialized = fIndex->Find(key, keyLength, &fIterator);
54		if (fInitialized)
55			error = B_OK;
56	}
57	return error;
58}
59
60
61status_t
62IndexIterator::Rewind()
63{
64	status_t error = B_ENTRY_NOT_FOUND;
65	if (fIndex) {
66		fInitialized = fIndex->GetIterator(&fIterator);
67		if (fInitialized)
68			error = B_OK;
69	}
70	return error;
71}
72
73
74status_t
75IndexIterator::GetNextEntry(uint8 *buffer, size_t *_keyLength, Entry **_entry)
76{
77	status_t error = B_ENTRY_NOT_FOUND;
78	if (fIndex) {
79		// init iterator, if not done yet
80		if (!fInitialized) {
81			fIndex->GetIterator(&fIterator);
82			fInitialized = true;
83		}
84
85		// get key
86		size_t keyLength;
87		if (Entry *entry = fIterator.GetCurrent(buffer, &keyLength)) {
88			*_keyLength = keyLength;
89			*_entry = entry;
90			error = B_OK;
91		}
92
93		// get next entry
94		fIterator.GetNext();
95	}
96	return error;
97}
98
99
100// #pragma mark - QueryPolicy
101
102
103struct Query::QueryPolicy {
104	typedef Query Context;
105	typedef ::Entry Entry;
106	typedef ::Node Node;
107
108	struct Index {
109		Query*		query;
110		::Index*	index;
111
112		Index(Context* context)
113			:
114			query(context)
115		{
116		}
117	};
118
119	struct IndexIterator : ::IndexIterator {
120		IndexIterator(::Index* index)
121			:
122			::IndexIterator(index)
123		{
124		}
125	};
126
127	static const int32 kMaxFileNameLength = B_FILE_NAME_LENGTH;
128
129	// Entry interface
130
131	static ino_t EntryGetParentID(Entry* entry)
132	{
133		return entry->GetParent()->GetID();
134	}
135
136	static Node* EntryGetNode(Entry* entry)
137	{
138		return entry->GetNode();
139	}
140
141	static ino_t EntryGetNodeID(Entry* entry)
142	{
143		return entry->GetNode()->GetID();
144	}
145
146	static ssize_t EntryGetName(Entry* entry, void* buffer, size_t bufferSize)
147	{
148		const char* name = entry->GetName();
149		size_t nameLength = strlen(name);
150		if (nameLength >= bufferSize)
151			return B_BUFFER_OVERFLOW;
152
153		memcpy(buffer, name, nameLength + 1);
154		return nameLength + 1;
155	}
156
157	static const char* EntryGetNameNoCopy(Entry* entry, void* buffer,
158		size_t bufferSize)
159	{
160		return entry->GetName();
161	}
162
163	// Index interface
164
165	static status_t IndexSetTo(Index& index, const char* attribute)
166	{
167		index.index = index.query->fVolume->FindIndex(attribute);
168		return index.index != NULL ? B_OK : B_ENTRY_NOT_FOUND;
169	}
170
171	static void IndexUnset(Index& index)
172	{
173		index.index = NULL;
174	}
175
176	static int32 IndexGetWeightedScore(Index& index, int32 score)
177	{
178		// should be inversely proportional to the index size; max input score
179		// is 2048
180		static const int32 maxFactor = (1024 * 1024) - 1;
181		return score * (maxFactor /
182			std::min(maxFactor, std::max((int32)1, index.index->CountEntries())));
183	}
184
185	static type_code IndexGetType(Index& index)
186	{
187		return index.index->GetType();
188	}
189
190	static int32 IndexGetKeySize(Index& index)
191	{
192		return index.index->GetKeyLength();
193	}
194
195	static IndexIterator* IndexCreateIterator(Index& index)
196	{
197		IndexIterator* iterator = new(std::nothrow) IndexIterator(index.index);
198		if (iterator == NULL)
199			return NULL;
200
201		return iterator;
202	}
203
204	// IndexIterator interface
205
206	static void IndexIteratorDelete(IndexIterator* indexIterator)
207	{
208		delete indexIterator;
209	}
210
211	static status_t IndexIteratorFind(IndexIterator* indexIterator,
212		const void* value, size_t size)
213	{
214		return indexIterator->Find((const uint8*)value, size);
215	}
216
217	static status_t IndexIteratorGetNextEntry(IndexIterator* indexIterator,
218		void* value, size_t* _valueLength, size_t bufferSize, Entry** _entry)
219	{
220		return indexIterator->GetNextEntry((uint8*)value, _valueLength, _entry);
221	}
222
223	static void IndexIteratorSuspend(IndexIterator* indexIterator)
224	{
225		// Nothing to do.
226	}
227
228	static void IndexIteratorResume(IndexIterator* indexIterator)
229	{
230		// Nothing to do.
231	}
232
233	// Node interface
234
235	static const off_t NodeGetSize(Node* node)
236	{
237		return node->GetSize();
238	}
239
240	static time_t NodeGetLastModifiedTime(Node* node)
241	{
242		return node->GetMTime();
243	}
244
245	static status_t NodeGetAttribute(Node* node, const char* attribute,
246		void* buffer, size_t* _size, int32* _type)
247	{
248		Attribute* attr = NULL;
249		status_t error = node->FindAttribute(attribute, &attr);
250		if (error != B_OK)
251			return error;
252
253		*_type = attr->GetType();
254		error = attr->ReadAt(0, buffer, *_size, _size);
255
256		return error;
257	}
258
259	static Entry* NodeGetFirstReferrer(Node* node)
260	{
261		return node->GetFirstReferrer();
262	}
263
264	static Entry* NodeGetNextReferrer(Node* node, Entry* entry)
265	{
266		return node->GetNextReferrer(entry);
267	}
268
269	// Volume interface
270
271	static dev_t ContextGetVolumeID(Context* context)
272	{
273		return context->fVolume->GetID();
274	}
275};
276
277
278// #pragma mark - Query
279
280
281Query::Query(Volume* volume)
282	:
283	fVolume(volume),
284	fImpl(NULL)
285{
286}
287
288
289Query::~Query()
290{
291	if (fImpl != NULL) {
292		if ((fImpl->Flags() & B_LIVE_QUERY) != 0)
293			fVolume->RemoveQuery(this);
294
295		delete fImpl;
296	}
297}
298
299
300/*static*/ status_t
301Query::Create(Volume* volume, const char* queryString, uint32 flags,
302	port_id port, uint32 token, Query*& _query)
303{
304	Query* query = new(std::nothrow) Query(volume);
305	if (query == NULL)
306		return B_NO_MEMORY;
307
308	status_t error = query->_Init(queryString, flags, port, token);
309	if (error != B_OK) {
310		delete query;
311		return error;
312	}
313
314	_query = query;
315	return B_OK;
316}
317
318
319status_t
320Query::Rewind()
321{
322	return fImpl->Rewind();
323}
324
325
326status_t
327Query::GetNextEntry(struct dirent* entry, size_t size)
328{
329	return fImpl->GetNextEntry(entry, size);
330}
331
332
333void
334Query::LiveUpdate(Entry* entry, Node* node, const char* attribute, int32 type,
335	const void* oldKey, size_t oldLength, const void* newKey, size_t newLength)
336{
337	fImpl->LiveUpdate(entry, node, attribute, type, (const uint8*)oldKey,
338		oldLength, (const uint8*)newKey, newLength);
339}
340
341
342status_t
343Query::_Init(const char* queryString, uint32 flags, port_id port, uint32 token)
344{
345	status_t error = QueryImpl::Create(this, queryString, flags, port, token,
346		fImpl);
347	if (error != B_OK)
348		return error;
349
350	if ((fImpl->Flags() & B_LIVE_QUERY) != 0)
351		fVolume->AddQuery(this);
352
353	return B_OK;
354}
355