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     // If we have entries and a comparator, sort them.
73     if (size_ > 0 && comparator != nullptr) {
74       qsort(names_, size_, sizeof(dirent*),
75             reinterpret_cast<int (*)(const void*, const void*)>(comparator));
76     }
77   }
78 
79  private:
80   dirent** names_;
81   size_t size_;
82   size_t capacity_;
83 
CopyDirent(dirent * original)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);
87     dirent* copy = reinterpret_cast<dirent*>(malloc(size));
88     memcpy(copy, original, original->d_reclen);
89     return copy;
90   }
91 
92   BIONIC_DISALLOW_COPY_AND_ASSIGN(ScandirResult);
93 };
94 
scandirat(int parent_fd,const char * dir_name,dirent *** name_list,int (* filter)(const dirent *),int (* comparator)(const dirent **,const dirent **))95 int 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);
109   if (reader.IsBad()) {
110     return -1;
111   }
112 
113   ScandirResult names;
114   dirent* entry;
115   while ((entry = reader.ReadEntry()) != nullptr) {
116     // If we have a filter, skip names that don't match.
117     if (filter != nullptr && !(*filter)(entry)) {
118       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 }
129 __strong_alias(scandirat64, scandirat);
130 
scandir(const char * dir_path,dirent *** name_list,int (* filter)(const dirent *),int (* comparator)(const dirent **,const dirent **))131 int 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 }
136 __strong_alias(scandir64, scandir);
137