• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MediaScanner()28 MediaScanner::MediaScanner()
29     : mLocale(NULL) {
30 }
31 
~MediaScanner()32 MediaScanner::~MediaScanner() {
33     setLocale(NULL);
34 }
35 
setLocale(const char * locale)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 
locale() const46 const char *MediaScanner::locale() const {
47     return mLocale;
48 }
49 
processDirectory(const char * path,const char * extensions,MediaScannerClient & client,ExceptionCheck exceptionCheck,void * exceptionEnv)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 
fileMatchesExtension(const char * path,const char * extensions)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 
doProcessDirectory(char * path,int pathRemaining,const char * extensions,MediaScannerClient & client,ExceptionCheck exceptionCheck,void * exceptionEnv)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