/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <dirent.h>
#include <dirent_private.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <errno_private.h>
#include <syscalls.h>
#include <syscall_utils.h>
#define DIR_BUFFER_SIZE 4096
struct __DIR {
int fd;
short next_entry;
unsigned short entries_left;
long seek_position;
long current_position;
struct dirent first_entry;
};
static int
do_seek_dir(DIR* dir)
{
if (dir->seek_position == dir->current_position)
return 0;
// If the seek position lies before the current position (the usual case),
// rewind to the beginning.
if (dir->seek_position < dir->current_position) {
status_t status = _kern_rewind_dir(dir->fd);
if (status < 0) {
__set_errno(status);
return -1;
}
dir->current_position = 0;
dir->entries_left = 0;
}
// Now skip entries until we have reached seek_position.
while (dir->seek_position > dir->current_position) {
ssize_t count;
long toSkip = dir->seek_position - dir->current_position;
if (toSkip == dir->entries_left) {
// we have to skip exactly all of the currently buffered entries
dir->current_position = dir->seek_position;
dir->entries_left = 0;
return 0;
}
if (toSkip < dir->entries_left) {
// we have to skip only some of the buffered entries
for (; toSkip > 0; toSkip--) {
struct dirent* entry = (struct dirent*)
((uint8*)&dir->first_entry + dir->next_entry);
dir->entries_left--;
dir->next_entry += entry->d_reclen;
}
dir->current_position = dir->seek_position;
return 0;
}
// we have to skip more than the currently buffered entries
dir->current_position += dir->entries_left;
dir->entries_left = 0;
count = _kern_read_dir(dir->fd, &dir->first_entry,
(char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX);
if (count <= 0) {
if (count < 0)
__set_errno(count);
// end of directory
return -1;
}
dir->next_entry = 0;
dir->entries_left = count;
}
return 0;
}
// #pragma mark - private API
DIR*
__create_dir_struct(int fd)
{
/* allocate the memory for the DIR structure */
DIR* dir = (DIR*)malloc(DIR_BUFFER_SIZE);
if (dir == NULL) {
__set_errno(B_NO_MEMORY);
return NULL;
}
dir->fd = fd;
dir->entries_left = 0;
dir->seek_position = 0;
dir->current_position = 0;
return dir;
}
// #pragma mark - public API
DIR*
fdopendir(int fd)
{
DIR* dir;
// Since our standard file descriptors can't be used as directory file
// descriptors, we have to open a fresh one explicitly.
int dirFD = _kern_open_dir(fd, NULL);
if (dirFD < 0) {
__set_errno(dirFD);
return NULL;
}
// Since applications are allowed to use the file descriptor after a call
// to fdopendir() without changing its state (like for other *at()
// functions), we cannot close it now.
// We dup2() the new FD to the previous location instead.
if (dup2(dirFD, fd) == -1)
close(fd);
else {
close(dirFD);
dirFD = fd;
fcntl(dirFD, F_SETFD, FD_CLOEXEC);
// reset close-on-exec which is cleared by dup()
}
dir = __create_dir_struct(dirFD);
if (dir == NULL) {
close(dirFD);
return NULL;
}
return dir;
}
DIR*
opendir(const char* path)
{
DIR* dir;
int fd = _kern_open_dir(-1, path);
if (fd < 0) {
__set_errno(fd);
return NULL;
}
// allocate the DIR structure
if ((dir = __create_dir_struct(fd)) == NULL) {
_kern_close(fd);
return NULL;
}
return dir;
}
int
closedir(DIR* dir)
{
int status;
if (dir == NULL) {
__set_errno(B_BAD_VALUE);
return -1;
}
status = _kern_close(dir->fd);
free(dir);
RETURN_AND_SET_ERRNO(status);
}
struct dirent*
readdir(DIR* dir)
{
ssize_t count;
if (dir->seek_position != dir->current_position) {
if (do_seek_dir(dir) != 0)
return NULL;
}
if (dir->entries_left > 0) {
struct dirent *dirent
= (struct dirent *)((uint8 *)&dir->first_entry + dir->next_entry);
dir->entries_left--;
dir->next_entry += dirent->d_reclen;
dir->seek_position++;
dir->current_position++;
return dirent;
}
// we need to retrieve new entries
count = _kern_read_dir(dir->fd, &dir->first_entry,
(char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX);
if (count <= 0) {
if (count < 0)
__set_errno(count);
// end of directory
return NULL;
}
dir->entries_left = count - 1;
dir->next_entry = dir->first_entry.d_reclen;
dir->seek_position++;
dir->current_position++;
return &dir->first_entry;
}
int
readdir_r(DIR* dir, struct dirent* entry, struct dirent** _result)
{
ssize_t count;
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
*_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 0;
}
void
rewinddir(DIR* dir)
{
dir->seek_position = 0;
}
void
seekdir(DIR* dir, long int position)
{
dir->seek_position = position;
}
long int
telldir(DIR* dir)
{
return dir->seek_position;
}
int
dirfd(DIR* dir)
{
return dir->fd;
}
int
alphasort(const struct dirent** entry1, const struct dirent** entry2)
{
return strcmp((*entry1)->d_name, (*entry2)->d_name);
}
int
scandir(const char* path, struct dirent*** _entryArray,
int (*selectFunc)(const struct dirent*),
int (*compareFunc)(const struct dirent** entry1,
const struct dirent** entry2))
{
struct dirent** array = NULL;
size_t arrayCapacity = 0;
size_t arrayCount = 0;
DIR* dir = opendir(path);
if (dir == NULL)
return -1;
while (true) {
struct dirent* copiedEntry;
struct dirent* entry = readdir(dir);
if (entry == NULL)
break;
// Check whether or not we should include this entry
if (selectFunc != NULL && !selectFunc(entry))
continue;
copiedEntry = malloc(entry->d_reclen);
if (copiedEntry == NULL)
goto error;
memcpy(copiedEntry, entry, entry->d_reclen);
// Put it into the array
if (arrayCount == arrayCapacity) {
struct dirent** newArray;
// Enlarge array
if (arrayCapacity == 0)
arrayCapacity = 64;
else
arrayCapacity *= 2;
newArray = realloc(array, arrayCapacity * sizeof(void*));
if (newArray == NULL) {
free(copiedEntry);
goto error;
}
array = newArray;
}
array[arrayCount++] = copiedEntry;
}
closedir(dir);
if (arrayCount > 0 && compareFunc != NULL) {
qsort(array, arrayCount, sizeof(void*),
(int (*)(const void*, const void*))compareFunc);
}
*_entryArray = array;
return arrayCount;
error:
closedir(dir);
while (arrayCount-- > 0)
free(array[arrayCount]);
free(array);
return -1;
}
↑ V641 The size of the allocated memory buffer is not a multiple of the element size.