libroot: Implement readdir_r in terms of readdir.

Originally _r mostly avoided the buffers in the DIR* and invoked
_kern_read_dir correctly, however that did not interact properly
with the non-reentrant functions and so this method was reworked
some years ago to check entries_left and copy results out of the buffer.

However, that was done improperly; it just copied the structs,
which as they have a VLA at the end, meant the name entry was not
copied at all if an entry was read out of the buffer.

Since we have to read entries out of the buffer anyway,
just invoke the real readdir() and then memcpy what it returns.
Use an ErrnoMaintainer to prevent errno from being affected.
This commit is contained in:
Augustin Cavalier 2022-09-27 20:10:34 -04:00
parent eafa0e1f49
commit ba59081e52

View File

@ -14,6 +14,7 @@
#include <string.h>
#include <errno_private.h>
#include <ErrnoMaintainer.h>
#include <syscalls.h>
#include <syscall_utils.h>
@ -243,41 +244,17 @@ readdir(DIR* dir)
int
readdir_r(DIR* dir, struct dirent* entry, struct dirent** _result)
{
ssize_t count;
BPrivate::ErrnoMaintainer _;
errno = 0;
if (dir->seek_position != dir->current_position) {
if (do_seek_dir(dir) != 0)
return -1;
}
if (dir->entries_left > 0) {
*_result
= (struct dirent *)((uint8 *)&dir->first_entry + dir->next_entry);
dir->entries_left--;
dir->next_entry += (*_result)->d_reclen;
dir->seek_position++;
dir->current_position++;
return 0;
}
count = _kern_read_dir(dir->fd, entry, sizeof(struct dirent)
+ B_FILE_NAME_LENGTH, 1);
if (count < B_OK)
return count;
if (count == 0) {
// end of directory
struct dirent* dirent = readdir(dir);
if (dirent == NULL) {
*_result = NULL;
} else {
*_result = entry;
dir->entries_left = count - 1;
dir->next_entry = dir->first_entry.d_reclen;
dir->seek_position++;
dir->current_position++;
return errno;
}
memcpy(entry, dirent, dirent->d_reclen);
*_result = entry;
return 0;
}