/* * 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 #include #include #include #include "platform/bionic/macros.h" #include "private/ScopedReaddir.h" // A smart pointer to the scandir dirent**. class ScandirResult { public: ScandirResult() : names_(nullptr), size_(0), capacity_(0) { } ~ScandirResult() { // We always call release(), so this can't happen. if (names_ != nullptr) __assert(__FILE__, __LINE__, "missing call to release()"); } size_t size() { return size_; } dirent** release() { dirent** result = names_; names_ = nullptr; size_ = capacity_ = 0; return result; } bool Add(dirent* entry) { if (size_ >= capacity_) { size_t new_capacity = capacity_ + 32; dirent** new_names = reinterpret_cast(realloc(names_, new_capacity * sizeof(dirent*))); if (new_names == nullptr) { return false; } names_ = new_names; capacity_ = new_capacity; } dirent* copy = CopyDirent(entry); if (copy == nullptr) { return false; } names_[size_++] = copy; return true; } void Sort(int (*comparator)(const dirent**, const dirent**)) { qsort(names_, size_, sizeof(dirent*), reinterpret_cast(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 = reinterpret_cast(malloc(size)); memcpy(copy, original, original->d_reclen); return copy; } BIONIC_DISALLOW_COPY_AND_ASSIGN(ScandirResult); }; int scandirat(int parent_fd, const char* dir_name, dirent*** name_list, int (*filter)(const dirent*), int (*comparator)(const dirent**, const dirent**)) { DIR* dir = nullptr; if (parent_fd == AT_FDCWD) { dir = opendir(dir_name); } else { int dir_fd = openat(parent_fd, dir_name, O_CLOEXEC | O_DIRECTORY | O_RDONLY); if (dir_fd != -1) { dir = fdopendir(dir_fd); } } ScopedReaddir reader(dir); if (reader.IsBad()) { return -1; } ScandirResult names; dirent* entry; while ((entry = reader.ReadEntry()) != nullptr) { // If we have a filter, skip names that don't match. if (filter != nullptr && !(*filter)(entry)) { continue; } names.Add(entry); } if (comparator != nullptr) { names.Sort(comparator); } size_t size = names.size(); *name_list = names.release(); return size; } __strong_alias(scandirat64, scandirat); int scandir(const char* dir_path, dirent*** name_list, int (*filter)(const dirent*), int (*comparator)(const dirent**, const dirent**)) { return scandirat(AT_FDCWD, dir_path, name_list, filter, comparator); } __strong_alias(scandir64, scandir);