blob: f5282863405dec733dd9d226c8af87dfd035ffbc [file] [log] [blame]
Elliott Hughes701bec22013-02-25 13:14:31 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <dirent.h>
18
Elliott Hughes13a76102021-03-16 16:19:16 -070019#include <assert.h>
Elliott Hughes701bec22013-02-25 13:14:31 -080020#include <errno.h>
Elliott Hughes13a76102021-03-16 16:19:16 -070021#include <fcntl.h>
Elliott Hughes701bec22013-02-25 13:14:31 -080022#include <stdlib.h>
Elliott Hughes76f89162015-01-26 13:34:58 -080023#include <string.h>
Elliott Hughes6331e802015-10-27 11:10:36 -070024#include <unistd.h>
Elliott Hughes701bec22013-02-25 13:14:31 -080025
Josh Gao4956c372019-12-19 16:35:51 -080026#include "platform/bionic/macros.h"
Elliott Hughes701bec22013-02-25 13:14:31 -080027#include "private/ScopedReaddir.h"
28
29// A smart pointer to the scandir dirent**.
30class ScandirResult {
31 public:
Elliott Hughes6331e802015-10-27 11:10:36 -070032 ScandirResult() : names_(nullptr), size_(0), capacity_(0) {
Elliott Hughes701bec22013-02-25 13:14:31 -080033 }
34
35 ~ScandirResult() {
Elliott Hughes13a76102021-03-16 16:19:16 -070036 // We always call release(), so this can't happen.
37 if (names_ != nullptr) __assert(__FILE__, __LINE__, "missing call to release()");
Elliott Hughes701bec22013-02-25 13:14:31 -080038 }
39
40 size_t size() {
41 return size_;
42 }
43
44 dirent** release() {
45 dirent** result = names_;
Elliott Hughes6331e802015-10-27 11:10:36 -070046 names_ = nullptr;
Elliott Hughes701bec22013-02-25 13:14:31 -080047 size_ = capacity_ = 0;
48 return result;
49 }
50
51 bool Add(dirent* entry) {
52 if (size_ >= capacity_) {
53 size_t new_capacity = capacity_ + 32;
Elliott Hughes8b5df392015-01-21 16:19:07 -080054 dirent** new_names =
55 reinterpret_cast<dirent**>(realloc(names_, new_capacity * sizeof(dirent*)));
Elliott Hughes6331e802015-10-27 11:10:36 -070056 if (new_names == nullptr) {
Elliott Hughes701bec22013-02-25 13:14:31 -080057 return false;
58 }
59 names_ = new_names;
60 capacity_ = new_capacity;
61 }
62
63 dirent* copy = CopyDirent(entry);
Elliott Hughes6331e802015-10-27 11:10:36 -070064 if (copy == nullptr) {
Elliott Hughes701bec22013-02-25 13:14:31 -080065 return false;
66 }
67 names_[size_++] = copy;
68 return true;
69 }
70
71 void Sort(int (*comparator)(const dirent**, const dirent**)) {
72 // If we have entries and a comparator, sort them.
Elliott Hughes6331e802015-10-27 11:10:36 -070073 if (size_ > 0 && comparator != nullptr) {
Elliott Hughes8b5df392015-01-21 16:19:07 -080074 qsort(names_, size_, sizeof(dirent*),
75 reinterpret_cast<int (*)(const void*, const void*)>(comparator));
Elliott Hughes701bec22013-02-25 13:14:31 -080076 }
77 }
78
79 private:
80 dirent** names_;
81 size_t size_;
82 size_t capacity_;
83
84 static dirent* CopyDirent(dirent* original) {
85 // Allocate the minimum number of bytes necessary, rounded up to a 4-byte boundary.
86 size_t size = ((original->d_reclen + 3) & ~3);
Elliott Hughes8b5df392015-01-21 16:19:07 -080087 dirent* copy = reinterpret_cast<dirent*>(malloc(size));
Elliott Hughes701bec22013-02-25 13:14:31 -080088 memcpy(copy, original, original->d_reclen);
89 return copy;
90 }
91
Elliott Hughes5e62b342018-10-25 11:00:00 -070092 BIONIC_DISALLOW_COPY_AND_ASSIGN(ScandirResult);
Elliott Hughes701bec22013-02-25 13:14:31 -080093};
94
Elliott Hughes6331e802015-10-27 11:10:36 -070095int scandirat(int parent_fd, const char* dir_name, dirent*** name_list,
96 int (*filter)(const dirent*),
97 int (*comparator)(const dirent**, const dirent**)) {
98 DIR* dir = nullptr;
99 if (parent_fd == AT_FDCWD) {
100 dir = opendir(dir_name);
101 } else {
102 int dir_fd = openat(parent_fd, dir_name, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
103 if (dir_fd != -1) {
104 dir = fdopendir(dir_fd);
105 }
106 }
107
108 ScopedReaddir reader(dir);
Elliott Hughes701bec22013-02-25 13:14:31 -0800109 if (reader.IsBad()) {
110 return -1;
111 }
112
113 ScandirResult names;
114 dirent* entry;
Elliott Hughes6331e802015-10-27 11:10:36 -0700115 while ((entry = reader.ReadEntry()) != nullptr) {
Elliott Hughes701bec22013-02-25 13:14:31 -0800116 // If we have a filter, skip names that don't match.
Elliott Hughes6331e802015-10-27 11:10:36 -0700117 if (filter != nullptr && !(*filter)(entry)) {
Elliott Hughes701bec22013-02-25 13:14:31 -0800118 continue;
119 }
120 names.Add(entry);
121 }
122
123 names.Sort(comparator);
124
125 size_t size = names.size();
126 *name_list = names.release();
127 return size;
128}
Elliott Hughes6331e802015-10-27 11:10:36 -0700129__strong_alias(scandirat64, scandirat);
130
131int scandir(const char* dir_path, dirent*** name_list,
132 int (*filter)(const dirent*),
133 int (*comparator)(const dirent**, const dirent**)) {
134 return scandirat(AT_FDCWD, dir_path, name_list, filter, comparator);
135}
Elliott Hughesdb1ea342014-01-17 18:42:49 -0800136__strong_alias(scandir64, scandir);