1 /*
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
19 #include <assert.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "platform/bionic/macros.h"
27 #include "private/ScopedReaddir.h"
28
29 // A smart pointer to the scandir dirent**.
30 class ScandirResult {
31 public:
ScandirResult()32 ScandirResult() : names_(nullptr), size_(0), capacity_(0) {
33 }
34
~ScandirResult()35 ~ScandirResult() {
36 // We always call release(), so this can't happen.
37 if (names_ != nullptr) __assert(__FILE__, __LINE__, "missing call to release()");
38 }
39
size()40 size_t size() {
41 return size_;
42 }
43
release()44 dirent** release() {
45 dirent** result = names_;
46 names_ = nullptr;
47 size_ = capacity_ = 0;
48 return result;
49 }
50
Add(dirent * entry)51 bool Add(dirent* entry) {
52 if (size_ >= capacity_) {
53 size_t new_capacity = capacity_ + 32;
54 dirent** new_names =
55 reinterpret_cast<dirent**>(realloc(names_, new_capacity * sizeof(dirent*)));
56 if (new_names == nullptr) {
57 return false;
58 }
59 names_ = new_names;
60 capacity_ = new_capacity;
61 }
62
63 dirent* copy = CopyDirent(entry);
64 if (copy == nullptr) {
65 return false;
66 }
67 names_[size_++] = copy;
68 return true;
69 }
70
Sort(int (* comparator)(const dirent **,const dirent **))71 void Sort(int (*comparator)(const dirent**, const dirent**)) {
72 qsort(names_, size_, sizeof(dirent*),
73 reinterpret_cast<int (*)(const void*, const void*)>(comparator));
74 }
75
76 private:
77 dirent** names_;
78 size_t size_;
79 size_t capacity_;
80
CopyDirent(dirent * original)81 static dirent* CopyDirent(dirent* original) {
82 // Allocate the minimum number of bytes necessary, rounded up to a 4-byte boundary.
83 size_t size = ((original->d_reclen + 3) & ~3);
84 dirent* copy = reinterpret_cast<dirent*>(malloc(size));
85 memcpy(copy, original, original->d_reclen);
86 return copy;
87 }
88
89 BIONIC_DISALLOW_COPY_AND_ASSIGN(ScandirResult);
90 };
91
scandirat(int parent_fd,const char * dir_name,dirent *** name_list,int (* filter)(const dirent *),int (* comparator)(const dirent **,const dirent **))92 int scandirat(int parent_fd, const char* dir_name, dirent*** name_list,
93 int (*filter)(const dirent*),
94 int (*comparator)(const dirent**, const dirent**)) {
95 DIR* dir = nullptr;
96 if (parent_fd == AT_FDCWD) {
97 dir = opendir(dir_name);
98 } else {
99 int dir_fd = openat(parent_fd, dir_name, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
100 if (dir_fd != -1) {
101 dir = fdopendir(dir_fd);
102 }
103 }
104
105 ScopedReaddir reader(dir);
106 if (reader.IsBad()) {
107 return -1;
108 }
109
110 ScandirResult names;
111 dirent* entry;
112 while ((entry = reader.ReadEntry()) != nullptr) {
113 // If we have a filter, skip names that don't match.
114 if (filter != nullptr && !(*filter)(entry)) {
115 continue;
116 }
117 names.Add(entry);
118 }
119
120 if (comparator != nullptr) {
121 names.Sort(comparator);
122 }
123
124 size_t size = names.size();
125 *name_list = names.release();
126 return size;
127 }
128 __strong_alias(scandirat64, scandirat);
129
scandir(const char * dir_path,dirent *** name_list,int (* filter)(const dirent *),int (* comparator)(const dirent **,const dirent **))130 int scandir(const char* dir_path, dirent*** name_list,
131 int (*filter)(const dirent*),
132 int (*comparator)(const dirent**, const dirent**)) {
133 return scandirat(AT_FDCWD, dir_path, name_list, filter, comparator);
134 }
135 __strong_alias(scandir64, scandir);
136