/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2014 Haiku, Inc. All rights reserved.
*
* Distributed under the terms of the MIT License.
*
* Authors:
* Jérôme Duval, korli@users.berlios.de
* John Scipione, jscipione@gmail.com
*/
#include "DirectoryIterator.h"
#include <stdlib.h>
#include "convertutf.h"
#include "Inode.h"
//#define TRACE_EXFAT
#ifdef TRACE_EXFAT
# define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
#define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
// #pragma mark - DirectoryIterator
DirectoryIterator::DirectoryIterator(Inode* inode)
:
fOffset(-2),
fCluster(inode->StartCluster()),
fInode(inode),
fBlock(inode->GetVolume()),
fCurrent(NULL)
{
TRACE("DirectoryIterator::DirectoryIterator() %" B_PRIu32 "\n", fCluster);
}
DirectoryIterator::~DirectoryIterator()
{
}
status_t
DirectoryIterator::InitCheck()
{
return B_OK;
}
status_t
DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id,
EntryVisitor* visitor)
{
if (fCluster == EXFAT_CLUSTER_END)
return B_ENTRY_NOT_FOUND;
if (fOffset == -2) {
if (*_nameLength < 3)
return B_BUFFER_OVERFLOW;
*_nameLength = 2;
strlcpy(name, "..", *_nameLength + 1);
if (fInode->ID() == 1)
*_id = fInode->ID();
else
*_id = fInode->Parent();
fOffset = -1;
TRACE("DirectoryIterator::GetNext() found \"..\"\n");
return B_OK;
} else if (fOffset == -1) {
if (*_nameLength < 2)
return B_BUFFER_OVERFLOW;
*_nameLength = 1;
strlcpy(name, ".", *_nameLength + 1);
*_id = fInode->ID();
fOffset = 0;
TRACE("DirectoryIterator::GetNext() found \".\"\n");
return B_OK;
}
size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
uint16 utf16Name[utf16CodeUnitCount];
status_t status = _GetNext(utf16Name, &utf16CodeUnitCount, _id, visitor);
if (status == B_OK && utf16CodeUnitCount > 0) {
ssize_t lengthOrStatus = utf16le_to_utf8(utf16Name, utf16CodeUnitCount,
name, *_nameLength);
if (lengthOrStatus < 0) {
status = (status_t)lengthOrStatus;
if (status == B_NAME_TOO_LONG)
*_nameLength = strlen(name);
} else
*_nameLength = (size_t)lengthOrStatus;
}
if (status == B_OK) {
TRACE("DirectoryIterator::GetNext() cluster: %" B_PRIu32 " id: "
"%" B_PRIdINO " name: \"%s\", length: %zu\n", fInode->Cluster(),
*_id, name, *_nameLength);
} else if (status != B_ENTRY_NOT_FOUND) {
ERROR("DirectoryIterator::GetNext() (%s)\n", strerror(status));
}
return status;
}
status_t
DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
{
if (strcmp(name, ".") == 0) {
*_id = fInode->ID();
return B_OK;
} else if (strcmp(name, "..") == 0) {
if (fInode->ID() == 1)
*_id = fInode->ID();
else
*_id = fInode->Parent();
return B_OK;
}
Rewind();
fOffset = 0;
size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
uint16 utf16Name[utf16CodeUnitCount];
while (_GetNext(utf16Name, &utf16CodeUnitCount, _id) == B_OK) {
char utf8Name[nameLength + 1];
ssize_t lengthOrStatus = utf16le_to_utf8(utf16Name, utf16CodeUnitCount,
utf8Name, sizeof(utf8Name));
if (lengthOrStatus > 0 && (size_t)lengthOrStatus == nameLength
&& strncmp(utf8Name, name, nameLength) == 0) {
TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n",
*_id);
return B_OK;
}
utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
}
TRACE("DirectoryIterator::Lookup() not found %s\n", name);
return B_ENTRY_NOT_FOUND;
}
status_t
DirectoryIterator::LookupEntry(EntryVisitor* visitor)
{
fCluster = fInode->Cluster();
fOffset = fInode->Offset();
size_t utf16CodeUnitCount = EXFAT_FILENAME_MAX_LENGTH / sizeof(uint16);
uint16 utf16Name[utf16CodeUnitCount];
return _GetNext(utf16Name, &utf16CodeUnitCount, NULL, visitor);
}
status_t
DirectoryIterator::Rewind()
{
fOffset = -2;
fCluster = fInode->StartCluster();
return B_OK;
}
void
DirectoryIterator::Iterate(EntryVisitor &visitor)
{
fOffset = 0;
fCluster = fInode->StartCluster();
while (_NextEntry() != B_ENTRY_NOT_FOUND) {
switch (fCurrent->type) {
case EXFAT_ENTRY_TYPE_BITMAP:
visitor.VisitBitmap(fCurrent);
break;
case EXFAT_ENTRY_TYPE_UPPERCASE:
visitor.VisitUppercase(fCurrent);
break;
case EXFAT_ENTRY_TYPE_LABEL:
visitor.VisitLabel(fCurrent);
break;
case EXFAT_ENTRY_TYPE_FILE:
visitor.VisitFile(fCurrent);
break;
case EXFAT_ENTRY_TYPE_FILEINFO:
visitor.VisitFileInfo(fCurrent);
break;
case EXFAT_ENTRY_TYPE_FILENAME:
visitor.VisitFilename(fCurrent);
break;
}
}
}
status_t
DirectoryIterator::_GetNext(uint16* utf16Name, size_t* _codeUnitCount,
ino_t* _id, EntryVisitor* visitor)
{
size_t nameMax = *_codeUnitCount;
size_t nameIndex = 0;
status_t status;
int32 chunkCount = 1;
while ((status = _NextEntry()) == B_OK) {
TRACE("DirectoryIterator::_GetNext() %" B_PRIu32 "/%p, type 0x%x, "
"offset %" B_PRId64 "\n", fInode->Cluster(), fCurrent,
fCurrent->type, fOffset);
if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) {
chunkCount = fCurrent->file.chunkCount;
if (_id != NULL) {
*_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1,
fInode->ID());
}
TRACE("DirectoryIterator::_GetNext() File chunkCount %" B_PRId32
"\n", chunkCount);
if (visitor != NULL)
visitor->VisitFile(fCurrent);
} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) {
chunkCount--;
*_codeUnitCount = (size_t)fCurrent->file_info.name_length;
TRACE("DirectoryIterator::_GetNext() Filename chunk: %" B_PRId32
", code unit count: %" B_PRIu8 "\n", chunkCount, *_codeUnitCount);
if (visitor != NULL)
visitor->VisitFileInfo(fCurrent);
} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) {
chunkCount--;
size_t utf16Length = sizeof(fCurrent->file_name.name);
memcpy(utf16Name + nameIndex, fCurrent->file_name.name, utf16Length);
nameIndex += utf16Length / sizeof(uint16);
TRACE("DirectoryIterator::_GetNext() Filename index: %zu\n",
nameIndex);
if (visitor != NULL)
visitor->VisitFilename(fCurrent);
}
if (chunkCount == 0 || nameIndex >= nameMax)
break;
}
#ifdef TRACE_EXFAT
if (status == B_OK) {
size_t utf8Length = B_FILE_NAME_LENGTH * 4;
char utf8Name[utf8Length + 1];
ssize_t length = utf16le_to_utf8(utf16Name, *_codeUnitCount, utf8Name,
utf8Length);
if (length > 0) {
TRACE("DirectoryIterator::_GetNext() found name: \"%s\", "
"length: %d\n", utf8Name, length);
}
}
#endif
return status;
}
status_t
DirectoryIterator::_NextEntry()
{
if (fCurrent == NULL) {
fsblock_t block;
if (fInode->GetVolume()->ClusterToBlock(fCluster, block) != B_OK)
return B_BAD_DATA;
block += (fOffset / fInode->GetVolume()->EntriesPerBlock())
% (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift());
TRACE("DirectoryIterator::_NextEntry() init to block %" B_PRIu64 "\n",
block);
fCurrent = (struct exfat_entry*)fBlock.SetTo(block)
+ fOffset % fInode->GetVolume()->EntriesPerBlock();
} else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) {
fsblock_t block;
if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) {
fCluster = fInode->NextCluster(fCluster);
if (fCluster == EXFAT_CLUSTER_END)
return B_ENTRY_NOT_FOUND;
if (fInode->GetVolume()->ClusterToBlock(fCluster, block) != B_OK)
return B_BAD_DATA;
} else
block = fBlock.BlockNumber() + 1;
TRACE("DirectoryIterator::_NextEntry() block %" B_PRIu64 "\n", block);
fCurrent = (struct exfat_entry*)fBlock.SetTo(block);
} else
fCurrent++;
fOffset++;
return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK;
}
↑ V629 Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.