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