Home | History | Annotate | Download | only in unistd
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 #include <unistd.h>
     29 #include <dirent.h>
     30 #include <memory.h>
     31 #include <string.h>
     32 #include <fcntl.h>
     33 #include <stdlib.h>
     34 #include <pthread.h>
     35 #include <errno.h>
     36 
     37 struct DIR
     38 {
     39     int              _DIR_fd;
     40     size_t           _DIR_avail;
     41     struct dirent*   _DIR_next;
     42     pthread_mutex_t  _DIR_lock;
     43     struct dirent    _DIR_buff[15];
     44 };
     45 
     46 int dirfd(DIR* dirp)
     47 {
     48     return dirp->_DIR_fd;
     49 }
     50 
     51 DIR*  opendir( const char*  dirpath )
     52 {
     53     DIR*  dir = malloc(sizeof(DIR));
     54 
     55     if (!dir)
     56         goto Exit;
     57 
     58     dir->_DIR_fd = open(dirpath, O_RDONLY|O_DIRECTORY);
     59     if (dir->_DIR_fd < 0)
     60     {
     61         free(dir);
     62         dir = NULL;
     63     }
     64     else
     65     {
     66         dir->_DIR_avail = 0;
     67         dir->_DIR_next  = NULL;
     68         pthread_mutex_init( &dir->_DIR_lock, NULL );
     69     }
     70 Exit:
     71     return dir;
     72 }
     73 
     74 
     75 DIR*  fdopendir(int fd)
     76 {
     77     DIR*  dir = malloc(sizeof(DIR));
     78 
     79     if (!dir)
     80         return 0;
     81 
     82     dir->_DIR_fd = fd;
     83     dir->_DIR_avail = 0;
     84     dir->_DIR_next  = NULL;
     85     pthread_mutex_init( &dir->_DIR_lock, NULL );
     86 
     87     return dir;
     88 }
     89 
     90 
     91 static struct dirent*
     92 _readdir_unlocked(DIR*  dir)
     93 {
     94     struct dirent*  entry;
     95 #ifndef NDEBUG
     96     unsigned reclen;
     97 #endif
     98 
     99     if ( !dir->_DIR_avail )
    100     {
    101         int  rc;
    102 
    103         for (;;) {
    104             rc = getdents( dir->_DIR_fd, dir->_DIR_buff, sizeof(dir->_DIR_buff));
    105             if (rc >= 0 || errno != EINTR)
    106             break;
    107         }
    108         if (rc <= 0)
    109             return NULL;
    110 
    111         dir->_DIR_avail = rc;
    112         dir->_DIR_next  = dir->_DIR_buff;
    113     }
    114 
    115     entry = dir->_DIR_next;
    116 
    117     /* perform some sanity checks here */
    118     if (((long)(void*)entry & 3) != 0)
    119         return NULL;
    120 
    121 #ifndef NDEBUG
    122     // paranoid testing of the interface with the kernel getdents64 system call
    123     reclen = offsetof(struct dirent, d_name) + strlen(entry->d_name) + 1;
    124     if ( reclen > sizeof(*entry) || reclen <= offsetof(struct dirent, d_name) )
    125         goto Bad;
    126 
    127     if ( (char*)entry + reclen > (char*)dir->_DIR_buff + sizeof(dir->_DIR_buff) )
    128         goto Bad;
    129 
    130     if ( !memchr( entry->d_name, 0, reclen - offsetof(struct dirent, d_name)) )
    131         goto Bad;
    132 #endif
    133 
    134     dir->_DIR_next   = (struct dirent*)((char*)entry + entry->d_reclen);
    135     dir->_DIR_avail -= entry->d_reclen;
    136 
    137     return entry;
    138 
    139   Bad:
    140     errno = EINVAL;
    141     return NULL;
    142 }
    143 
    144 
    145 struct dirent*
    146 readdir(DIR * dir)
    147 {
    148     struct dirent *entry = NULL;
    149 
    150     pthread_mutex_lock( &dir->_DIR_lock );
    151     entry = _readdir_unlocked(dir);
    152     pthread_mutex_unlock( &dir->_DIR_lock );
    153 
    154     return entry;
    155 }
    156 
    157 
    158 int readdir_r(DIR*  dir, struct dirent *entry, struct dirent **result)
    159 {
    160     struct dirent*  ent;
    161     int  save_errno = errno;
    162     int  retval;
    163 
    164     *result = NULL;
    165     errno   = 0;
    166 
    167     pthread_mutex_lock( &dir->_DIR_lock );
    168 
    169     ent    = _readdir_unlocked(dir);
    170     retval = errno;
    171     if (ent == NULL) {
    172         if (!retval) {
    173             errno = save_errno;
    174         }
    175     } else {
    176         if (!retval) {
    177             errno   = save_errno;
    178             *result = entry;
    179             memcpy( entry, ent, ent->d_reclen );
    180         }
    181     }
    182 
    183     pthread_mutex_unlock( &dir->_DIR_lock );
    184 
    185     return retval;
    186 }
    187 
    188 
    189 
    190 int closedir(DIR *dir)
    191 {
    192   int rc;
    193 
    194   rc = close(dir->_DIR_fd);
    195   dir->_DIR_fd = -1;
    196 
    197   pthread_mutex_destroy( &dir->_DIR_lock );
    198 
    199   free(dir);
    200   return rc;
    201 }
    202 
    203 
    204 void   rewinddir(DIR *dir)
    205 {
    206     pthread_mutex_lock( &dir->_DIR_lock );
    207     lseek( dir->_DIR_fd, 0, SEEK_SET );
    208     dir->_DIR_avail = 0;
    209     pthread_mutex_unlock( &dir->_DIR_lock );
    210 }
    211 
    212 
    213 int alphasort(const void *a, const void *b)
    214 {
    215         struct dirent **d1, **d2;
    216 
    217         d1 = (struct dirent **) a;
    218         d2 = (struct dirent **) b;
    219         return strcmp((*d1)->d_name, (*d2)->d_name);
    220 }
    221 
    222 
    223 int scandir(const char *dir, struct dirent ***namelist,
    224             int(*filter)(const struct dirent *),
    225             int(*compar)(const struct dirent **, const struct dirent **))
    226 {
    227     DIR *d;
    228     int n_elem = 0;
    229     struct dirent *this_de, *de;
    230     struct dirent **de_list = NULL;
    231     int de_list_size = 0;
    232 
    233     d = opendir(dir);
    234     if (d == NULL) {
    235         return -1;
    236     }
    237 
    238     while ((this_de = readdir(d)) != NULL) {
    239         if (filter && (*filter)(this_de) == 0) {
    240             continue;
    241         }
    242         if (n_elem == 0) {
    243             de_list_size = 4;
    244             de_list = (struct dirent **)
    245                     malloc(sizeof(struct dirent *)*de_list_size);
    246             if (de_list == NULL) {
    247                 return -1;
    248             }
    249         }
    250         else if (n_elem == de_list_size) {
    251             struct dirent **de_list_new;
    252 
    253             de_list_size += 10;
    254             de_list_new = (struct dirent **)
    255                     realloc(de_list, sizeof(struct dirent *)*de_list_size);
    256             if (de_list_new == NULL) {
    257                 free(de_list);
    258                 return -1;
    259             }
    260             de_list = de_list_new;
    261         }
    262         de = (struct dirent *) malloc(sizeof(struct dirent));
    263         *de = *this_de;
    264         de_list[n_elem++] = de;
    265     }
    266     closedir(d);
    267     if (n_elem && compar) {
    268         qsort(de_list, n_elem, sizeof(struct dirent *),
    269               (int (*)(const void *, const void *)) compar);
    270     }
    271     *namelist = de_list;
    272     return n_elem;
    273 }
    274