1// Directory.cpp
2
3#include <new>
4
5#include <dirent.h>
6#include <errno.h>
7
8#include <AutoDeleter.h>
9
10#include "Directory.h"
11#include "FDManager.h"
12#include "Path.h"
13#include "VolumeManager.h"
14
15// CachedDirIterator
16class CachedDirIterator : public DirIterator {
17public:
18								CachedDirIterator();
19								~CachedDirIterator();
20
21	virtual	status_t			SetDirectory(Directory* directory);
22
23	virtual	Entry*				NextEntry();
24	virtual	void				Rewind();
25
26	virtual	Entry*				GetCurrentEntry() const;
27
28	virtual	status_t			GetStat(struct stat* st);
29
30private:
31			Entry*				fCurrentEntry;
32};
33
34// UncachedDirIterator
35class UncachedDirIterator : public DirIterator {
36public:
37								UncachedDirIterator();
38								~UncachedDirIterator();
39
40	virtual	status_t			SetDirectory(Directory* directory);
41
42	virtual	Entry*				NextEntry();
43	virtual	void				Rewind();
44
45	virtual	Entry*				GetCurrentEntry() const;
46
47protected:
48	virtual	int					GetFD() const;
49
50private:
51			DIR*				fDirHandle;
52};
53
54
55// #pragma mark -
56
57// constructor
58CachedDirIterator::CachedDirIterator()
59	: DirIterator(),
60	  fCurrentEntry(NULL)
61{
62}
63
64// destructor
65CachedDirIterator::~CachedDirIterator()
66{
67}
68
69// SetDirectory
70status_t
71CachedDirIterator::SetDirectory(Directory* directory)
72{
73	// set the new directory
74	fDirectory = directory;
75	fCurrentEntry = (fDirectory ? fDirectory->GetFirstEntry() : NULL);
76
77	if (directory)
78		fNodeRef = directory->GetNodeRef();
79
80	return B_OK;
81}
82
83// NextEntry
84Entry*
85CachedDirIterator::NextEntry()
86{
87	if (!IsValid() || !fCurrentEntry)
88		return NULL;
89
90	Entry* entry = fCurrentEntry;
91	fCurrentEntry = fDirectory->GetNextEntry(fCurrentEntry);
92	return entry;
93}
94
95// Rewind
96void
97CachedDirIterator::Rewind()
98{
99	fCurrentEntry = (IsValid() ? fDirectory->GetFirstEntry() : NULL);
100}
101
102// GetCurrentEntry
103Entry*
104CachedDirIterator::GetCurrentEntry() const
105{
106	return (IsValid() ? fCurrentEntry : NULL);
107}
108
109// GetStat
110status_t
111CachedDirIterator::GetStat(struct stat* st)
112{
113	if (!fDirectory || !st)
114		return B_BAD_VALUE;
115
116	*st = fDirectory->GetStat();
117	return B_OK;
118}
119
120
121// #pragma mark -
122
123// constructor
124UncachedDirIterator::UncachedDirIterator()
125	: DirIterator(),
126	  fDirHandle(NULL)
127{
128}
129
130// destructor
131UncachedDirIterator::~UncachedDirIterator()
132{
133	// close
134	if (fDirHandle) {
135		closedir(fDirHandle);
136		fDirHandle = NULL;
137	}
138}
139
140// SetDirectory
141status_t
142UncachedDirIterator::SetDirectory(Directory* directory)
143{
144	// unset old
145	if (fDirHandle) {
146		closedir(fDirHandle);
147		fDirHandle = NULL;
148	}
149	fDirectory = NULL;
150
151	// set new
152	if (directory) {
153		// get the directory path
154		Path path;
155		status_t error = directory->GetPath(&path);
156		if (error != B_OK)
157			return error;
158
159		// open the directory
160		error = FDManager::OpenDir(path.GetPath(), fDirHandle);
161		if (error != B_OK)
162			return error;
163
164		fDirectory = directory;
165		fNodeRef = directory->GetNodeRef();
166	}
167
168	return B_OK;
169}
170
171// NextEntry
172Entry*
173UncachedDirIterator::NextEntry()
174{
175	if (!IsValid() && fDirHandle)
176		return NULL;
177
178	while (struct dirent* dirEntry = readdir(fDirHandle)) {
179		Entry* entry;
180		if (VolumeManager::GetDefault()->LoadEntry(dirEntry->d_pdev,
181				dirEntry->d_pino, dirEntry->d_name, false, &entry) == B_OK) {
182			return entry;
183		}
184	}
185
186	// we're through: set the directory to "complete"
187	fDirectory->SetComplete(true);
188
189	return NULL;
190}
191
192// Rewind
193void
194UncachedDirIterator::Rewind()
195{
196	if (IsValid() && fDirHandle)
197		rewinddir(fDirHandle);
198}
199
200// GetCurrentEntry
201Entry*
202UncachedDirIterator::GetCurrentEntry() const
203{
204	return NULL;
205}
206
207// GetFD
208int
209UncachedDirIterator::GetFD() const
210{
211	return dirfd(fDirHandle);
212}
213
214
215// #pragma mark -
216
217// constructor
218Directory::Directory(Volume* volume, const struct stat& st)
219	: Node(volume, st),
220	  fEntries(),
221	  fIterators(),
222	  fIsComplete(false)
223{
224}
225
226// destructor
227Directory::~Directory()
228{
229	// remove all directory iterators
230	while (DirIterator* iterator = fIterators.First())
231		iterator->SetDirectory(NULL);
232}
233
234// GetActualReferringEntry
235Entry*
236Directory::GetActualReferringEntry() const
237{
238	// any entry other than "." and ".." is fine
239	for (Entry* entry = GetFirstReferringEntry();
240		 entry;
241		 entry = GetNextReferringEntry(entry)) {
242		if (entry->IsActualEntry())
243			return entry;
244	}
245	return NULL;
246}
247
248// AddEntry
249void
250Directory::AddEntry(Entry* entry)
251{
252	if (entry)
253		fEntries.Insert(entry);
254}
255
256// RemoveEntry
257void
258Directory::RemoveEntry(Entry* entry)
259{
260	if (entry) {
261		// update the directory iterators pointing to the removed entry
262		for (DirIterator* iterator = fIterators.First();
263			 iterator;
264			 iterator = fIterators.GetNext(iterator)) {
265			if (iterator->GetCurrentEntry() == entry)
266				iterator->NextEntry();
267		}
268
269		fEntries.Remove(entry);
270	}
271}
272
273// GetFirstEntry
274Entry*
275Directory::GetFirstEntry() const
276{
277	return fEntries.First();
278}
279
280// GetNextEntry
281Entry*
282Directory::GetNextEntry(Entry* entry) const
283{
284	return (entry ? fEntries.GetNext(entry) : NULL);
285}
286
287// CountEntries
288int32
289Directory::CountEntries() const
290{
291	int32 count = 0;
292	Entry* entry = GetFirstEntry();
293	while (entry) {
294		count++;
295		entry = GetNextEntry(entry);
296	}
297	return count;
298}
299
300// OpenDir
301status_t
302Directory::OpenDir(DirIterator** _iterator)
303{
304	if (!_iterator)
305		return B_BAD_VALUE;
306
307	// create the iterator
308	DirIterator* iterator;
309	if (fIsComplete)
310		iterator = new(std::nothrow) CachedDirIterator;
311	else
312		iterator = new(std::nothrow) UncachedDirIterator;
313	if (!iterator)
314		return B_NO_MEMORY;
315	ObjectDeleter<DirIterator> iteratorDeleter(iterator);
316
317	// initialize it
318	status_t error = iterator->SetDirectory(this);
319	if (error != B_OK)
320		return error;
321
322	// check, if it really belongs to this node
323	error = _CheckNodeHandle(iterator);
324	if (error != B_OK)
325		return error;
326
327	// add it
328	fIterators.Insert(iterator);
329
330	iteratorDeleter.Detach();
331	*_iterator = iterator;
332	return B_OK;
333}
334
335// HasDirIterators
336bool
337Directory::HasDirIterators() const
338{
339	return fIterators.First();
340}
341
342// RemoveDirIterator
343void
344Directory::RemoveDirIterator(DirIterator* iterator)
345{
346	if (iterator)
347		fIterators.Remove(iterator);
348}
349
350// SetComplete
351void
352Directory::SetComplete(bool complete)
353{
354	fIsComplete = complete;
355}
356
357// IsComplete
358bool
359Directory::IsComplete() const
360{
361	return fIsComplete;
362}
363