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