1// AttributeDirectory.cpp
2
3#include <new>
4
5#include <stdlib.h>
6#include <string.h>
7
8#include <AutoDeleter.h>
9#include <Node.h>
10
11#include "AttributeDirectory.h"
12
13// small data size
14static const int32 kSmallAttributeSize = 8;
15
16// constructor
17Attribute::Attribute(const char* name, const attr_info& info,
18	const void* data)
19	: fInfo(info)
20{
21	char* nameBuffer = fDataAndName;
22
23	// copy data, if any
24	if (data) {
25		nameBuffer += info.size;
26		memcpy(fDataAndName, data, info.size);
27
28		// store a negative size to indicate we also have the data
29		fInfo.size = -info.size;
30	}
31
32	// copy the name
33	strcpy(nameBuffer, name);
34}
35
36// destructor
37Attribute::~Attribute()
38{
39}
40
41// CreateAttribute
42status_t
43Attribute::CreateAttribute(const char* name, const attr_info& info,
44	const void* data, Attribute** attribute)
45{
46	if (!name || !attribute)
47		return B_BAD_VALUE;
48
49	// compute the size
50	int32 nameLen = strlen(name);
51	int32 size = sizeof(Attribute) + nameLen;
52	if (data)
53		size += info.size;
54
55	void* buffer = malloc(size);
56	if (!buffer)
57		return B_NO_MEMORY;
58
59	*attribute = new(buffer) Attribute(name, info, data);
60	return B_OK;
61}
62
63// DeleteAttribute
64void
65Attribute::DeleteAttribute(Attribute* attribute)
66{
67	if (attribute) {
68		attribute->~Attribute();
69		free(attribute);
70	}
71}
72
73// GetName
74const char*
75Attribute::GetName() const
76{
77	return (fInfo.size >= 0 ? fDataAndName : fDataAndName - fInfo.size);
78}
79
80// GetInfo
81void
82Attribute::GetInfo(attr_info* info) const
83{
84	if (info) {
85		info->type = fInfo.type;
86		info->size = GetSize();
87	}
88}
89
90// GetType
91uint32
92Attribute::GetType() const
93{
94	return fInfo.type;
95}
96
97// GetSize
98off_t
99Attribute::GetSize() const
100{
101	return (fInfo.size >= 0 ? fInfo.size : -fInfo.size);
102}
103
104// GetData
105const void*
106Attribute::GetData() const
107{
108	return (fInfo.size >= 0 ? NULL : fDataAndName);
109}
110
111
112// #pragma mark -
113
114// constructor
115AttributeDirectory::AttributeDirectory()
116	: fAttributes(),
117	  fStatus(ATTRIBUTE_DIRECTORY_NOT_LOADED)
118{
119}
120
121// destructor
122AttributeDirectory::~AttributeDirectory()
123{
124	ClearAttrDir();
125}
126
127// GetAttrDirStatus
128uint32
129AttributeDirectory::GetAttrDirStatus() const
130{
131	return fStatus;
132}
133
134// IsAttrDirValid
135bool
136AttributeDirectory::IsAttrDirValid() const
137{
138	return (fStatus == ATTRIBUTE_DIRECTORY_VALID);
139}
140
141// LoadAttrDir
142status_t
143AttributeDirectory::LoadAttrDir()
144{
145	// nothing to do, if already loaded
146	if (fStatus == ATTRIBUTE_DIRECTORY_VALID)
147		return B_OK;
148
149	// if we tried earlier and the attr dir was too big, we fail again
150	if (fStatus == ATTRIBUTE_DIRECTORY_TOO_BIG)
151		return B_ERROR;
152
153	// open the node
154	BNode node;
155	status_t error = OpenNode(node);
156	if (error != B_OK)
157		return error;
158
159	// iterate through the attribute directory
160	char name[B_ATTR_NAME_LENGTH];
161	while (node.GetNextAttrName(name) == B_OK) {
162		// get the attribute data
163		attr_info info;
164		char data[kSmallAttributeSize];
165		bool dataLoaded = false;
166		error = _LoadAttribute(node, name, info, data, dataLoaded);
167		if (error != B_OK)
168			break;
169
170		// create the attribute
171		error = AddAttribute(name, info, (dataLoaded ? data : NULL));
172	}
173
174	if (error != B_OK)
175		ClearAttrDir();
176// TODO: Enforce maximum attribute directory size.
177
178	return error;
179}
180
181// ClearAttrDir
182void
183AttributeDirectory::ClearAttrDir()
184{
185	while (Attribute* attribute = GetFirstAttribute())
186		RemoveAttribute(attribute);
187}
188
189// AddAttribute
190status_t
191AttributeDirectory::AddAttribute(const char* name, const attr_info& info,
192	const void* data)
193{
194	if (!name || GetAttribute(name))
195		return B_BAD_VALUE;
196
197	// create the attribute
198	Attribute* attribute;
199	status_t error = Attribute::CreateAttribute(name, info, data, &attribute);
200	if (error != B_OK)
201		return error;
202
203	// add the attribute
204	fAttributes.Insert(attribute);
205
206	return B_OK;
207}
208
209// RemoveAttribute
210bool
211AttributeDirectory::RemoveAttribute(const char* name)
212{
213	if (!name)
214		return false;
215
216	for (SLList<Attribute>::Iterator it = fAttributes.GetIterator();
217		 it.HasNext();) {
218		Attribute* attribute = it.Next();
219		if (strcmp(attribute->GetName(), name) == 0) {
220			it.Remove();
221			Attribute::DeleteAttribute(attribute);
222			return true;
223		}
224	}
225
226	return false;
227}
228
229// RemoveAttribute
230void
231AttributeDirectory::RemoveAttribute(Attribute* attribute)
232{
233	if (!attribute)
234		return;
235
236	fAttributes.Remove(attribute);
237	Attribute::DeleteAttribute(attribute);
238}
239
240// UpdateAttribute
241status_t
242AttributeDirectory::UpdateAttribute(const char* name, bool* removed,
243	attr_info* _info, const void** _data)
244{
245	if (!name || !removed)
246		return B_BAD_VALUE;
247
248	// open the node
249	BNode node;
250	status_t error = OpenNode(node);
251	if (error != B_OK) {
252		ClearAttrDir();
253		if (fStatus == ATTRIBUTE_DIRECTORY_VALID)
254			fStatus = ATTRIBUTE_DIRECTORY_NOT_LOADED;
255		return error;
256	}
257
258	// get the attribute data
259	attr_info info;
260	char data[kSmallAttributeSize];
261	bool dataLoaded = false;
262	error = _LoadAttribute(node, name, info,
263		(fStatus == ATTRIBUTE_DIRECTORY_VALID ? data : NULL), dataLoaded);
264	if (error == B_OK) {
265		if (fStatus == ATTRIBUTE_DIRECTORY_VALID) {
266			// remove the attribute
267			Attribute* previous = NULL;
268			for (SLList<Attribute>::Iterator it = fAttributes.GetIterator();
269				 it.HasNext();) {
270				Attribute* attribute = it.Next();
271				if (strcmp(attribute->GetName(), name) == 0) {
272					it.Remove();
273					Attribute::DeleteAttribute(attribute);
274					break;
275				}
276				previous = attribute;
277			}
278
279// TODO: Enforce the maximum attr dir size.
280			// re-create the attribute
281			Attribute* attribute;
282			error = Attribute::CreateAttribute(name, info, data,
283				&attribute);
284			if (error == B_OK) {
285				// add the attribute
286				fAttributes.InsertAfter(previous, attribute);
287
288				// return the desired info
289				if (_info)
290					attribute->GetInfo(_info);
291				if (_data)
292					*_data = attribute->GetData();
293				*removed = false;
294			}
295		} else if (error == B_OK) {
296			if (_info)
297				*_info = info;
298			if (_data)
299				*_data = NULL;
300			*removed = false;
301		}
302	} else {
303		*removed = true;
304		RemoveAttribute(name);
305		error = B_OK;
306	}
307
308	// clean up on error
309	if (error != B_OK) {
310		ClearAttrDir();
311		if (fStatus == ATTRIBUTE_DIRECTORY_VALID)
312			fStatus = ATTRIBUTE_DIRECTORY_NOT_LOADED;
313	}
314
315	return error;
316}
317
318// GetAttribute
319Attribute*
320AttributeDirectory::GetAttribute(const char* name) const
321{
322	if (!name)
323		return NULL;
324
325	for (SLList<Attribute>::ConstIterator it = fAttributes.GetIterator();
326		 it.HasNext();) {
327		Attribute* attribute = it.Next();
328		if (strcmp(attribute->GetName(), name) == 0)
329			return attribute;
330	}
331
332	return NULL;
333}
334
335// GetFirstAttribute
336Attribute*
337AttributeDirectory::GetFirstAttribute() const
338{
339	return fAttributes.GetFirst();
340}
341
342// GetNextAttribute
343Attribute*
344AttributeDirectory::GetNextAttribute(Attribute* attribute) const
345{
346	return (attribute ? fAttributes.GetNext(attribute) : NULL);
347}
348
349// _LoadAttribute
350status_t
351AttributeDirectory::_LoadAttribute(BNode& node, const char* name,
352	attr_info& info, void* data, bool& dataLoaded)
353{
354	// stat the attribute
355	status_t error = node.GetAttrInfo(name, &info);
356	if (error != B_OK)
357		return error;
358
359	// if the data are small enough, read them
360	if (data && info.size <= kSmallAttributeSize) {
361		ssize_t bytesRead = node.ReadAttr(name, info.type, 0, data,
362			info.size);
363		dataLoaded = (bytesRead == info.size);
364	}
365
366	return B_OK;
367}
368
369