From ee668d24e34a6da029d2f188118c023929f97391 Mon Sep 17 00:00:00 2001 From: Jerome Duval Date: Sat, 4 May 2013 18:12:51 +0200 Subject: [PATCH] exfat: Return more than a single dirent at a time in exfat_read_dir(). * similar to what mmlr did in hrev45575 for bfs. * DirectoryIterator could try to read past the end of the directory. * replaced a dprintf with a TRACE() statement. --- .../file_systems/exfat/DirectoryIterator.cpp | 23 ++++++--- .../kernel/file_systems/exfat/Volume.cpp | 4 +- .../kernel/file_systems/exfat/encodings.cpp | 18 +++++-- .../file_systems/exfat/kernel_interface.cpp | 50 ++++++++++++------- 4 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/add-ons/kernel/file_systems/exfat/DirectoryIterator.cpp b/src/add-ons/kernel/file_systems/exfat/DirectoryIterator.cpp index ffeaff1bac..bfa0553136 100644 --- a/src/add-ons/kernel/file_systems/exfat/DirectoryIterator.cpp +++ b/src/add-ons/kernel/file_systems/exfat/DirectoryIterator.cpp @@ -47,9 +47,13 @@ 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) { - *_nameLength = 3; - strlcpy(name, "..", *_nameLength); + if (*_nameLength < 3) + return B_BUFFER_OVERFLOW; + *_nameLength = 2; + strlcpy(name, "..", *_nameLength + 1); if (fInode->ID() == 1) *_id = fInode->ID(); else @@ -58,19 +62,22 @@ DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id, TRACE("DirectoryIterator::GetNext() found ..\n"); return B_OK; } else if (fOffset == -1) { - *_nameLength = 2; - strlcpy(name, ".", *_nameLength); + 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; } - uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH]; + uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH + 1]; size_t nameLength = EXFAT_FILENAME_MAX_LENGTH; status_t status = _GetNext(unicodeName, &nameLength, _id, visitor); if (status == B_OK && name != NULL) { - unicode_to_utf8(unicodeName, nameLength, (uint8 *)name , _nameLength); + status = unicode_to_utf8(unicodeName, nameLength, (uint8 *)name, + _nameLength); TRACE("DirectoryIterator::GetNext() %ld %s, %" B_PRIdINO "\n", fInode->Cluster(), name, *_id); } @@ -96,7 +103,7 @@ DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id) Rewind(); fOffset = 0; - uchar currentName[EXFAT_FILENAME_MAX_LENGTH]; + uchar currentName[EXFAT_FILENAME_MAX_LENGTH + 1]; size_t currentLength = EXFAT_FILENAME_MAX_LENGTH; while (_GetNext((uchar*)currentName, ¤tLength, _id) == B_OK) { char utfName[EXFAT_FILENAME_MAX_LENGTH]; @@ -122,7 +129,7 @@ DirectoryIterator::LookupEntry(EntryVisitor* visitor) fCluster = fInode->Cluster(); fOffset = fInode->Offset(); - uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH]; + uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH + 1]; size_t nameLength = EXFAT_FILENAME_MAX_LENGTH; return _GetNext(unicodeName, &nameLength, NULL, visitor); } diff --git a/src/add-ons/kernel/file_systems/exfat/Volume.cpp b/src/add-ons/kernel/file_systems/exfat/Volume.cpp index 1881909751..8fea222077 100644 --- a/src/add-ons/kernel/file_systems/exfat/Volume.cpp +++ b/src/add-ons/kernel/file_systems/exfat/Volume.cpp @@ -218,7 +218,7 @@ LabelVisitor::LabelVisitor(Volume* volume) bool LabelVisitor::VisitLabel(struct exfat_entry* entry) { - dprintf("LabelVisitor::VisitLabel()\n"); + TRACE("LabelVisitor::VisitLabel()\n"); char utfName[30]; size_t utfLength = 30; unicode_to_utf8((const uchar*)entry->name_label.name, @@ -418,6 +418,8 @@ Volume::LoadSuperBlock() status_t Volume::ClusterToBlock(cluster_t cluster, fsblock_t &block) { + if (cluster < 2) + return B_BAD_VALUE; block = ((cluster - 2) << SuperBlock().BlocksPerClusterShift()) + SuperBlock().FirstDataBlock(); TRACE("Volume::ClusterToBlock() cluster %lu %u %lu: %llu, %lu\n", cluster, diff --git a/src/add-ons/kernel/file_systems/exfat/encodings.cpp b/src/add-ons/kernel/file_systems/exfat/encodings.cpp index 53e5b35b95..09c69c56d8 100644 --- a/src/add-ons/kernel/file_systems/exfat/encodings.cpp +++ b/src/add-ons/kernel/file_systems/exfat/encodings.cpp @@ -106,6 +106,7 @@ _lendian_unicode_to_utf8( int32 dstLimit = *dstLen; int32 srcCount = 0; int32 dstCount = 0; + status_t status = B_ERROR; for (srcCount = 0; srcCount < srcLimit; srcCount += 2) { uint16 *UNICODE = (uint16 *)&src[srcCount]; @@ -119,19 +120,22 @@ _lendian_unicode_to_utf8( u_lendian_to_utf8(UTF8, UNICODE); utf8Len = UTF8 - utf8; - if ((dstCount + utf8Len) > dstLimit) + if ((dstCount + utf8Len) > dstLimit) { + status = B_BUFFER_OVERFLOW; break; + } for (j = 0; j < utf8Len; j++) dst[dstCount + j] = utf8[j]; dstCount += utf8Len; + status = B_OK; } *srcLen = srcCount; *dstLen = dstCount; dst[dstCount] = '\0'; - return ((dstCount > 0) ? B_NO_ERROR : B_ERROR); + return status; } // utf8 to LENDIAN unicode @@ -146,6 +150,7 @@ _utf8_to_lendian_unicode( int32 dstLimit = *dstLen - 1; int32 srcCount = 0; int32 dstCount = 0; + status_t status = B_ERROR; while ((srcCount < srcLimit) && (dstCount < dstLimit)) { uint16 unicode; @@ -157,20 +162,25 @@ _utf8_to_lendian_unicode( break; utf8_to_u_hostendian(UTF8, UNICODE, err_flag); - if(err_flag == 1) + if (err_flag == 1) return EINVAL; unicode = B_HOST_TO_LENDIAN_INT16(unicode); + if ((dstCount + 1) > dstLimit) { + status = B_BUFFER_OVERFLOW; + break; + } dst[dstCount++] = unicode & 0xFF; dst[dstCount++] = unicode >> 8; srcCount += UTF8 - ((uchar *)(src + srcCount)); + status = B_OK; } *srcLen = srcCount; *dstLen = dstCount; - return ((dstCount > 0) ? B_NO_ERROR : B_ERROR); + return status; } diff --git a/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp b/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp index 2d0a6b2b6b..2fd7d2fbbf 100644 --- a/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp @@ -179,7 +179,7 @@ static status_t exfat_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, uint32* _flags, bool reenter) { - TRACE("get_vnode %lu\n", id); + TRACE("get_vnode %" B_PRIdINO "\n", id); Volume* volume = (Volume*)_volume->private_volume; Inode* inode = new(std::nothrow) Inode(volume, id); @@ -357,7 +357,7 @@ exfat_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, return status; } - TRACE("exfat_lookup: ID %d\n", *_vnodeID); + TRACE("exfat_lookup: ID %" B_PRIdINO "\n", *_vnodeID); return get_vnode(volume->FSVolume(), *_vnodeID, NULL); } @@ -526,24 +526,40 @@ exfat_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, { TRACE("exfat_read_dir\n"); DirectoryIterator* iterator = (DirectoryIterator*)_cookie; - - size_t length = bufferSize; - ino_t id; - status_t status = iterator->GetNext(dirent->d_name, &length, &id); - if (status == B_ENTRY_NOT_FOUND) { - *_num = 0; - return B_OK; - } else if (status != B_OK) - return status; - Volume* volume = (Volume*)_volume->private_volume; - dirent->d_dev = volume->ID(); - dirent->d_ino = id; - dirent->d_reclen = sizeof(struct dirent) + length; - *_num = 1; + + uint32 maxCount = *_num; + uint32 count = 0; + while (count < maxCount && bufferSize > sizeof(struct dirent)) { + ino_t id; + size_t length = bufferSize - sizeof(struct dirent) + 1; + + status_t status = iterator->GetNext(dirent->d_name, &length, &id); + if (status == B_ENTRY_NOT_FOUND) + break; + + if (status == B_BUFFER_OVERFLOW) { + // the remaining name buffer length was too small + if (count == 0) + return B_BUFFER_OVERFLOW; + break; + } + + if (status != B_OK) + return status; + + dirent->d_dev = volume->ID(); + dirent->d_ino = id; + dirent->d_reclen = sizeof(struct dirent) + length; + + bufferSize -= dirent->d_reclen; + dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); + count++; + } + + *_num = count; TRACE("exfat_read_dir end\n"); - return B_OK; }