2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2014 Haiku, Inc. All rights reserved.
5 * Distributed under the terms of the MIT License.
8 * Jérôme Duval, korli@users.berlios.de
9 * John Scipione, jscipione@gmail.com
13 #include "DirectoryIterator.h"
17 #include "convertutf.h"
24 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
26 # define TRACE(x...) ;
29 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
32 // #pragma mark - DirectoryIterator
35 DirectoryIterator::DirectoryIterator(Inode
* inode
)
38 fCluster(inode
->StartCluster()),
40 fBlock(inode
->GetVolume()),
43 TRACE("DirectoryIterator::DirectoryIterator() %" B_PRIu32
"\n", fCluster
);
47 DirectoryIterator::~DirectoryIterator()
53 DirectoryIterator::InitCheck()
60 DirectoryIterator::GetNext(char* name
, size_t* _nameLength
, ino_t
* _id
,
61 EntryVisitor
* visitor
)
63 if (fCluster
== EXFAT_CLUSTER_END
)
64 return B_ENTRY_NOT_FOUND
;
68 return B_BUFFER_OVERFLOW
;
71 strlcpy(name
, "..", *_nameLength
+ 1);
72 if (fInode
->ID() == 1)
75 *_id
= fInode
->Parent();
78 TRACE("DirectoryIterator::GetNext() found \"..\"\n");
81 } else if (fOffset
== -1) {
83 return B_BUFFER_OVERFLOW
;
86 strlcpy(name
, ".", *_nameLength
+ 1);
89 TRACE("DirectoryIterator::GetNext() found \".\"\n");
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
,
100 if (lengthOrStatus
< 0) {
101 status
= (status_t
)lengthOrStatus
;
102 if (status
== B_NAME_TOO_LONG
)
103 *_nameLength
= strlen(name
);
105 *_nameLength
= (size_t)lengthOrStatus
;
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
));
121 DirectoryIterator::Lookup(const char* name
, size_t nameLength
, ino_t
* _id
)
123 if (strcmp(name
, ".") == 0) {
126 } else if (strcmp(name
, "..") == 0) {
127 if (fInode
->ID() == 1)
130 *_id
= fInode
->Parent();
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",
150 utf16CodeUnitCount
= EXFAT_FILENAME_MAX_LENGTH
/ sizeof(uint16
);
153 TRACE("DirectoryIterator::Lookup() not found %s\n", name
);
155 return B_ENTRY_NOT_FOUND
;
160 DirectoryIterator::LookupEntry(EntryVisitor
* visitor
)
162 fCluster
= fInode
->Cluster();
163 fOffset
= fInode
->Offset();
165 size_t utf16CodeUnitCount
= EXFAT_FILENAME_MAX_LENGTH
/ sizeof(uint16
);
166 uint16 utf16Name
[utf16CodeUnitCount
];
167 return _GetNext(utf16Name
, &utf16CodeUnitCount
, NULL
, visitor
);
172 DirectoryIterator::Rewind()
175 fCluster
= fInode
->StartCluster();
181 DirectoryIterator::Iterate(EntryVisitor
&visitor
)
184 fCluster
= fInode
->StartCluster();
186 while (_NextEntry() != B_ENTRY_NOT_FOUND
) {
187 switch (fCurrent
->type
) {
188 case EXFAT_ENTRY_TYPE_BITMAP
:
189 visitor
.VisitBitmap(fCurrent
);
191 case EXFAT_ENTRY_TYPE_UPPERCASE
:
192 visitor
.VisitUppercase(fCurrent
);
194 case EXFAT_ENTRY_TYPE_LABEL
:
195 visitor
.VisitLabel(fCurrent
);
197 case EXFAT_ENTRY_TYPE_FILE
:
198 visitor
.VisitFile(fCurrent
);
200 case EXFAT_ENTRY_TYPE_FILEINFO
:
201 visitor
.VisitFileInfo(fCurrent
);
203 case EXFAT_ENTRY_TYPE_FILENAME
:
204 visitor
.VisitFilename(fCurrent
);
212 DirectoryIterator::_GetNext(uint16
* utf16Name
, size_t* _codeUnitCount
,
213 ino_t
* _id
, EntryVisitor
* visitor
)
215 size_t nameMax
= *_codeUnitCount
;
216 size_t nameIndex
= 0;
218 int32 chunkCount
= 1;
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
;
227 *_id
= fInode
->GetVolume()->GetIno(fCluster
, fOffset
- 1,
230 TRACE("DirectoryIterator::_GetNext() File chunkCount %" B_PRId32
233 visitor
->VisitFile(fCurrent
);
234 } else if (fCurrent
->type
== EXFAT_ENTRY_TYPE_FILEINFO
) {
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
);
240 visitor
->VisitFileInfo(fCurrent
);
241 } else if (fCurrent
->type
== EXFAT_ENTRY_TYPE_FILENAME
) {
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",
249 visitor
->VisitFilename(fCurrent
);
252 if (chunkCount
== 0 || nameIndex
>= nameMax
)
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
,
263 TRACE("DirectoryIterator::_GetNext() found name: \"%s\", "
264 "length: %d\n", utf8Name
, length
);
274 DirectoryIterator::_NextEntry()
276 if (fCurrent
== NULL
) {
278 fInode
->GetVolume()->ClusterToBlock(fCluster
, block
);
279 block
+= (fOffset
/ fInode
->GetVolume()->EntriesPerBlock())
280 % (1 << fInode
->GetVolume()->SuperBlock().BlocksPerClusterShift());
281 TRACE("DirectoryIterator::_NextEntry() init to block %" B_PRIu64
"\n",
283 fCurrent
= (struct exfat_entry
*)fBlock
.SetTo(block
)
284 + fOffset
% fInode
->GetVolume()->EntriesPerBlock();
285 } else if ((fOffset
% fInode
->GetVolume()->EntriesPerBlock()) == 0) {
287 if ((fOffset
% fInode
->GetVolume()->EntriesPerCluster()) == 0) {
288 fCluster
= fInode
->NextCluster(fCluster
);
289 if (fCluster
== EXFAT_CLUSTER_END
)
290 return B_ENTRY_NOT_FOUND
;
292 fInode
->GetVolume()->ClusterToBlock(fCluster
, block
);
294 block
= fBlock
.BlockNumber() + 1;
296 TRACE("DirectoryIterator::_NextEntry() block %" B_PRIu64
"\n", block
);
297 fCurrent
= (struct exfat_entry
*)fBlock
.SetTo(block
);
302 return fCurrent
->type
== 0 ? B_ENTRY_NOT_FOUND
: B_OK
;