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