OSDN Git Service

am 2d9ee707: am 364fe3a8: am b6bc020d: doc change: Samples browsing first changes...
[android-x86/frameworks-base.git] / core / jni / com_android_internal_content_NativeLibraryHelper.cpp
1 /*
2  * Copyright (C) 2011 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_TAG "NativeLibraryHelper"
18 //#define LOG_NDEBUG 0
19
20 #include <android_runtime/AndroidRuntime.h>
21
22 #include <utils/Log.h>
23 #include <androidfw/ZipFileRO.h>
24 #include <ScopedUtfChars.h>
25
26 #include <zlib.h>
27
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35
36
37 #define APK_LIB "lib/"
38 #define APK_LIB_LEN (sizeof(APK_LIB) - 1)
39
40 #define LIB_PREFIX "/lib"
41 #define LIB_PREFIX_LEN (sizeof(LIB_PREFIX) - 1)
42
43 #define LIB_SUFFIX ".so"
44 #define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1)
45
46 #define GDBSERVER "gdbserver"
47 #define GDBSERVER_LEN (sizeof(GDBSERVER) - 1)
48
49 #define TMP_FILE_PATTERN "/tmp.XXXXXX"
50 #define TMP_FILE_PATTERN_LEN (sizeof(TMP_FILE_PATTERN) - 1)
51
52 namespace android {
53
54 // These match PackageManager.java install codes
55 typedef enum {
56     INSTALL_SUCCEEDED = 1,
57     INSTALL_FAILED_INVALID_APK = -2,
58     INSTALL_FAILED_INSUFFICIENT_STORAGE = -4,
59     INSTALL_FAILED_CONTAINER_ERROR = -18,
60     INSTALL_FAILED_INTERNAL_ERROR = -110,
61 } install_status_t;
62
63 typedef install_status_t (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*);
64
65 // Equivalent to isFilenameSafe
66 static bool
67 isFilenameSafe(const char* filename)
68 {
69     off_t offset = 0;
70     for (;;) {
71         switch (*(filename + offset)) {
72         case 0:
73             // Null.
74             // If we've reached the end, all the other characters are good.
75             return true;
76
77         case 'A' ... 'Z':
78         case 'a' ... 'z':
79         case '0' ... '9':
80         case '+':
81         case ',':
82         case '-':
83         case '.':
84         case '/':
85         case '=':
86         case '_':
87             offset++;
88             break;
89
90         default:
91             // We found something that is not good.
92             return false;
93         }
94     }
95     // Should not reach here.
96 }
97
98 static bool
99 isFileDifferent(const char* filePath, size_t fileSize, time_t modifiedTime,
100         long zipCrc, struct stat64* st)
101 {
102     if (lstat64(filePath, st) < 0) {
103         // File is not found or cannot be read.
104         ALOGV("Couldn't stat %s, copying: %s\n", filePath, strerror(errno));
105         return true;
106     }
107
108     if (!S_ISREG(st->st_mode)) {
109         return true;
110     }
111
112     if (st->st_size != fileSize) {
113         return true;
114     }
115
116     // For some reason, bionic doesn't define st_mtime as time_t
117     if (time_t(st->st_mtime) != modifiedTime) {
118         ALOGV("mod time doesn't match: %ld vs. %ld\n", st->st_mtime, modifiedTime);
119         return true;
120     }
121
122     int fd = TEMP_FAILURE_RETRY(open(filePath, O_RDONLY));
123     if (fd < 0) {
124         ALOGV("Couldn't open file %s: %s", filePath, strerror(errno));
125         return true;
126     }
127
128     long crc = crc32(0L, Z_NULL, 0);
129     unsigned char crcBuffer[16384];
130     ssize_t numBytes;
131     while ((numBytes = TEMP_FAILURE_RETRY(read(fd, crcBuffer, sizeof(crcBuffer)))) > 0) {
132         crc = crc32(crc, crcBuffer, numBytes);
133     }
134     close(fd);
135
136     ALOGV("%s: crc = %lx, zipCrc = %lx\n", filePath, crc, zipCrc);
137
138     if (crc != zipCrc) {
139         return true;
140     }
141
142     return false;
143 }
144
145 static install_status_t
146 sumFiles(JNIEnv* env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
147 {
148     size_t* total = (size_t*) arg;
149     size_t uncompLen;
150
151     if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, NULL, NULL)) {
152         return INSTALL_FAILED_INVALID_APK;
153     }
154
155     *total += uncompLen;
156
157     return INSTALL_SUCCEEDED;
158 }
159
160 /*
161  * Copy the native library if needed.
162  *
163  * This function assumes the library and path names passed in are considered safe.
164  */
165 static install_status_t
166 copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
167 {
168     jstring* javaNativeLibPath = (jstring*) arg;
169     ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
170
171     size_t uncompLen;
172     long when;
173     long crc;
174     time_t modTime;
175
176     if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, &when, &crc)) {
177         ALOGD("Couldn't read zip entry info\n");
178         return INSTALL_FAILED_INVALID_APK;
179     } else {
180         struct tm t;
181         ZipFileRO::zipTimeToTimespec(when, &t);
182         modTime = mktime(&t);
183     }
184
185     // Build local file path
186     const size_t fileNameLen = strlen(fileName);
187     char localFileName[nativeLibPath.size() + fileNameLen + 2];
188
189     if (strlcpy(localFileName, nativeLibPath.c_str(), sizeof(localFileName)) != nativeLibPath.size()) {
190         ALOGD("Couldn't allocate local file name for library");
191         return INSTALL_FAILED_INTERNAL_ERROR;
192     }
193
194     *(localFileName + nativeLibPath.size()) = '/';
195
196     if (strlcpy(localFileName + nativeLibPath.size() + 1, fileName, sizeof(localFileName)
197                     - nativeLibPath.size() - 1) != fileNameLen) {
198         ALOGD("Couldn't allocate local file name for library");
199         return INSTALL_FAILED_INTERNAL_ERROR;
200     }
201
202     // Only copy out the native file if it's different.
203     struct stat st;
204     if (!isFileDifferent(localFileName, uncompLen, modTime, crc, &st)) {
205         return INSTALL_SUCCEEDED;
206     }
207
208     char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 2];
209     if (strlcpy(localTmpFileName, nativeLibPath.c_str(), sizeof(localTmpFileName))
210             != nativeLibPath.size()) {
211         ALOGD("Couldn't allocate local file name for library");
212         return INSTALL_FAILED_INTERNAL_ERROR;
213     }
214
215     *(localFileName + nativeLibPath.size()) = '/';
216
217     if (strlcpy(localTmpFileName + nativeLibPath.size(), TMP_FILE_PATTERN,
218                     TMP_FILE_PATTERN_LEN - nativeLibPath.size()) != TMP_FILE_PATTERN_LEN) {
219         ALOGI("Couldn't allocate temporary file name for library");
220         return INSTALL_FAILED_INTERNAL_ERROR;
221     }
222
223     int fd = mkstemp(localTmpFileName);
224     if (fd < 0) {
225         ALOGI("Couldn't open temporary file name: %s: %s\n", localTmpFileName, strerror(errno));
226         return INSTALL_FAILED_CONTAINER_ERROR;
227     }
228
229     if (!zipFile->uncompressEntry(zipEntry, fd)) {
230         ALOGI("Failed uncompressing %s to %s\n", fileName, localTmpFileName);
231         close(fd);
232         unlink(localTmpFileName);
233         return INSTALL_FAILED_CONTAINER_ERROR;
234     }
235
236     close(fd);
237
238     // Set the modification time for this file to the ZIP's mod time.
239     struct timeval times[2];
240     times[0].tv_sec = st.st_atime;
241     times[1].tv_sec = modTime;
242     times[0].tv_usec = times[1].tv_usec = 0;
243     if (utimes(localTmpFileName, times) < 0) {
244         ALOGI("Couldn't change modification time on %s: %s\n", localTmpFileName, strerror(errno));
245         unlink(localTmpFileName);
246         return INSTALL_FAILED_CONTAINER_ERROR;
247     }
248
249     // Set the mode to 755
250     static const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |  S_IXGRP | S_IROTH | S_IXOTH;
251     if (chmod(localTmpFileName, mode) < 0) {
252         ALOGI("Couldn't change permissions on %s: %s\n", localTmpFileName, strerror(errno));
253         unlink(localTmpFileName);
254         return INSTALL_FAILED_CONTAINER_ERROR;
255     }
256
257     // Finally, rename it to the final name.
258     if (rename(localTmpFileName, localFileName) < 0) {
259         ALOGI("Couldn't rename %s to %s: %s\n", localTmpFileName, localFileName, strerror(errno));
260         unlink(localTmpFileName);
261         return INSTALL_FAILED_CONTAINER_ERROR;
262     }
263
264     ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName);
265
266     return INSTALL_SUCCEEDED;
267 }
268
269 static install_status_t
270 iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2,
271         iterFunc callFunc, void* callArg) {
272     ScopedUtfChars filePath(env, javaFilePath);
273     ScopedUtfChars cpuAbi(env, javaCpuAbi);
274     ScopedUtfChars cpuAbi2(env, javaCpuAbi2);
275
276     ZipFileRO zipFile;
277
278     if (zipFile.open(filePath.c_str()) != NO_ERROR) {
279         ALOGI("Couldn't open APK %s\n", filePath.c_str());
280         return INSTALL_FAILED_INVALID_APK;
281     }
282
283     const int N = zipFile.getNumEntries();
284
285     char fileName[PATH_MAX];
286     bool hasPrimaryAbi = false;
287
288     for (int i = 0; i < N; i++) {
289         const ZipEntryRO entry = zipFile.findEntryByIndex(i);
290         if (entry == NULL) {
291             continue;
292         }
293
294         // Make sure this entry has a filename.
295         if (zipFile.getEntryFileName(entry, fileName, sizeof(fileName))) {
296             continue;
297         }
298
299         // Make sure we're in the lib directory of the ZIP.
300         if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) {
301             continue;
302         }
303
304         // Make sure the filename is at least to the minimum library name size.
305         const size_t fileNameLen = strlen(fileName);
306         static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
307         if (fileNameLen < minLength) {
308             continue;
309         }
310
311         const char* lastSlash = strrchr(fileName, '/');
312         ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName);
313
314         // Check to make sure the CPU ABI of this file is one we support.
315         const char* cpuAbiOffset = fileName + APK_LIB_LEN;
316         const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset;
317
318         ALOGV("Comparing ABIs %s and %s versus %s\n", cpuAbi.c_str(), cpuAbi2.c_str(), cpuAbiOffset);
319         if (cpuAbi.size() == cpuAbiRegionSize
320                 && *(cpuAbiOffset + cpuAbi.size()) == '/'
321                 && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
322             ALOGV("Using primary ABI %s\n", cpuAbi.c_str());
323             hasPrimaryAbi = true;
324         } else if (cpuAbi2.size() == cpuAbiRegionSize
325                 && *(cpuAbiOffset + cpuAbi2.size()) == '/'
326                 && !strncmp(cpuAbiOffset, cpuAbi2.c_str(), cpuAbiRegionSize)) {
327
328             /*
329              * If this library matches both the primary and secondary ABIs,
330              * only use the primary ABI.
331              */
332             if (hasPrimaryAbi) {
333                 ALOGV("Already saw primary ABI, skipping secondary ABI %s\n", cpuAbi2.c_str());
334                 continue;
335             } else {
336                 ALOGV("Using secondary ABI %s\n", cpuAbi2.c_str());
337             }
338         } else {
339             ALOGV("abi didn't match anything: %s (end at %zd)\n", cpuAbiOffset, cpuAbiRegionSize);
340             continue;
341         }
342
343         // If this is a .so file, check to see if we need to copy it.
344         if ((!strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
345                     && !strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)
346                     && isFilenameSafe(lastSlash + 1))
347                 || !strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
348
349             install_status_t ret = callFunc(env, callArg, &zipFile, entry, lastSlash + 1);
350
351             if (ret != INSTALL_SUCCEEDED) {
352                 ALOGV("Failure for entry %s", lastSlash + 1);
353                 return ret;
354             }
355         }
356     }
357
358     return INSTALL_SUCCEEDED;
359 }
360
361 static jint
362 com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
363         jstring javaFilePath, jstring javaNativeLibPath, jstring javaCpuAbi, jstring javaCpuAbi2)
364 {
365     return (jint) iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2,
366             copyFileIfChanged, &javaNativeLibPath);
367 }
368
369 static jlong
370 com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, jclass clazz,
371         jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2)
372 {
373     size_t totalSize = 0;
374
375     iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2, sumFiles, &totalSize);
376
377     return totalSize;
378 }
379
380 static JNINativeMethod gMethods[] = {
381     {"nativeCopyNativeBinaries",
382             "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
383             (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
384     {"nativeSumNativeBinaries",
385             "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
386             (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
387 };
388
389
390 int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env)
391 {
392     return AndroidRuntime::registerNativeMethods(env,
393                 "com/android/internal/content/NativeLibraryHelper", gMethods, NELEM(gMethods));
394 }
395
396 };