1/*
2 * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
5
6#include "AllocationInfo.h"
7#include "DebugSupport.h"
8#include "EntryIterator.h"
9#include "LastModifiedIndex.h"
10#include "Node.h"
11#include "Volume.h"
12
13// is_user_in_group
14inline static
15bool
16is_user_in_group(gid_t gid)
17{
18// Either I miss something, or we don't have getgroups() in the kernel. :-(
19/*
20	gid_t groups[NGROUPS_MAX];
21	int groupCount = getgroups(NGROUPS_MAX, groups);
22	for (int i = 0; i < groupCount; i++) {
23		if (gid == groups[i])
24			return true;
25	}
26*/
27	return (gid == getegid());
28}
29
30
31// constructor
32Node::Node(Volume *volume, uint8 type)
33	: fVolume(volume),
34	  fID(fVolume->NextNodeID()),
35	  fRefCount(0),
36	  fMode(0),
37	  fUID(0),
38	  fGID(0),
39	  fATime(0),
40	  fMTime(0),
41	  fCTime(0),
42	  fCrTime(0),
43	  fModified(0),
44	  fIsKnownToVFS(false),
45	  // attribute management
46	  fAttributes(),
47	  // referrers
48	  fReferrers()
49{
50	// set file type
51	switch (type) {
52		case NODE_TYPE_DIRECTORY:
53			fMode = S_IFDIR;
54			break;
55		case NODE_TYPE_FILE:
56			fMode = S_IFREG;
57			break;
58		case NODE_TYPE_SYMLINK:
59			fMode = S_IFLNK;
60			break;
61	}
62	// set defaults for time
63	fATime = fMTime = fCTime = fCrTime = time(NULL);
64}
65
66// destructor
67Node::~Node()
68{
69	ASSERT(fRefCount == 0);
70
71	// delete all attributes
72	while (Attribute *attribute = fAttributes.First()) {
73		status_t error = DeleteAttribute(attribute);
74		if (error != B_OK) {
75			FATAL("Node::~Node(): Failed to delete attribute!\n");
76			break;
77		}
78	}
79}
80
81// InitCheck
82status_t
83Node::InitCheck() const
84{
85	return (fVolume && fID >= 0 ? B_OK : B_NO_INIT);
86}
87
88// AddReference
89status_t
90Node::AddReference()
91{
92	if (++fRefCount == 1) {
93		status_t error = GetVolume()->PublishVNode(this);
94		if (error != B_OK) {
95			fRefCount--;
96			return error;
97		}
98
99		fIsKnownToVFS = true;
100	}
101
102	return B_OK;
103}
104
105// RemoveReference
106void
107Node::RemoveReference()
108{
109	ASSERT(fRefCount > 0);
110	if (--fRefCount == 0) {
111		GetVolume()->RemoveVNode(this);
112			// RemoveVNode can potentially delete us immediately!
113	}
114}
115
116// Link
117status_t
118Node::Link(Entry *entry)
119{
120PRINT("Node[%" B_PRIdINO "]::Link(): %" B_PRId32 " ->...\n", fID, fRefCount);
121	fReferrers.Insert(entry);
122
123	status_t error = AddReference();
124	if (error != B_OK)
125		fReferrers.Remove(entry);
126
127	return error;
128}
129
130// Unlink
131status_t
132Node::Unlink(Entry *entry)
133{
134PRINT("Node[%" B_PRIdINO "]::Unlink(): %" B_PRId32 " ->...\n", fID, fRefCount);
135	fReferrers.Remove(entry);
136
137	RemoveReference();
138	return B_OK;
139}
140
141// SetMTime
142void
143Node::SetMTime(time_t mTime)
144{
145	time_t oldMTime = fMTime;
146	fATime = fMTime = mTime;
147	if (oldMTime != fMTime) {
148		if (LastModifiedIndex *index = fVolume->GetLastModifiedIndex())
149			index->Changed(this, oldMTime);
150	}
151}
152
153// CheckPermissions
154status_t
155Node::CheckPermissions(int mode) const
156{
157	int userPermissions = (fMode & S_IRWXU) >> 6;
158	int groupPermissions = (fMode & S_IRWXG) >> 3;
159	int otherPermissions = fMode & S_IRWXO;
160	// get the permissions for this uid/gid
161	int permissions = 0;
162	uid_t uid = geteuid();
163	// user is root
164	if (uid == 0) {
165		// root has always read/write permission, but at least one of the
166		// X bits must be set for execute permission
167		permissions = userPermissions | groupPermissions | otherPermissions
168			| ACCESS_R | ACCESS_W;
169	// user is node owner
170	} else if (uid == fUID)
171		permissions = userPermissions;
172	// user is in owning group
173	else if (is_user_in_group(fGID))
174		permissions = groupPermissions;
175	// user is one of the others
176	else
177		permissions = otherPermissions;
178	// do the check
179	return ((mode & ~permissions) ? B_NOT_ALLOWED : B_OK);
180}
181
182// CreateAttribute
183status_t
184Node::CreateAttribute(const char *name, Attribute **_attribute)
185{
186	status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
187	if (error == B_OK) {
188		// create attribute
189		Attribute *attribute = new(nothrow) Attribute(fVolume, NULL, name);
190		if (attribute) {
191			error = attribute->InitCheck();
192			if (error == B_OK) {
193				// add attribute to node
194				error = AddAttribute(attribute);
195				if (error == B_OK)
196					*_attribute = attribute;
197			}
198			if (error != B_OK)
199				delete attribute;
200		} else
201			SET_ERROR(error, B_NO_MEMORY);
202	}
203	return error;
204}
205
206// DeleteAttribute
207status_t
208Node::DeleteAttribute(Attribute *attribute)
209{
210	status_t error = RemoveAttribute(attribute);
211	if (error == B_OK)
212		delete attribute;
213	return error;
214}
215
216// AddAttribute
217status_t
218Node::AddAttribute(Attribute *attribute)
219{
220	status_t error = (attribute && !attribute->GetNode() ? B_OK : B_BAD_VALUE);
221	if (error == B_OK) {
222		error = GetVolume()->NodeAttributeAdded(GetID(), attribute);
223		if (error == B_OK) {
224			fAttributes.Insert(attribute);
225			attribute->SetNode(this);
226			MarkModified(B_STAT_MODIFICATION_TIME);
227		}
228	}
229	return error;
230}
231
232// RemoveAttribute
233status_t
234Node::RemoveAttribute(Attribute *attribute)
235{
236	status_t error = (attribute && attribute->GetNode() == this
237					  ? B_OK : B_BAD_VALUE);
238	if (error == B_OK) {
239		// move all iterators pointing to the attribute to the next attribute
240		if (GetVolume()->IteratorLock()) {
241			// set the iterators' current entry
242			Attribute *nextAttr = fAttributes.GetNext(attribute);
243			DoublyLinkedList<AttributeIterator> *iterators
244				= attribute->GetAttributeIteratorList();
245			for (AttributeIterator *iterator = iterators->First();
246				 iterator;
247				 iterator = iterators->GetNext(iterator)) {
248				iterator->SetCurrent(nextAttr, true);
249			}
250			// Move the iterators from one list to the other, or just remove
251			// them, if there is no next attribute.
252			if (nextAttr) {
253				DoublyLinkedList<AttributeIterator> *nextIterators
254					= nextAttr->GetAttributeIteratorList();
255				nextIterators->MoveFrom(iterators);
256			} else
257				iterators->RemoveAll();
258			GetVolume()->IteratorUnlock();
259		} else
260			error = B_ERROR;
261		// remove the attribute
262		if (error == B_OK) {
263			error = GetVolume()->NodeAttributeRemoved(GetID(), attribute);
264			if (error == B_OK) {
265				fAttributes.Remove(attribute);
266				attribute->SetNode(NULL);
267				MarkModified(B_STAT_MODIFICATION_TIME);
268			}
269		}
270	}
271	return error;
272}
273
274// FindAttribute
275status_t
276Node::FindAttribute(const char *name, Attribute **_attribute) const
277{
278	status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
279	if (error == B_OK) {
280		Attribute *attribute = NULL;
281		while (GetNextAttribute(&attribute) == B_OK) {
282			if (!strcmp(attribute->GetName(), name)) {
283				*_attribute = attribute;
284				return B_OK;
285			}
286		}
287		error = B_ENTRY_NOT_FOUND;
288	}
289	return error;
290}
291
292// GetPreviousAttribute
293status_t
294Node::GetPreviousAttribute(Attribute **attribute) const
295{
296	status_t error = (attribute ? B_OK : B_BAD_VALUE);
297	if (error == B_OK) {
298		if (!*attribute)
299			*attribute = fAttributes.Last();
300		else if ((*attribute)->GetNode() == this)
301			*attribute = fAttributes.GetPrevious(*attribute);
302		else
303			error = B_BAD_VALUE;
304		if (error == B_OK && !*attribute)
305			error = B_ENTRY_NOT_FOUND;
306	}
307	return error;
308}
309
310// GetNextAttribute
311status_t
312Node::GetNextAttribute(Attribute **attribute) const
313{
314	status_t error = (attribute ? B_OK : B_BAD_VALUE);
315	if (error == B_OK) {
316		if (!*attribute)
317			*attribute = fAttributes.First();
318		else if ((*attribute)->GetNode() == this)
319			*attribute = fAttributes.GetNext(*attribute);
320		else
321			error = B_BAD_VALUE;
322		if (error == B_OK && !*attribute)
323			error = B_ENTRY_NOT_FOUND;
324	}
325	return error;
326}
327
328// GetFirstReferrer
329Entry *
330Node::GetFirstReferrer() const
331{
332	return fReferrers.First();
333}
334
335// GetLastReferrer
336Entry *
337Node::GetLastReferrer() const
338{
339	return fReferrers.Last();
340}
341
342// GetPreviousReferrer
343Entry *
344Node::GetPreviousReferrer(Entry *entry) const
345{
346	return (entry ? fReferrers.GetPrevious(entry) : NULL );
347}
348
349// GetNextReferrer
350Entry *
351Node::GetNextReferrer(Entry *entry) const
352{
353	return (entry ? fReferrers.GetNext(entry) : NULL );
354}
355
356// GetAllocationInfo
357void
358Node::GetAllocationInfo(AllocationInfo &info)
359{
360	Attribute *attribute = NULL;
361	while (GetNextAttribute(&attribute) == B_OK)
362		attribute->GetAllocationInfo(info);
363}
364