btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / exfat / DirectoryIterator.cpp
blob278af03b1e79bf45c953474bedbd859a5473abc9
1 /*
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.
7 * Authors:
8 * Jérôme Duval, korli@users.berlios.de
9 * John Scipione, jscipione@gmail.com
13 #include "DirectoryIterator.h"
15 #include <stdlib.h>
17 #include "convertutf.h"
19 #include "Inode.h"
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
29 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
32 // #pragma mark - DirectoryIterator
35 DirectoryIterator::DirectoryIterator(Inode* inode)
37 fOffset(-2),
38 fCluster(inode->StartCluster()),
39 fInode(inode),
40 fBlock(inode->GetVolume()),
41 fCurrent(NULL)
43 TRACE("DirectoryIterator::DirectoryIterator() %" B_PRIu32 "\n", fCluster);
47 DirectoryIterator::~DirectoryIterator()
52 status_t
53 DirectoryIterator::InitCheck()
55 return B_OK;
59 status_t
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;
66 if (fOffset == -2) {
67 if (*_nameLength < 3)
68 return B_BUFFER_OVERFLOW;
70 *_nameLength = 2;
71 strlcpy(name, "..", *_nameLength + 1);
72 if (fInode->ID() == 1)
73 *_id = fInode->ID();
74 else
75 *_id = fInode->Parent();
77 fOffset = -1;
78 TRACE("DirectoryIterator::GetNext() found \"..\"\n");
80 return B_OK;
81 } else if (fOffset == -1) {
82 if (*_nameLength < 2)
83 return B_BUFFER_OVERFLOW;
85 *_nameLength = 1;
86 strlcpy(name, ".", *_nameLength + 1);
87 *_id = fInode->ID();
88 fOffset = 0;
89 TRACE("DirectoryIterator::GetNext() found \".\"\n");
91 return B_OK;
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;
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));
116 return status;
120 status_t
121 DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
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();
132 return B_OK;
135 Rewind();
136 fOffset = 0;
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;
150 utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
153 TRACE("DirectoryIterator::Lookup() not found %s\n", name);
155 return B_ENTRY_NOT_FOUND;
159 status_t
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);
171 status_t
172 DirectoryIterator::Rewind()
174 fOffset = -2;
175 fCluster = fInode->StartCluster();
176 return B_OK;
180 void
181 DirectoryIterator::Iterate(EntryVisitor &visitor)
183 fOffset = 0;
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);
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;
211 status_t
212 DirectoryIterator::_GetNext(uint16* utf16Name, size_t* _codeUnitCount,
213 ino_t* _id, EntryVisitor* visitor)
215 size_t nameMax = *_codeUnitCount;
216 size_t nameIndex = 0;
217 status_t status;
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;
226 if (_id != NULL) {
227 *_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1,
228 fInode->ID());
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);
252 if (chunkCount == 0 || nameIndex >= nameMax)
253 break;
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);
267 #endif
269 return status;
273 status_t
274 DirectoryIterator::_NextEntry()
276 if (fCurrent == NULL) {
277 fsblock_t block;
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",
282 block);
283 fCurrent = (struct exfat_entry*)fBlock.SetTo(block)
284 + fOffset % fInode->GetVolume()->EntriesPerBlock();
285 } else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) {
286 fsblock_t block;
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);
293 } else
294 block = fBlock.BlockNumber() + 1;
296 TRACE("DirectoryIterator::_NextEntry() block %" B_PRIu64 "\n", block);
297 fCurrent = (struct exfat_entry*)fBlock.SetTo(block);
298 } else
299 fCurrent++;
301 fOffset++;
302 return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK;