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