xref: /haiku/src/add-ons/kernel/file_systems/exfat/DirectoryIterator.cpp (revision 18027fff34af4a666c1e62254b462cbaeae1859e)
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,
112 			&utfLength);
113 		if (nameLength == utfLength
114 			&& strncmp(utfName, name, nameLength) == 0) {
115 			TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n",
116 				*_id);
117 			return B_OK;
118 		}
119 		currentLength = EXFAT_FILENAME_MAX_LENGTH;
120 	}
121 
122 	TRACE("DirectoryIterator::Lookup() not found %s\n", name);
123 
124 	return B_ENTRY_NOT_FOUND;
125 }
126 
127 
128 status_t
129 DirectoryIterator::LookupEntry(EntryVisitor* visitor)
130 {
131 	fCluster = fInode->Cluster();
132 	fOffset = fInode->Offset();
133 
134 	uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH + 1];
135 	size_t nameLength = EXFAT_FILENAME_MAX_LENGTH;
136 	return _GetNext(unicodeName, &nameLength, NULL, visitor);
137 }
138 
139 
140 status_t
141 DirectoryIterator::Rewind()
142 {
143 	fOffset = -2;
144 	fCluster = fInode->StartCluster();
145 	return B_OK;
146 }
147 
148 
149 void
150 DirectoryIterator::Iterate(EntryVisitor &visitor)
151 {
152 	fOffset = 0;
153 	fCluster = fInode->StartCluster();
154 
155 	while (_NextEntry() != B_ENTRY_NOT_FOUND) {
156 		switch (fCurrent->type) {
157 			case EXFAT_ENTRY_TYPE_BITMAP:
158 				visitor.VisitBitmap(fCurrent);
159 				break;
160 			case EXFAT_ENTRY_TYPE_UPPERCASE:
161 				visitor.VisitUppercase(fCurrent);
162 				break;
163 			case EXFAT_ENTRY_TYPE_LABEL:
164 				visitor.VisitLabel(fCurrent);
165 				break;
166 			case EXFAT_ENTRY_TYPE_FILE:
167 				visitor.VisitFile(fCurrent);
168 				break;
169 			case EXFAT_ENTRY_TYPE_FILEINFO:
170 				visitor.VisitFileInfo(fCurrent);
171 				break;
172 			case EXFAT_ENTRY_TYPE_FILENAME:
173 				visitor.VisitFilename(fCurrent);
174 				break;
175 		}
176 	}
177 }
178 
179 
180 status_t
181 DirectoryIterator::_GetNext(uchar* name, size_t* _nameLength, ino_t* _id,
182 	EntryVisitor* visitor)
183 {
184 	size_t nameMax = *_nameLength;
185 	size_t nameIndex = 0;
186 	status_t status;
187 	int32 chunkCount = 1;
188 	while ((status = _NextEntry()) == B_OK) {
189 		TRACE("DirectoryIterator::_GetNext() %" B_PRIu32 "/%p, type 0x%x, "
190 			"offset %" B_PRId64 "\n", fInode->Cluster(), fCurrent,
191 			fCurrent->type, fOffset);
192 		if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) {
193 			chunkCount = fCurrent->file.chunkCount;
194 			if (_id != NULL) {
195 				*_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1,
196 					fInode->ID());
197 			}
198 			if (visitor != NULL)
199 				visitor->VisitFile(fCurrent);
200 			TRACE("DirectoryIterator::_GetNext() File chunkCount %" B_PRId32
201 				"\n", chunkCount);
202 		} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) {
203 			chunkCount--;
204 			TRACE("DirectoryIterator::_GetNext() Filename length %d\n",
205 				fCurrent->file_info.name_length);
206 			*_nameLength = fCurrent->file_info.name_length * 2;
207 			if (visitor != NULL)
208 				visitor->VisitFileInfo(fCurrent);
209 		} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) {
210 			TRACE("DirectoryIterator::_GetNext() Filename\n");
211 			memcpy((uint8*)name + nameIndex, fCurrent->file_name.name,
212 				sizeof(fCurrent->file_name.name));
213 			nameIndex += sizeof(fCurrent->file_name.name);
214 			name[nameIndex] = '\0';
215 			chunkCount--;
216 			if (visitor != NULL)
217 				visitor->VisitFilename(fCurrent);
218 		}
219 
220 		if (chunkCount == 0 || nameIndex >= nameMax)
221 			break;
222 	}
223 
224 	if (status == B_OK) {
225 		//*_nameLength = nameIndex;
226 #ifdef TRACE_EXFAT
227 		char utfName[EXFAT_FILENAME_MAX_LENGTH];
228 		size_t utfLen = EXFAT_FILENAME_MAX_LENGTH;
229 		unicode_to_utf8(name, nameIndex, (uint8*)utfName, &utfLen);
230 		TRACE("DirectoryIterator::_GetNext() Found %s %ld\n", utfName,
231 			*_nameLength);
232 #endif
233 	}
234 
235 	return status;
236 }
237 
238 
239 status_t
240 DirectoryIterator::_NextEntry()
241 {
242 	if (fCurrent == NULL) {
243 		fsblock_t block;
244 		fInode->GetVolume()->ClusterToBlock(fCluster, block);
245 		block += (fOffset / fInode->GetVolume()->EntriesPerBlock())
246 			% (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift());
247 		TRACE("DirectoryIterator::_NextEntry() init to block %" B_PRIu64 "\n",
248 			block);
249 		fCurrent = (struct exfat_entry*)fBlock.SetTo(block)
250 			+ fOffset % fInode->GetVolume()->EntriesPerBlock();
251 	} else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) {
252 		fsblock_t block;
253 		if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) {
254 			fCluster = fInode->NextCluster(fCluster);
255 			if (fCluster == EXFAT_CLUSTER_END)
256 				return B_ENTRY_NOT_FOUND;
257 			fInode->GetVolume()->ClusterToBlock(fCluster, block);
258 		} else
259 			block = fBlock.BlockNumber() + 1;
260 		TRACE("DirectoryIterator::_NextEntry() block %" B_PRIu64 "\n", block);
261 		fCurrent = (struct exfat_entry*)fBlock.SetTo(block);
262 	} else
263 		fCurrent++;
264 	fOffset++;
265 
266 	return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK;
267 }
268