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