diff --git a/libc/Android.mk b/libc/Android.mk index 5a1356a62..72192117f 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -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 \ diff --git a/libc/bionic/dirent.cpp b/libc/bionic/dirent.cpp index 74297d821..056569891 100644 --- a/libc/bionic/dirent.cpp +++ b/libc/bionic/dirent.cpp @@ -27,11 +27,9 @@ */ #include + #include #include -#include -#include -#include #include #include #include @@ -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); } diff --git a/libc/bionic/scandir.cpp b/libc/bionic/scandir.cpp new file mode 100644 index 000000000..dd22b2254 --- /dev/null +++ b/libc/bionic/scandir.cpp @@ -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 + +#include +#include + +#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; +} diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp index 869faef77..5e39e6156 100644 --- a/libc/bionic/sysconf.cpp +++ b/libc/bionic/sysconf.cpp @@ -39,6 +39,8 @@ #include #include +#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; } diff --git a/libc/private/ScopedReaddir.h b/libc/private/ScopedReaddir.h new file mode 100644 index 000000000..797809a28 --- /dev/null +++ b/libc/private/ScopedReaddir.h @@ -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 + +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