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 
     96     if ( !dir->_DIR_avail )
     97     {
     98         int  rc;
     99 
    100         for (;;) {
    101             rc = getdents( dir->_DIR_fd, dir->_DIR_buff, sizeof(dir->_DIR_buff));
    102             if (rc >= 0 || errno != EINTR)
    103             break;
    104         }
    105         if (rc <= 0)
    106             return NULL;
    107 
    108         dir->_DIR_avail = rc;
    109         dir->_DIR_next  = dir->_DIR_buff;
    110     }
    111 
    112     entry = dir->_DIR_next;
    113 
    114     /* perform some sanity checks here */
    115     if (((long)(void*)entry & 3) != 0)
    116         return NULL;
    117 
    118     if ( (unsigned)entry->d_reclen > sizeof(*entry)         ||
    119          entry->d_reclen <= offsetof(struct dirent, d_name) )
    120         goto Bad;
    121 
    122     if ( (char*)entry + entry->d_reclen > (char*)dir->_DIR_buff + sizeof(dir->_DIR_buff) )
    123         goto Bad;
    124 
    125     if ( !memchr( entry->d_name, 0, entry->d_reclen - offsetof(struct dirent, d_name)) )
    126         goto Bad;
    127 
    128     dir->_DIR_next   = (struct dirent*)((char*)entry + entry->d_reclen);
    129     dir->_DIR_avail -= entry->d_reclen;
    130 
    131     return entry;
    132 
    133   Bad:
    134     errno = EINVAL;
    135     return NULL;
    136 }
    137 
    138 
    139 struct dirent*
    140 readdir(DIR * dir)
    141 {
    142     struct dirent *entry = NULL;
    143 
    144     pthread_mutex_lock( &dir->_DIR_lock );
    145     entry = _readdir_unlocked(dir);
    146     pthread_mutex_unlock( &dir->_DIR_lock );
    147 
    148     return entry;
    149 }
    150 
    151 
    152 int readdir_r(DIR*  dir, struct dirent *entry, struct dirent **result)
    153 {
    154     struct dirent*  ent;
    155     int  save_errno = errno;
    156     int  retval;
    157 
    158     *result = NULL;
    159     errno   = 0;
    160 
    161     pthread_mutex_lock( &dir->_DIR_lock );
    162 
    163     ent    = _readdir_unlocked(dir);
    164     retval = errno;
    165     if (ent == NULL) {
    166         if (!retval) {
    167             errno = save_errno;
    168         }
    169     } else {
    170         if (!retval) {
    171             errno   = save_errno;
    172             *result = entry;
    173             memcpy( entry, ent, ent->d_reclen );
    174         }
    175     }
    176 
    177     pthread_mutex_unlock( &dir->_DIR_lock );
    178 
    179     return retval;
    180 }
    181 
    182 
    183 
    184 int closedir(DIR *dir)
    185 {
    186   int rc;
    187 
    188   rc = close(dir->_DIR_fd);
    189   dir->_DIR_fd = -1;
    190 
    191   pthread_mutex_destroy( &dir->_DIR_lock );
    192 
    193   free(dir);
    194   return rc;
    195 }
    196 
    197 
    198 void   rewinddir(DIR *dir)
    199 {
    200     pthread_mutex_lock( &dir->_DIR_lock );
    201     lseek( dir->_DIR_fd, 0, SEEK_SET );
    202     dir->_DIR_avail = 0;
    203     pthread_mutex_unlock( &dir->_DIR_lock );
    204 }
    205 
    206 
    207 int alphasort(const void *a, const void *b)
    208 {
    209         struct dirent **d1, **d2;
    210 
    211         d1 = (struct dirent **) a;
    212         d2 = (struct dirent **) b;
    213         return strcmp((*d1)->d_name, (*d2)->d_name);
    214 }
    215 
    216 
    217 int scandir(const char *dir, struct dirent ***namelist,
    218             int(*filter)(const struct dirent *),
    219             int(*compar)(const struct dirent **, const struct dirent **))
    220 {
    221     DIR *d;
    222     int n_elem = 0;
    223     struct dirent *this_de, *de;
    224     struct dirent **de_list = NULL;
    225     int de_list_size = 0;
    226 
    227     d = opendir(dir);
    228     if (d == NULL) {
    229         return -1;
    230     }
    231 
    232     while ((this_de = readdir(d)) != NULL) {
    233         if (filter && (*filter)(this_de) == 0) {
    234             continue;
    235         }
    236         if (n_elem == 0) {
    237             de_list_size = 4;
    238             de_list = (struct dirent **)
    239                     malloc(sizeof(struct dirent *)*de_list_size);
    240             if (de_list == NULL) {
    241                 return -1;
    242             }
    243         }
    244         else if (n_elem == de_list_size) {
    245             struct dirent **de_list_new;
    246 
    247             de_list_size += 10;
    248             de_list_new = (struct dirent **)
    249                     realloc(de_list, sizeof(struct dirent *)*de_list_size);
    250             if (de_list_new == NULL) {
    251                 free(de_list);
    252                 return -1;
    253             }
    254             de_list = de_list_new;
    255         }
    256         de = (struct dirent *) malloc(sizeof(struct dirent));
    257         *de = *this_de;
    258         de_list[n_elem++] = de;
    259     }
    260     closedir(d);
    261     if (n_elem && compar) {
    262         qsort(de_list, n_elem, sizeof(struct dirent *),
    263               (int (*)(const void *, const void *)) compar);
    264     }
    265     *namelist = de_list;
    266     return n_elem;
    267 }
    268