diff --git a/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp b/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp index 0803f55425..6ea65249bd 100644 --- a/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011, Jérôme Duval, korli@users.berlios.de. + * Copyright 2011-2013, Jérôme Duval, korli@users.berlios.de. * This file may be used under the terms of the MIT License. */ @@ -52,14 +52,18 @@ status_t DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id) { if (fOffset == 0) { - *_nameLength = 3; - strlcpy(name, "..", *_nameLength); + if (*_nameLength < 3) + return B_BUFFER_OVERFLOW; + *_nameLength = 2; + strlcpy(name, "..", *_nameLength + 1); *_id = fInode->ID(); fOffset = 1; 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); fOffset = 2; if (fInode->ID() == BTRFS_OBJECT_ID_CHUNK_TREE) { *_id = fInode->ID(); @@ -85,12 +89,19 @@ DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id) entry = (btrfs_dir_entry *)((uint8*)entry + entry->Length()); } + size_t length = entry->NameLength(); + TRACE("DirectoryIterator::GetNext() entries_length %ld name_length %d\n", entries_length, entry->NameLength()); - memcpy(name, entry + 1, entry->NameLength()); - name[entry->NameLength()] = '\0'; - *_nameLength = entry->NameLength(); + if (length + 1 > *_nameLength) { + free(entries); + return B_BUFFER_OVERFLOW; + } + + memcpy(name, entry + 1, length); + name[length] = '\0'; + *_nameLength = length; *_id = entry->InodeID(); free(entries); diff --git a/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp index 5c6905b5f5..eaac2c97c6 100644 --- a/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp @@ -520,22 +520,41 @@ btrfs_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num) { 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; return B_OK; }