/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "BackupHelperDispatcher_native" #include #include "JNIHelp.h" #include #include #include #include #define VERSION_1_HEADER 0x01706c48 // 'Hlp'1 little endian namespace android { struct chunk_header_v1 { int headerSize; int version; int dataSize; // corresponds to Header.chunkSize int nameLength; // not including the NULL terminator, which is not written to the file }; static jfieldID s_chunkSizeField = 0; static jfieldID s_keyPrefixField = 0; static int readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) { chunk_header_v1 flattenedHeader; ssize_t amt; String8 keyPrefix; char* buf; int fd = jniGetFDFromFileDescriptor(env, fdObj); amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize)); if (amt != sizeof(flattenedHeader.headerSize)) { return -1; } int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize); if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) { ALOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize); if (remainingHeader > 0) { lseek(fd, remainingHeader, SEEK_CUR); // >0 means skip this chunk return 1; } } amt = read(fd, &flattenedHeader.version, sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize)); if (amt <= 0) { ALOGW("Failed reading chunk header"); return -1; } remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize); if (flattenedHeader.version != VERSION_1_HEADER) { ALOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version, flattenedHeader.headerSize); if (remainingHeader > 0) { lseek(fd, remainingHeader, SEEK_CUR); // >0 means skip this chunk return 1; } } #if 0 ALOGD("chunk header:"); ALOGD(" headerSize=%d", flattenedHeader.headerSize); ALOGD(" version=0x%08x", flattenedHeader.version); ALOGD(" dataSize=%d", flattenedHeader.dataSize); ALOGD(" nameLength=%d", flattenedHeader.nameLength); #endif if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 || remainingHeader < flattenedHeader.nameLength) { ALOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader, flattenedHeader.dataSize, flattenedHeader.nameLength); return -1; } buf = keyPrefix.lockBuffer(flattenedHeader.nameLength); if (buf == NULL) { ALOGW("unable to allocate %d bytes", flattenedHeader.nameLength); return -1; } amt = read(fd, buf, flattenedHeader.nameLength); buf[flattenedHeader.nameLength] = 0; keyPrefix.unlockBuffer(flattenedHeader.nameLength); remainingHeader -= flattenedHeader.nameLength; if (remainingHeader > 0) { lseek(fd, remainingHeader, SEEK_CUR); } env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize); env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string())); return 0; } static int skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip) { int fd = jniGetFDFromFileDescriptor(env, fdObj); lseek(fd, bytesToSkip, SEEK_CUR); return 0; } static int padding_len(int len) { len = len % 4; return len == 0 ? len : 4 - len; } static int allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) { int pos; jstring nameObj; int nameLength; int namePadding; int headerSize; int fd = jniGetFDFromFileDescriptor(env, fdObj); nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); nameLength = env->GetStringUTFLength(nameObj); namePadding = padding_len(nameLength); headerSize = sizeof(chunk_header_v1) + nameLength + namePadding; pos = lseek(fd, 0, SEEK_CUR); lseek(fd, headerSize, SEEK_CUR); return pos; } static int writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos) { int err; chunk_header_v1 header; int namePadding; int prevPos; jstring nameObj; const char* buf; int fd = jniGetFDFromFileDescriptor(env, fdObj); prevPos = lseek(fd, 0, SEEK_CUR); nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); header.nameLength = env->GetStringUTFLength(nameObj); namePadding = padding_len(header.nameLength); header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding; header.version = VERSION_1_HEADER; header.dataSize = prevPos - (pos + header.headerSize); lseek(fd, pos, SEEK_SET); err = write(fd, &header, sizeof(chunk_header_v1)); if (err != sizeof(chunk_header_v1)) { return errno; } buf = env->GetStringUTFChars(nameObj, NULL); err = write(fd, buf, header.nameLength); env->ReleaseStringUTFChars(nameObj, buf); if (err != header.nameLength) { return errno; } if (namePadding != 0) { int zero = 0; err = write(fd, &zero, namePadding); if (err != namePadding) { return errno; } } lseek(fd, prevPos, SEEK_SET); return 0; } static const JNINativeMethod g_methods[] = { { "readHeader_native", "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", (void*)readHeader_native }, { "skipChunk_native", "(Ljava/io/FileDescriptor;I)I", (void*)skipChunk_native }, { "allocateHeader_native", "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", (void*)allocateHeader_native }, { "writeHeader_native", "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I", (void*)writeHeader_native }, }; int register_android_backup_BackupHelperDispatcher(JNIEnv* env) { jclass clazz = env->FindClass("android/app/backup/BackupHelperDispatcher$Header"); LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.backup.BackupHelperDispatcher.Header"); s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I"); LOG_FATAL_IF(s_chunkSizeField == NULL, "Unable to find chunkSize field in android.app.backup.BackupHelperDispatcher.Header"); s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;"); LOG_FATAL_IF(s_keyPrefixField == NULL, "Unable to find keyPrefix field in android.app.backup.BackupHelperDispatcher.Header"); return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupHelperDispatcher", g_methods, NELEM(g_methods)); } }