Home | History | Annotate | Download | only in bionic
      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**.
     29 class 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 
     96 int 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 
    132 int 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