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
diff --git a/libc/bionic/scandir.cpp b/libc/bionic/scandir.cpp
new file mode 100644
index 0000000..dd22b22
--- /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 <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;
+}