Home | History | Annotate | Download | only in libmedia
      1 /*
      2  * Copyright (C) 2009 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 <media/mediascanner.h>
     18 
     19 #include <sys/stat.h>
     20 #include <dirent.h>
     21 
     22 namespace android {
     23 
     24 MediaScanner::MediaScanner()
     25     : mLocale(NULL) {
     26 }
     27 
     28 MediaScanner::~MediaScanner() {
     29     setLocale(NULL);
     30 }
     31 
     32 void MediaScanner::setLocale(const char *locale) {
     33     if (mLocale) {
     34         free(mLocale);
     35         mLocale = NULL;
     36     }
     37     if (locale) {
     38         mLocale = strdup(locale);
     39     }
     40 }
     41 
     42 const char *MediaScanner::locale() const {
     43     return mLocale;
     44 }
     45 
     46 status_t MediaScanner::processDirectory(
     47         const char *path, const char *extensions,
     48         MediaScannerClient &client,
     49         ExceptionCheck exceptionCheck, void *exceptionEnv) {
     50     int pathLength = strlen(path);
     51     if (pathLength >= PATH_MAX) {
     52         return UNKNOWN_ERROR;
     53     }
     54     char* pathBuffer = (char *)malloc(PATH_MAX + 1);
     55     if (!pathBuffer) {
     56         return UNKNOWN_ERROR;
     57     }
     58 
     59     int pathRemaining = PATH_MAX - pathLength;
     60     strcpy(pathBuffer, path);
     61     if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') {
     62         pathBuffer[pathLength] = '/';
     63         pathBuffer[pathLength + 1] = 0;
     64         --pathRemaining;
     65     }
     66 
     67     client.setLocale(locale());
     68 
     69     status_t result =
     70         doProcessDirectory(
     71                 pathBuffer, pathRemaining, extensions, client,
     72                 exceptionCheck, exceptionEnv);
     73 
     74     free(pathBuffer);
     75 
     76     return result;
     77 }
     78 
     79 static bool fileMatchesExtension(const char* path, const char* extensions) {
     80     char* extension = strrchr(path, '.');
     81     if (!extension) return false;
     82     ++extension;    // skip the dot
     83     if (extension[0] == 0) return false;
     84 
     85     while (extensions[0]) {
     86         char* comma = strchr(extensions, ',');
     87         size_t length = (comma ? comma - extensions : strlen(extensions));
     88         if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
     89         extensions += length;
     90         if (extensions[0] == ',') ++extensions;
     91     }
     92 
     93     return false;
     94 }
     95 
     96 status_t MediaScanner::doProcessDirectory(
     97         char *path, int pathRemaining, const char *extensions,
     98         MediaScannerClient &client, ExceptionCheck exceptionCheck,
     99         void *exceptionEnv) {
    100     // place to copy file or directory name
    101     char* fileSpot = path + strlen(path);
    102     struct dirent* entry;
    103 
    104     // ignore directories that contain a  ".nomedia" file
    105     if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
    106         strcpy(fileSpot, ".nomedia");
    107         if (access(path, F_OK) == 0) {
    108             LOGD("found .nomedia, skipping directory\n");
    109             fileSpot[0] = 0;
    110             client.addNoMediaFolder(path);
    111             return OK;
    112         }
    113 
    114         // restore path
    115         fileSpot[0] = 0;
    116     }
    117 
    118     DIR* dir = opendir(path);
    119     if (!dir) {
    120         LOGD("opendir %s failed, errno: %d", path, errno);
    121         return UNKNOWN_ERROR;
    122     }
    123 
    124     while ((entry = readdir(dir))) {
    125         const char* name = entry->d_name;
    126 
    127         // ignore "." and ".."
    128         if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
    129             continue;
    130         }
    131 
    132         int type = entry->d_type;
    133         if (type == DT_UNKNOWN) {
    134             // If the type is unknown, stat() the file instead.
    135             // This is sometimes necessary when accessing NFS mounted filesystems, but
    136             // could be needed in other cases well.
    137             struct stat statbuf;
    138             if (stat(path, &statbuf) == 0) {
    139                 if (S_ISREG(statbuf.st_mode)) {
    140                     type = DT_REG;
    141                 } else if (S_ISDIR(statbuf.st_mode)) {
    142                     type = DT_DIR;
    143                 }
    144             } else {
    145                 LOGD("stat() failed for %s: %s", path, strerror(errno) );
    146             }
    147         }
    148         if (type == DT_REG || type == DT_DIR) {
    149             int nameLength = strlen(name);
    150             bool isDirectory = (type == DT_DIR);
    151 
    152             if (nameLength > pathRemaining || (isDirectory && nameLength + 1 > pathRemaining)) {
    153                 // path too long!
    154                 continue;
    155             }
    156 
    157             strcpy(fileSpot, name);
    158             if (isDirectory) {
    159                 // ignore directories with a name that starts with '.'
    160                 // for example, the Mac ".Trashes" directory
    161                 if (name[0] == '.') continue;
    162 
    163                 strcat(fileSpot, "/");
    164                 int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv);
    165                 if (err) {
    166                     // pass exceptions up - ignore other errors
    167                     if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
    168                     LOGE("Error processing '%s' - skipping\n", path);
    169                     continue;
    170                 }
    171             } else if (fileMatchesExtension(path, extensions)) {
    172                 struct stat statbuf;
    173                 stat(path, &statbuf);
    174                 if (statbuf.st_size > 0) {
    175                     client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
    176                 }
    177                 if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
    178             }
    179         }
    180     }
    181 
    182     closedir(dir);
    183     return OK;
    184 failure:
    185     closedir(dir);
    186     return -1;
    187 }
    188 
    189 }  // namespace android
    190