xref: /haiku/src/add-ons/kernel/file_systems/exfat/DirectoryIterator.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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() %ld\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 (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 
82 status_t
83 DirectoryIterator::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 
119 status_t
120 DirectoryIterator::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 
131 status_t
132 DirectoryIterator::Rewind()
133 {
134 	fOffset = -2;
135 	fCluster = fInode->StartCluster();
136 	return B_OK;
137 }
138 
139 
140 void
141 DirectoryIterator::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 
171 status_t
172 DirectoryIterator::_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 
229 status_t
230 DirectoryIterator::_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