1// Directory.cpp
2
3#include "AllocationInfo.h"
4#include "Debug.h"
5#include "Directory.h"
6#include "Entry.h"
7#include "EntryIterator.h"
8#include "File.h"
9#include "SymLink.h"
10#include "Volume.h"
11
12// constructor
13Directory::Directory(Volume *volume)
14	: Node(volume, NODE_TYPE_DIRECTORY),
15	  fEntries()
16{
17}
18
19// destructor
20Directory::~Directory()
21{
22	// delete all entries
23	while (Entry *entry = fEntries.First()) {
24		if (DeleteEntry(entry) != B_OK) {
25			FATAL(("Could not delete all entries in directory.\n"));
26			break;
27		}
28	}
29}
30
31// Link
32status_t
33Directory::Link(Entry *entry)
34{
35	if (fReferrers.IsEmpty())
36		return Node::Link(entry);
37	return B_IS_A_DIRECTORY;
38}
39
40// Unlink
41status_t
42Directory::Unlink(Entry *entry)
43{
44	if (entry == fReferrers.First())
45		return Node::Unlink(entry);
46	return B_BAD_VALUE;
47}
48
49// SetSize
50status_t
51Directory::SetSize(off_t /*newSize*/)
52{
53	return B_IS_A_DIRECTORY;
54}
55
56// GetSize
57off_t
58Directory::GetSize() const
59{
60	return 0;
61}
62
63// GetParent
64Directory *
65Directory::GetParent() const
66{
67	Entry *entry = fReferrers.First();
68	return (entry ? entry->GetParent() : NULL);
69}
70
71// CreateDirectory
72status_t
73Directory::CreateDirectory(const char *name, Directory **directory)
74{
75	status_t error = (name && directory ? B_OK : B_BAD_VALUE);
76	if (error == B_OK) {
77		// create directory
78		if (Directory *node = new(nothrow) Directory(GetVolume())) {
79			error = _CreateCommon(node, name);
80				// deletes the node on failure
81			if (error == B_OK)
82				*directory = node;
83		} else
84			SET_ERROR(error, B_NO_MEMORY);
85	}
86	return error;
87}
88
89// CreateFile
90status_t
91Directory::CreateFile(const char *name, File **file)
92{
93	status_t error = (name && file ? B_OK : B_BAD_VALUE);
94	if (error == B_OK) {
95		// create file
96		if (File *node = new(nothrow) File(GetVolume())) {
97			error = _CreateCommon(node, name);
98				// deletes the node on failure
99			if (error == B_OK)
100				*file = node;
101		} else
102			SET_ERROR(error, B_NO_MEMORY);
103	}
104	return error;
105}
106
107// CreateSymLink
108status_t
109Directory::CreateSymLink(const char *name, const char *path, SymLink **symLink)
110{
111	status_t error = (name && symLink ? B_OK : B_BAD_VALUE);
112	if (error == B_OK) {
113		// create symlink
114		if (SymLink *node = new(nothrow) SymLink(GetVolume())) {
115			error = node->SetLinkedPath(path);
116			if (error == B_OK) {
117				error = _CreateCommon(node, name);
118					// deletes the node on failure
119				if (error == B_OK)
120					*symLink = node;
121			} else
122				delete node;
123		} else
124			SET_ERROR(error, B_NO_MEMORY);
125	}
126	return error;
127}
128
129// AddEntry
130status_t
131Directory::AddEntry(Entry *entry)
132{
133	status_t error = (entry && !entry->GetParent() ? B_OK : B_BAD_VALUE);
134	if (error == B_OK) {
135		fEntries.Insert(entry);
136		entry->SetParent(this);
137		error = GetVolume()->EntryAdded(GetID(), entry);
138		if (error == B_OK) {
139			MarkModified(B_STAT_MODIFICATION_TIME);
140		} else {
141			fEntries.Remove(entry);
142			entry->SetParent(NULL);
143		}
144	}
145	return error;
146}
147
148// CreateEntry
149status_t
150Directory::CreateEntry(Node *node, const char *name, Entry **_entry)
151{
152	status_t error = (node ? B_OK : B_BAD_VALUE);
153	if (error == B_OK) {
154		// create an entry
155		Entry *entry = new(nothrow) Entry(name);
156		if (entry) {
157			error = entry->InitCheck();
158			if (error == B_OK) {
159				// link to the node
160				error = entry->Link(node);
161				if (error == B_OK) {
162					// add the entry
163					error = AddEntry(entry);
164					if (error == B_OK) {
165						if (_entry)
166							*_entry = entry;
167					} else {
168						// failure: unlink the node
169						entry->Unlink();
170					}
171				}
172			}
173			// delete the entry on failure
174			if (error != B_OK)
175				delete entry;
176		} else
177			SET_ERROR(error, B_NO_MEMORY);
178	}
179	return error;
180}
181
182// RemoveEntry
183status_t
184Directory::RemoveEntry(Entry *entry)
185{
186	status_t error = (entry && entry->GetParent() == this ? B_OK
187														  : B_BAD_VALUE);
188	if (error == B_OK) {
189		// move all iterators pointing to the entry to the next entry
190		if (GetVolume()->IteratorLock()) {
191			// set the iterators' current entry
192			Entry *nextEntry = fEntries.GetNext(entry);
193			DoublyLinkedList<EntryIterator> *iterators
194				= entry->GetEntryIteratorList();
195			for (EntryIterator *iterator = iterators->First();
196				 iterator;
197				 iterator = iterators->GetNext(iterator)) {
198				iterator->SetCurrent(nextEntry, true);
199			}
200			// Move the iterators from one list to the other, or just remove
201			// them, if there is no next entry.
202			if (nextEntry) {
203				DoublyLinkedList<EntryIterator> *nextIterators
204					= nextEntry->GetEntryIteratorList();
205				nextIterators->MoveFrom(iterators);
206			} else
207				iterators->RemoveAll();
208			GetVolume()->IteratorUnlock();
209		} else
210			error = B_ERROR;
211		// remove the entry
212		if (error == B_OK) {
213			error = GetVolume()->EntryRemoved(GetID(), entry);
214			if (error == B_OK) {
215				fEntries.Remove(entry);
216				entry->SetParent(NULL);
217				MarkModified(B_STAT_MODIFICATION_TIME);
218			}
219		}
220	}
221	return error;
222}
223
224// DeleteEntry
225status_t
226Directory::DeleteEntry(Entry *entry)
227{
228	status_t error = RemoveEntry(entry);
229	if (error == B_OK) {
230		error = entry->Unlink();
231		if (error == B_OK)
232			delete entry;
233		else {
234			FATAL(("Failed to Unlink() entry %p from node %Ld!\n", entry,
235				   entry->GetNode()->GetID()));
236			AddEntry(entry);
237		}
238	}
239	return error;
240}
241
242// FindEntry
243status_t
244Directory::FindEntry(const char *name, Entry **_entry) const
245{
246	status_t error = (name && _entry ? B_OK : B_BAD_VALUE);
247	if (error == B_OK) {
248/*
249		Entry *entry = NULL;
250		while (GetNextEntry(&entry) == B_OK) {
251			if (!strcmp(entry->GetName(), name)) {
252				*_entry = entry;
253				return B_OK;
254			}
255		}
256		error = B_ENTRY_NOT_FOUND;
257*/
258		error = GetVolume()->FindEntry(GetID(), name, _entry);
259	}
260	return error;
261}
262
263// FindNode
264status_t
265Directory::FindNode(const char *name, Node **node) const
266{
267	status_t error = (name && node ? B_OK : B_BAD_VALUE);
268	Entry *entry = NULL;
269	if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK)
270		*node = entry->GetNode();
271	return error;
272}
273
274// FindAndGetNode
275status_t
276Directory::FindAndGetNode(const char *name, Node **node, Entry **_entry) const
277{
278	status_t error = (name && node ? B_OK : B_BAD_VALUE);
279	Entry *entry = NULL;
280	if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) {
281		*node = entry->GetNode();
282		if (_entry)
283			*_entry = entry;
284		error = GetVolume()->GetVNode(*node);
285	}
286	return error;
287}
288
289// GetPreviousEntry
290status_t
291Directory::GetPreviousEntry(Entry **entry) const
292{
293	status_t error = (entry ? B_OK : B_BAD_VALUE);
294	if (error == B_OK) {
295		if (!*entry)
296			*entry = fEntries.Last();
297		else if ((*entry)->GetParent() == this)
298			*entry = fEntries.GetPrevious(*entry);
299		else
300			error = B_BAD_VALUE;
301		if (error == B_OK && !*entry)
302			error = B_ENTRY_NOT_FOUND;
303	}
304	return error;
305}
306
307// GetNextEntry
308status_t
309Directory::GetNextEntry(Entry **entry) const
310{
311	status_t error = (entry ? B_OK : B_BAD_VALUE);
312	if (error == B_OK) {
313		if (!*entry)
314			*entry = fEntries.First();
315		else if ((*entry)->GetParent() == this)
316			*entry = fEntries.GetNext(*entry);
317		else
318			error = B_BAD_VALUE;
319		if (error == B_OK && !*entry)
320			error = B_ENTRY_NOT_FOUND;
321	}
322	return error;
323}
324
325// GetAllocationInfo
326void
327Directory::GetAllocationInfo(AllocationInfo &info)
328{
329	Node::GetAllocationInfo(info);
330	info.AddDirectoryAllocation();
331	Entry *entry = NULL;
332	while (GetNextEntry(&entry) == B_OK)
333		entry->GetAllocationInfo(info);
334}
335
336// _CreateCommon
337status_t
338Directory::_CreateCommon(Node *node, const char *name)
339{
340	status_t error = node->InitCheck();
341	if (error == B_OK) {
342		// add node to directory
343		error = CreateEntry(node, name);
344	}
345	if (error != B_OK)
346		delete node;
347	return error;
348}
349
350