1 /*
2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2014 Haiku, Inc. All rights reserved.
4 *
5 * Distributed under the terms of the MIT License.
6 *
7 * Authors:
8 * Jérôme Duval, korli@users.berlios.de
9 * John Scipione, jscipione@gmail.com
10 */
11
12
13 #include "DirectoryIterator.h"
14
15 #include <stdlib.h>
16
17 #include "convertutf.h"
18
19 #include "Inode.h"
20
21
22 //#define TRACE_EXFAT
23 #ifdef TRACE_EXFAT
24 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
25 #else
26 # define TRACE(x...) ;
27 #endif
28
29 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
30
31
32 // #pragma mark - DirectoryIterator
33
34
DirectoryIterator(Inode * inode)35 DirectoryIterator::DirectoryIterator(Inode* inode)
36 :
37 fOffset(-2),
38 fCluster(inode->StartCluster()),
39 fInode(inode),
40 fBlock(inode->GetVolume()),
41 fCurrent(NULL)
42 {
43 TRACE("DirectoryIterator::DirectoryIterator() %" B_PRIu32 "\n", fCluster);
44 }
45
46
~DirectoryIterator()47 DirectoryIterator::~DirectoryIterator()
48 {
49 }
50
51
52 status_t
InitCheck()53 DirectoryIterator::InitCheck()
54 {
55 return B_OK;
56 }
57
58
59 status_t
GetNext(char * name,size_t * _nameLength,ino_t * _id,EntryVisitor * visitor)60 DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id,
61 EntryVisitor* visitor)
62 {
63 if (fCluster == EXFAT_CLUSTER_END)
64 return B_ENTRY_NOT_FOUND;
65
66 if (fOffset == -2) {
67 if (*_nameLength < 3)
68 return B_BUFFER_OVERFLOW;
69
70 *_nameLength = 2;
71 strlcpy(name, "..", *_nameLength + 1);
72 if (fInode->ID() == 1)
73 *_id = fInode->ID();
74 else
75 *_id = fInode->Parent();
76
77 fOffset = -1;
78 TRACE("DirectoryIterator::GetNext() found \"..\"\n");
79
80 return B_OK;
81 } else if (fOffset == -1) {
82 if (*_nameLength < 2)
83 return B_BUFFER_OVERFLOW;
84
85 *_nameLength = 1;
86 strlcpy(name, ".", *_nameLength + 1);
87 *_id = fInode->ID();
88 fOffset = 0;
89 TRACE("DirectoryIterator::GetNext() found \".\"\n");
90
91 return B_OK;
92 }
93
94 size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
95 uint16 utf16Name[utf16CodeUnitCount];
96 status_t status = _GetNext(utf16Name, &utf16CodeUnitCount, _id, visitor);
97 if (status == B_OK && utf16CodeUnitCount > 0) {
98 ssize_t lengthOrStatus = utf16le_to_utf8(utf16Name, utf16CodeUnitCount,
99 name, *_nameLength);
100 if (lengthOrStatus < 0) {
101 status = (status_t)lengthOrStatus;
102 if (status == B_NAME_TOO_LONG)
103 *_nameLength = strlen(name);
104 } else
105 *_nameLength = (size_t)lengthOrStatus;
106 }
107
108 if (status == B_OK) {
109 TRACE("DirectoryIterator::GetNext() cluster: %" B_PRIu32 " id: "
110 "%" B_PRIdINO " name: \"%s\", length: %zu\n", fInode->Cluster(),
111 *_id, name, *_nameLength);
112 } else if (status != B_ENTRY_NOT_FOUND) {
113 ERROR("DirectoryIterator::GetNext() (%s)\n", strerror(status));
114 }
115
116 return status;
117 }
118
119
120 status_t
Lookup(const char * name,size_t nameLength,ino_t * _id)121 DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
122 {
123 if (strcmp(name, ".") == 0) {
124 *_id = fInode->ID();
125 return B_OK;
126 } else if (strcmp(name, "..") == 0) {
127 if (fInode->ID() == 1)
128 *_id = fInode->ID();
129 else
130 *_id = fInode->Parent();
131
132 return B_OK;
133 }
134
135 Rewind();
136 fOffset = 0;
137
138 size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
139 uint16 utf16Name[utf16CodeUnitCount];
140 while (_GetNext(utf16Name, &utf16CodeUnitCount, _id) == B_OK) {
141 char utf8Name[nameLength + 1];
142 ssize_t lengthOrStatus = utf16le_to_utf8(utf16Name, utf16CodeUnitCount,
143 utf8Name, sizeof(utf8Name));
144 if (lengthOrStatus > 0 && (size_t)lengthOrStatus == nameLength
145 && strncmp(utf8Name, name, nameLength) == 0) {
146 TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n",
147 *_id);
148 return B_OK;
149 }
150 utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
151 }
152
153 TRACE("DirectoryIterator::Lookup() not found %s\n", name);
154
155 return B_ENTRY_NOT_FOUND;
156 }
157
158
159 status_t
LookupEntry(EntryVisitor * visitor)160 DirectoryIterator::LookupEntry(EntryVisitor* visitor)
161 {
162 fCluster = fInode->Cluster();
163 fOffset = fInode->Offset();
164
165 size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
166 uint16 utf16Name[utf16CodeUnitCount];
167 return _GetNext(utf16Name, &utf16CodeUnitCount, NULL, visitor);
168 }
169
170
171 status_t
Rewind()172 DirectoryIterator::Rewind()
173 {
174 fOffset = -2;
175 fCluster = fInode->StartCluster();
176 return B_OK;
177 }
178
179
180 void
Iterate(EntryVisitor & visitor)181 DirectoryIterator::Iterate(EntryVisitor &visitor)
182 {
183 fOffset = 0;
184 fCluster = fInode->StartCluster();
185
186 while (_NextEntry() != B_ENTRY_NOT_FOUND) {
187 switch (fCurrent->type) {
188 case EXFAT_ENTRY_TYPE_BITMAP:
189 visitor.VisitBitmap(fCurrent);
190 break;
191 case EXFAT_ENTRY_TYPE_UPPERCASE:
192 visitor.VisitUppercase(fCurrent);
193 break;
194 case EXFAT_ENTRY_TYPE_LABEL:
195 visitor.VisitLabel(fCurrent);
196 break;
197 case EXFAT_ENTRY_TYPE_FILE:
198 visitor.VisitFile(fCurrent);
199 break;
200 case EXFAT_ENTRY_TYPE_FILEINFO:
201 visitor.VisitFileInfo(fCurrent);
202 break;
203 case EXFAT_ENTRY_TYPE_FILENAME:
204 visitor.VisitFilename(fCurrent);
205 break;
206 }
207 }
208 }
209
210
211 status_t
_GetNext(uint16 * utf16Name,size_t * _codeUnitCount,ino_t * _id,EntryVisitor * visitor)212 DirectoryIterator::_GetNext(uint16* utf16Name, size_t* _codeUnitCount,
213 ino_t* _id, EntryVisitor* visitor)
214 {
215 size_t nameMax = *_codeUnitCount;
216 size_t nameIndex = 0;
217 status_t status;
218 int32 chunkCount = 1;
219
220 while ((status = _NextEntry()) == B_OK) {
221 TRACE("DirectoryIterator::_GetNext() %" B_PRIu32 "/%p, type 0x%x, "
222 "offset %" B_PRId64 "\n", fInode->Cluster(), fCurrent,
223 fCurrent->type, fOffset);
224 if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) {
225 chunkCount = fCurrent->file.chunkCount;
226 if (_id != NULL) {
227 *_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1,
228 fInode->ID());
229 }
230 TRACE("DirectoryIterator::_GetNext() File chunkCount %" B_PRId32
231 "\n", chunkCount);
232 if (visitor != NULL)
233 visitor->VisitFile(fCurrent);
234 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) {
235 chunkCount--;
236 *_codeUnitCount = (size_t)fCurrent->file_info.name_length;
237 TRACE("DirectoryIterator::_GetNext() Filename chunk: %" B_PRId32
238 ", code unit count: %" B_PRIu8 "\n", chunkCount, *_codeUnitCount);
239 if (visitor != NULL)
240 visitor->VisitFileInfo(fCurrent);
241 } else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) {
242 chunkCount--;
243 size_t utf16Length = sizeof(fCurrent->file_name.name);
244 memcpy(utf16Name + nameIndex, fCurrent->file_name.name, utf16Length);
245 nameIndex += utf16Length / sizeof(uint16);
246 TRACE("DirectoryIterator::_GetNext() Filename index: %zu\n",
247 nameIndex);
248 if (visitor != NULL)
249 visitor->VisitFilename(fCurrent);
250 }
251
252 if (chunkCount == 0 || nameIndex >= nameMax)
253 break;
254 }
255
256 #ifdef TRACE_EXFAT
257 if (status == B_OK) {
258 size_t utf8Length = B_FILE_NAME_LENGTH * 4;
259 char utf8Name[utf8Length + 1];
260 ssize_t length = utf16le_to_utf8(utf16Name, *_codeUnitCount, utf8Name,
261 utf8Length);
262 if (length > 0) {
263 TRACE("DirectoryIterator::_GetNext() found name: \"%s\", "
264 "length: %d\n", utf8Name, length);
265 }
266 }
267 #endif
268
269 return status;
270 }
271
272
273 status_t
_NextEntry()274 DirectoryIterator::_NextEntry()
275 {
276 if (fCurrent == NULL) {
277 fsblock_t block;
278 if (fInode->GetVolume()->ClusterToBlock(fCluster, block) != B_OK)
279 return B_BAD_DATA;
280 block += (fOffset / fInode->GetVolume()->EntriesPerBlock())
281 % (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift());
282 TRACE("DirectoryIterator::_NextEntry() init to block %" B_PRIu64 "\n",
283 block);
284 fCurrent = (struct exfat_entry*)fBlock.SetTo(block)
285 + fOffset % fInode->GetVolume()->EntriesPerBlock();
286 } else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) {
287 fsblock_t block;
288 if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) {
289 fCluster = fInode->NextCluster(fCluster);
290 if (fCluster == EXFAT_CLUSTER_END)
291 return B_ENTRY_NOT_FOUND;
292
293 if (fInode->GetVolume()->ClusterToBlock(fCluster, block) != B_OK)
294 return B_BAD_DATA;
295 } else
296 block = fBlock.BlockNumber() + 1;
297
298 TRACE("DirectoryIterator::_NextEntry() block %" B_PRIu64 "\n", block);
299 fCurrent = (struct exfat_entry*)fBlock.SetTo(block);
300 } else
301 fCurrent++;
302
303 fOffset++;
304 return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK;
305 }
306