a37f372973
Issue: The kernel will pad the entry->d_reclen in a getdents64 call to a long-word boundary. For very long records, this could exceed the size of a struct dirent. The mismatch in the size was causing error paranoid checking code in bionic to fail... thus causing an early "end" when reading the dirent structures from the kernel buffer. Test: ls mkdir abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu ls Change-Id: I75d1f8e45e1655fdd7bac4a08a481d086f28073a Author: Bruce Beare <bruce.j.beare@intel.com>
274 lines
6.4 KiB
C
274 lines
6.4 KiB
C
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <errno.h>
|
|
|
|
struct DIR
|
|
{
|
|
int _DIR_fd;
|
|
size_t _DIR_avail;
|
|
struct dirent* _DIR_next;
|
|
pthread_mutex_t _DIR_lock;
|
|
struct dirent _DIR_buff[15];
|
|
};
|
|
|
|
int dirfd(DIR* dirp)
|
|
{
|
|
return dirp->_DIR_fd;
|
|
}
|
|
|
|
DIR* opendir( const char* dirpath )
|
|
{
|
|
DIR* dir = malloc(sizeof(DIR));
|
|
|
|
if (!dir)
|
|
goto Exit;
|
|
|
|
dir->_DIR_fd = open(dirpath, O_RDONLY|O_DIRECTORY);
|
|
if (dir->_DIR_fd < 0)
|
|
{
|
|
free(dir);
|
|
dir = NULL;
|
|
}
|
|
else
|
|
{
|
|
dir->_DIR_avail = 0;
|
|
dir->_DIR_next = NULL;
|
|
pthread_mutex_init( &dir->_DIR_lock, NULL );
|
|
}
|
|
Exit:
|
|
return dir;
|
|
}
|
|
|
|
|
|
DIR* fdopendir(int fd)
|
|
{
|
|
DIR* dir = malloc(sizeof(DIR));
|
|
|
|
if (!dir)
|
|
return 0;
|
|
|
|
dir->_DIR_fd = fd;
|
|
dir->_DIR_avail = 0;
|
|
dir->_DIR_next = NULL;
|
|
pthread_mutex_init( &dir->_DIR_lock, NULL );
|
|
|
|
return dir;
|
|
}
|
|
|
|
|
|
static struct dirent*
|
|
_readdir_unlocked(DIR* dir)
|
|
{
|
|
struct dirent* entry;
|
|
#ifndef NDEBUG
|
|
unsigned reclen;
|
|
#endif
|
|
|
|
if ( !dir->_DIR_avail )
|
|
{
|
|
int rc;
|
|
|
|
for (;;) {
|
|
rc = getdents( dir->_DIR_fd, dir->_DIR_buff, sizeof(dir->_DIR_buff));
|
|
if (rc >= 0 || errno != EINTR)
|
|
break;
|
|
}
|
|
if (rc <= 0)
|
|
return NULL;
|
|
|
|
dir->_DIR_avail = rc;
|
|
dir->_DIR_next = dir->_DIR_buff;
|
|
}
|
|
|
|
entry = dir->_DIR_next;
|
|
|
|
/* perform some sanity checks here */
|
|
if (((long)(void*)entry & 3) != 0)
|
|
return NULL;
|
|
|
|
#ifndef NDEBUG
|
|
// paranoid testing of the interface with the kernel getdents64 system call
|
|
reclen = offsetof(struct dirent, d_name) + strlen(entry->d_name) + 1;
|
|
if ( reclen > sizeof(*entry) || reclen <= offsetof(struct dirent, d_name) )
|
|
goto Bad;
|
|
|
|
if ( (char*)entry + reclen > (char*)dir->_DIR_buff + sizeof(dir->_DIR_buff) )
|
|
goto Bad;
|
|
|
|
if ( !memchr( entry->d_name, 0, reclen - offsetof(struct dirent, d_name)) )
|
|
goto Bad;
|
|
#endif
|
|
|
|
dir->_DIR_next = (struct dirent*)((char*)entry + entry->d_reclen);
|
|
dir->_DIR_avail -= entry->d_reclen;
|
|
|
|
return entry;
|
|
|
|
Bad:
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
struct dirent*
|
|
readdir(DIR * dir)
|
|
{
|
|
struct dirent *entry = NULL;
|
|
|
|
pthread_mutex_lock( &dir->_DIR_lock );
|
|
entry = _readdir_unlocked(dir);
|
|
pthread_mutex_unlock( &dir->_DIR_lock );
|
|
|
|
return entry;
|
|
}
|
|
|
|
|
|
int readdir_r(DIR* dir, struct dirent *entry, struct dirent **result)
|
|
{
|
|
struct dirent* ent;
|
|
int save_errno = errno;
|
|
int retval;
|
|
|
|
*result = NULL;
|
|
errno = 0;
|
|
|
|
pthread_mutex_lock( &dir->_DIR_lock );
|
|
|
|
ent = _readdir_unlocked(dir);
|
|
retval = errno;
|
|
if (ent == NULL) {
|
|
if (!retval) {
|
|
errno = save_errno;
|
|
}
|
|
} else {
|
|
if (!retval) {
|
|
errno = save_errno;
|
|
*result = entry;
|
|
memcpy( entry, ent, ent->d_reclen );
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock( &dir->_DIR_lock );
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
int closedir(DIR *dir)
|
|
{
|
|
int rc;
|
|
|
|
rc = close(dir->_DIR_fd);
|
|
dir->_DIR_fd = -1;
|
|
|
|
pthread_mutex_destroy( &dir->_DIR_lock );
|
|
|
|
free(dir);
|
|
return rc;
|
|
}
|
|
|
|
|
|
void rewinddir(DIR *dir)
|
|
{
|
|
pthread_mutex_lock( &dir->_DIR_lock );
|
|
lseek( dir->_DIR_fd, 0, SEEK_SET );
|
|
dir->_DIR_avail = 0;
|
|
pthread_mutex_unlock( &dir->_DIR_lock );
|
|
}
|
|
|
|
|
|
int alphasort(const void *a, const void *b)
|
|
{
|
|
struct dirent **d1, **d2;
|
|
|
|
d1 = (struct dirent **) a;
|
|
d2 = (struct dirent **) b;
|
|
return strcmp((*d1)->d_name, (*d2)->d_name);
|
|
}
|
|
|
|
|
|
int scandir(const char *dir, struct dirent ***namelist,
|
|
int(*filter)(const struct dirent *),
|
|
int(*compar)(const struct dirent **, const struct dirent **))
|
|
{
|
|
DIR *d;
|
|
int n_elem = 0;
|
|
struct dirent *this_de, *de;
|
|
struct dirent **de_list = NULL;
|
|
int de_list_size = 0;
|
|
|
|
d = opendir(dir);
|
|
if (d == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
while ((this_de = readdir(d)) != NULL) {
|
|
if (filter && (*filter)(this_de) == 0) {
|
|
continue;
|
|
}
|
|
if (n_elem == 0) {
|
|
de_list_size = 4;
|
|
de_list = (struct dirent **)
|
|
malloc(sizeof(struct dirent *)*de_list_size);
|
|
if (de_list == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
else if (n_elem == de_list_size) {
|
|
struct dirent **de_list_new;
|
|
|
|
de_list_size += 10;
|
|
de_list_new = (struct dirent **)
|
|
realloc(de_list, sizeof(struct dirent *)*de_list_size);
|
|
if (de_list_new == NULL) {
|
|
free(de_list);
|
|
return -1;
|
|
}
|
|
de_list = de_list_new;
|
|
}
|
|
de = (struct dirent *) malloc(sizeof(struct dirent));
|
|
*de = *this_de;
|
|
de_list[n_elem++] = de;
|
|
}
|
|
closedir(d);
|
|
if (n_elem && compar) {
|
|
qsort(de_list, n_elem, sizeof(struct dirent *),
|
|
(int (*)(const void *, const void *)) compar);
|
|
}
|
|
*namelist = de_list;
|
|
return n_elem;
|
|
}
|