1/*
2 * Copyright 2011, J��r��me Duval, korli@users.berlios.de.
3 * This file may be used under the terms of the MIT License.
4 */
5
6
7#include "DirectoryIterator.h"
8
9#include "encodings.h"
10#include "Inode.h"
11
12
13//#define TRACE_EXFAT
14#ifdef TRACE_EXFAT
15#	define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
16#else
17#	define TRACE(x...) ;
18#endif
19#	define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
20
21
22DirectoryIterator::DirectoryIterator(Inode* inode)
23	:
24	fOffset(-2),
25	fCluster(inode->StartCluster()),
26	fInode(inode),
27	fBlock(inode->GetVolume()),
28	fCurrent(NULL)
29{
30	TRACE("DirectoryIterator::DirectoryIterator() %ld\n", fCluster);
31}
32
33
34DirectoryIterator::~DirectoryIterator()
35{
36}
37
38
39status_t
40DirectoryIterator::InitCheck()
41{
42	return B_OK;
43}
44
45
46status_t
47DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id,
48	EntryVisitor* visitor)
49{
50	if (fOffset == -2) {
51		*_nameLength = 3;
52		strlcpy(name, "..", *_nameLength);
53		if (fInode->ID() == 1)
54			*_id = fInode->ID();
55		else
56			*_id = fInode->Parent();
57		fOffset = -1;
58		TRACE("DirectoryIterator::GetNext() found ..\n");
59		return B_OK;
60	} else if (fOffset == -1) {
61		*_nameLength = 2;
62		strlcpy(name, ".", *_nameLength);
63		*_id = fInode->ID();
64		fOffset = 0;
65		TRACE("DirectoryIterator::GetNext() found .\n");
66		return B_OK;
67	}
68
69	uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH];
70	size_t nameLength = EXFAT_FILENAME_MAX_LENGTH;
71	status_t status = _GetNext(unicodeName, &nameLength, _id, visitor);
72	if (status == B_OK && name != NULL) {
73		unicode_to_utf8(unicodeName, nameLength, (uint8 *)name , _nameLength);
74		TRACE("DirectoryIterator::GetNext() %ld %s, %" B_PRIdINO "\n",
75			fInode->Cluster(), name, *_id);
76	}
77
78	return status;
79}
80
81
82status_t
83DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
84{
85	if (strcmp(name, ".") == 0) {
86		*_id = fInode->ID();
87		return B_OK;
88	} else if (strcmp(name, "..") == 0) {
89		if (fInode->ID() == 1)
90			*_id = fInode->ID();
91		else
92			*_id = fInode->Parent();
93		return B_OK;
94	}
95
96	Rewind();
97	fOffset = 0;
98
99	uchar currentName[EXFAT_FILENAME_MAX_LENGTH];
100	size_t currentLength = EXFAT_FILENAME_MAX_LENGTH;
101	while (_GetNext((uchar*)currentName, &currentLength, _id) == B_OK) {
102		char utfName[EXFAT_FILENAME_MAX_LENGTH];
103		size_t utfLength = EXFAT_FILENAME_MAX_LENGTH;
104		unicode_to_utf8(currentName, currentLength, (uint8*)utfName, &utfLength);
105		if (nameLength == utfLength
106			&& strncmp(utfName, name, nameLength) == 0) {
107			TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n", *_id);
108			return B_OK;
109		}
110		currentLength = EXFAT_FILENAME_MAX_LENGTH;
111	}
112
113	TRACE("DirectoryIterator::Lookup() not found %s\n", name);
114
115	return B_ENTRY_NOT_FOUND;
116}
117
118
119status_t
120DirectoryIterator::LookupEntry(EntryVisitor* visitor)
121{
122	fCluster = fInode->Cluster();
123	fOffset = fInode->Offset();
124
125	uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH];
126	size_t nameLength = EXFAT_FILENAME_MAX_LENGTH;
127	return _GetNext(unicodeName, &nameLength, NULL, visitor);
128}
129
130
131status_t
132DirectoryIterator::Rewind()
133{
134	fOffset = -2;
135	fCluster = fInode->StartCluster();
136	return B_OK;
137}
138
139
140void
141DirectoryIterator::Iterate(EntryVisitor &visitor)
142{
143	fOffset = 0;
144	fCluster = fInode->StartCluster();
145
146	while (_NextEntry() != B_ENTRY_NOT_FOUND) {
147		switch (fCurrent->type) {
148			case EXFAT_ENTRY_TYPE_BITMAP:
149				visitor.VisitBitmap(fCurrent);
150				break;
151			case EXFAT_ENTRY_TYPE_UPPERCASE:
152				visitor.VisitUppercase(fCurrent);
153				break;
154			case EXFAT_ENTRY_TYPE_LABEL:
155				visitor.VisitLabel(fCurrent);
156				break;
157			case EXFAT_ENTRY_TYPE_FILE:
158				visitor.VisitFile(fCurrent);
159				break;
160			case EXFAT_ENTRY_TYPE_FILEINFO:
161				visitor.VisitFileInfo(fCurrent);
162				break;
163			case EXFAT_ENTRY_TYPE_FILENAME:
164				visitor.VisitFilename(fCurrent);
165				break;
166		}
167	}
168}
169
170
171status_t
172DirectoryIterator::_GetNext(uchar* name, size_t* _nameLength, ino_t* _id,
173	EntryVisitor* visitor)
174{
175	size_t nameMax = *_nameLength;
176	size_t nameIndex = 0;
177	status_t status;
178	int32 chunkCount = 1;
179	while ((status = _NextEntry()) == B_OK) {
180		TRACE("DirectoryIterator::_GetNext() %ld/%p, type 0x%x, offset %lld\n",
181			fInode->Cluster(), fCurrent, fCurrent->type, fOffset);
182		if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) {
183			chunkCount = fCurrent->file.chunkCount;
184			if (_id != NULL) {
185				*_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1,
186					fInode->ID());
187			}
188			if (visitor != NULL)
189				visitor->VisitFile(fCurrent);
190			TRACE("DirectoryIterator::_GetNext() File chunkCount %ld\n",
191				chunkCount);
192		} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) {
193			chunkCount--;
194			TRACE("DirectoryIterator::_GetNext() Filename length %d\n",
195				fCurrent->file_info.name_length);
196			*_nameLength = fCurrent->file_info.name_length * 2;
197			if (visitor != NULL)
198				visitor->VisitFileInfo(fCurrent);
199		} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) {
200			TRACE("DirectoryIterator::_GetNext() Filename\n");
201			memcpy((uint8*)name + nameIndex, fCurrent->name_label.name,
202				sizeof(fCurrent->name_label.name));
203			nameIndex += sizeof(fCurrent->name_label.name);
204			name[nameIndex] = '\0';
205			chunkCount--;
206			if (visitor != NULL)
207				visitor->VisitFilename(fCurrent);
208		}
209
210		if (chunkCount == 0 || nameIndex >= nameMax)
211			break;
212	}
213
214	if (status == B_OK) {
215		//*_nameLength = nameIndex;
216#ifdef TRACE_EXFAT
217		char utfName[EXFAT_FILENAME_MAX_LENGTH];
218		size_t utfLen = EXFAT_FILENAME_MAX_LENGTH;
219		unicode_to_utf8(name, nameIndex, (uint8*)utfName, &utfLen);
220		TRACE("DirectoryIterator::_GetNext() Found %s %ld\n", utfName,
221			*_nameLength);
222#endif
223	}
224
225	return status;
226}
227
228
229status_t
230DirectoryIterator::_NextEntry()
231{
232	if (fCurrent == NULL) {
233		fsblock_t block;
234		fInode->GetVolume()->ClusterToBlock(fCluster, block);
235		block += (fOffset / fInode->GetVolume()->EntriesPerBlock())
236			% (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift());
237		TRACE("DirectoryIterator::_NextEntry() init to block %lld\n", block);
238		fCurrent = (struct exfat_entry*)fBlock.SetTo(block)
239			+ fOffset % fInode->GetVolume()->EntriesPerBlock();
240	} else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) {
241		fsblock_t block;
242		if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) {
243			fCluster = fInode->NextCluster(fCluster);
244			if (fCluster == EXFAT_CLUSTER_END)
245				return B_ENTRY_NOT_FOUND;
246			fInode->GetVolume()->ClusterToBlock(fCluster, block);
247		} else
248			block = fBlock.BlockNumber() + 1;
249		TRACE("DirectoryIterator::_NextEntry() block %lld\n", block);
250		fCurrent = (struct exfat_entry*)fBlock.SetTo(block);
251	} else
252		fCurrent++;
253	fOffset++;
254
255	return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK;
256}
257
258
259