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