Reimplement scandir(3).
The old scandir implementation didn't take into account the varying size of directory entries, and didn't correctly clean up on its error exits. Bug: 7339844 Change-Id: Ib40e3564709752241a3119a496cbb2192e3f9abe
This commit is contained in:
parent
f6bb5bf498
commit
701bec2af3
5 changed files with 175 additions and 55 deletions
|
@ -293,6 +293,7 @@ libc_bionic_src_files := \
|
|||
bionic/pthread_sigmask.cpp \
|
||||
bionic/raise.cpp \
|
||||
bionic/sbrk.cpp \
|
||||
bionic/scandir.cpp \
|
||||
bionic/__set_errno.cpp \
|
||||
bionic/setlocale.cpp \
|
||||
bionic/signalfd.cpp \
|
||||
|
|
|
@ -27,11 +27,9 @@
|
|||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
@ -146,51 +144,6 @@ void rewinddir(DIR* d) {
|
|||
d->available_bytes_ = 0;
|
||||
}
|
||||
|
||||
int scandir(const char* path, dirent*** namelist,
|
||||
int(*filter)(const dirent*),
|
||||
int(*compar)(const dirent**, const dirent**))
|
||||
{
|
||||
int n_elem = 0;
|
||||
dirent* this_de, *de;
|
||||
dirent** de_list = NULL;
|
||||
int de_list_size = 0;
|
||||
|
||||
DIR* d = opendir(path);
|
||||
if (d == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((this_de = readdir(d)) != NULL) {
|
||||
if (filter != NULL && (*filter)(this_de) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (n_elem == 0) {
|
||||
de_list_size = 4;
|
||||
de_list = (dirent**) malloc(sizeof(dirent*) * de_list_size);
|
||||
if (de_list == NULL) {
|
||||
return -1;
|
||||
}
|
||||
} else if (n_elem == de_list_size) {
|
||||
de_list_size += 10;
|
||||
dirent** de_list_new = (dirent**) realloc(de_list, sizeof(dirent*) * de_list_size);
|
||||
if (de_list_new == NULL) {
|
||||
free(de_list);
|
||||
return -1;
|
||||
}
|
||||
de_list = de_list_new;
|
||||
}
|
||||
de = (dirent*) malloc(sizeof(dirent));
|
||||
*de = *this_de;
|
||||
de_list[n_elem++] = de;
|
||||
}
|
||||
closedir(d);
|
||||
if (n_elem && compar) {
|
||||
qsort(de_list, n_elem, sizeof(dirent*), (int (*)(const void*, const void*)) compar);
|
||||
}
|
||||
*namelist = de_list;
|
||||
return n_elem;
|
||||
}
|
||||
|
||||
int alphasort(const struct dirent** a, const struct dirent** b) {
|
||||
int alphasort(const dirent** a, const dirent** b) {
|
||||
return strcoll((*a)->d_name, (*b)->d_name);
|
||||
}
|
||||
|
|
115
libc/bionic/scandir.cpp
Normal file
115
libc/bionic/scandir.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "private/ScopedReaddir.h"
|
||||
|
||||
// A smart pointer to the scandir dirent**.
|
||||
class ScandirResult {
|
||||
public:
|
||||
ScandirResult() : names_(NULL), size_(0), capacity_(0) {
|
||||
}
|
||||
|
||||
~ScandirResult() {
|
||||
while (size_ > 0) {
|
||||
free(names_[--size_]);
|
||||
}
|
||||
free(names_);
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
return size_;
|
||||
}
|
||||
|
||||
dirent** release() {
|
||||
dirent** result = names_;
|
||||
names_ = NULL;
|
||||
size_ = capacity_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Add(dirent* entry) {
|
||||
if (size_ >= capacity_) {
|
||||
size_t new_capacity = capacity_ + 32;
|
||||
dirent** new_names = (dirent**) realloc(names_, new_capacity * sizeof(dirent*));
|
||||
if (new_names == NULL) {
|
||||
return false;
|
||||
}
|
||||
names_ = new_names;
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
dirent* copy = CopyDirent(entry);
|
||||
if (copy == NULL) {
|
||||
return false;
|
||||
}
|
||||
names_[size_++] = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sort(int (*comparator)(const dirent**, const dirent**)) {
|
||||
// If we have entries and a comparator, sort them.
|
||||
if (size_ > 0 && comparator != NULL) {
|
||||
qsort(names_, size_, sizeof(dirent*), (int (*)(const void*, const void*)) comparator);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
dirent** names_;
|
||||
size_t size_;
|
||||
size_t capacity_;
|
||||
|
||||
static dirent* CopyDirent(dirent* original) {
|
||||
// Allocate the minimum number of bytes necessary, rounded up to a 4-byte boundary.
|
||||
size_t size = ((original->d_reclen + 3) & ~3);
|
||||
dirent* copy = (dirent*) malloc(size);
|
||||
memcpy(copy, original, original->d_reclen);
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Disallow copy and assignment.
|
||||
ScandirResult(const ScandirResult&);
|
||||
void operator=(const ScandirResult&);
|
||||
};
|
||||
|
||||
int scandir(const char* dirname, dirent*** name_list,
|
||||
int (*filter)(const dirent*),
|
||||
int (*comparator)(const dirent**, const dirent**)) {
|
||||
ScopedReaddir reader(dirname);
|
||||
if (reader.IsBad()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ScandirResult names;
|
||||
dirent* entry;
|
||||
while ((entry = reader.ReadEntry()) != NULL) {
|
||||
// If we have a filter, skip names that don't match.
|
||||
if (filter != NULL && !(*filter)(entry)) {
|
||||
continue;
|
||||
}
|
||||
names.Add(entry);
|
||||
}
|
||||
|
||||
names.Sort(comparator);
|
||||
|
||||
size_t size = names.size();
|
||||
*name_list = names.release();
|
||||
return size;
|
||||
}
|
|
@ -39,6 +39,8 @@
|
|||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "private/ScopedReaddir.h"
|
||||
|
||||
/* seems to be the default on Linux, per the GLibc sources and my own digging */
|
||||
|
||||
#define SYSTEM_CLK_TCK 100
|
||||
|
@ -78,19 +80,18 @@ static bool __matches_cpuN(const char* s) {
|
|||
static int __sysconf_nprocessors_conf() {
|
||||
// On x86 kernels you can use /proc/cpuinfo for this, but on ARM kernels offline CPUs disappear
|
||||
// from there. This method works on both.
|
||||
DIR* d = opendir("/sys/devices/system/cpu");
|
||||
if (!d) {
|
||||
ScopedReaddir reader("/sys/devices/system/cpu");
|
||||
if (reader.IsBad()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
struct dirent* e;
|
||||
while ((e = readdir(d)) != NULL) {
|
||||
if (e->d_type == DT_DIR && __matches_cpuN(e->d_name)) {
|
||||
dirent* entry;
|
||||
while ((entry = reader.ReadEntry()) != NULL) {
|
||||
if (entry->d_type == DT_DIR && __matches_cpuN(entry->d_name)) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
50
libc/private/ScopedReaddir.h
Normal file
50
libc/private/ScopedReaddir.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SCOPED_READDIR_H
|
||||
#define SCOPED_READDIR_H
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
class ScopedReaddir {
|
||||
public:
|
||||
ScopedReaddir(const char* path) {
|
||||
dir_ = opendir(path);
|
||||
}
|
||||
|
||||
~ScopedReaddir() {
|
||||
if (dir_ != NULL) {
|
||||
closedir(dir_);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsBad() {
|
||||
return dir_ == NULL;
|
||||
}
|
||||
|
||||
dirent* ReadEntry() {
|
||||
return readdir(dir_);
|
||||
}
|
||||
|
||||
private:
|
||||
DIR* dir_;
|
||||
|
||||
// Disallow copy and assignment.
|
||||
ScopedReaddir(const ScopedReaddir&);
|
||||
void operator=(const ScopedReaddir&);
|
||||
};
|
||||
|
||||
#endif // SCOPED_READDIR_H
|
Loading…
Reference in a new issue