Home | History | Annotate | Download | only in jni
      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_TAG "BackupHelperDispatcher_native"
     18 #include <utils/Log.h>
     19 
     20 #include "JNIHelp.h"
     21 #include <android_runtime/AndroidRuntime.h>
     22 
     23 #include <sys/types.h>
     24 #include <sys/uio.h>
     25 #include <unistd.h>
     26 
     27 
     28 #define VERSION_1_HEADER 0x01706c48  // 'Hlp'1 little endian
     29 
     30 namespace android
     31 {
     32 
     33 struct chunk_header_v1 {
     34     int headerSize;
     35     int version;
     36     int dataSize; // corresponds to Header.chunkSize
     37     int nameLength; // not including the NULL terminator, which is not written to the file
     38 };
     39 
     40 static jfieldID s_chunkSizeField = 0;
     41 static jfieldID s_keyPrefixField = 0;
     42 
     43 static jint
     44 readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
     45 {
     46     chunk_header_v1 flattenedHeader;
     47     ssize_t amt;
     48     String8 keyPrefix;
     49     char* buf;
     50 
     51     int fd = jniGetFDFromFileDescriptor(env, fdObj);
     52 
     53     amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
     54     if (amt != sizeof(flattenedHeader.headerSize)) {
     55         return (jint) -1;
     56     }
     57 
     58     int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize);
     59 
     60     if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) {
     61         ALOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize);
     62         if (remainingHeader > 0) {
     63             lseek(fd, remainingHeader, SEEK_CUR);
     64             // >0 means skip this chunk
     65             return (jint) 1;
     66         }
     67     }
     68 
     69     amt = read(fd, &flattenedHeader.version,
     70             sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize));
     71     if (amt <= 0) {
     72         ALOGW("Failed reading chunk header");
     73         return (jint) -1;
     74     }
     75     remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize);
     76 
     77     if (flattenedHeader.version != VERSION_1_HEADER) {
     78         ALOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version,
     79                 flattenedHeader.headerSize);
     80         if (remainingHeader > 0) {
     81             lseek(fd, remainingHeader, SEEK_CUR);
     82             // >0 means skip this chunk
     83             return (jint) 1;
     84         }
     85     }
     86 
     87 #if 0
     88     ALOGD("chunk header:");
     89     ALOGD("  headerSize=%d", flattenedHeader.headerSize);
     90     ALOGD("  version=0x%08x", flattenedHeader.version);
     91     ALOGD("  dataSize=%d", flattenedHeader.dataSize);
     92     ALOGD("  nameLength=%d", flattenedHeader.nameLength);
     93 #endif
     94 
     95     if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 ||
     96             remainingHeader < flattenedHeader.nameLength) {
     97         ALOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader,
     98                 flattenedHeader.dataSize, flattenedHeader.nameLength);
     99         return (jint) -1;
    100     }
    101 
    102     buf = keyPrefix.lockBuffer(flattenedHeader.nameLength);
    103     if (buf == NULL) {
    104         ALOGW("unable to allocate %d bytes", flattenedHeader.nameLength);
    105         return (jint) -1;
    106     }
    107 
    108     amt = read(fd, buf, flattenedHeader.nameLength);
    109     buf[flattenedHeader.nameLength] = 0;
    110 
    111     keyPrefix.unlockBuffer(flattenedHeader.nameLength);
    112 
    113     remainingHeader -= flattenedHeader.nameLength;
    114 
    115     if (remainingHeader > 0) {
    116         lseek(fd, remainingHeader, SEEK_CUR);
    117     }
    118 
    119     env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize);
    120     env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string()));
    121 
    122     return (jint) 0;
    123 }
    124 
    125 static jint
    126 skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
    127 {
    128     int fd = jniGetFDFromFileDescriptor(env, fdObj);
    129 
    130     lseek(fd, bytesToSkip, SEEK_CUR);
    131 
    132     return (jint) 0;
    133 }
    134 
    135 static int
    136 padding_len(int len)
    137 {
    138     len = len % 4;
    139     return len == 0 ? len : 4 - len;
    140 }
    141 
    142 static jint
    143 allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
    144 {
    145     int pos;
    146     jstring nameObj;
    147     int nameLength;
    148     int namePadding;
    149     int headerSize;
    150 
    151     int fd = jniGetFDFromFileDescriptor(env, fdObj);
    152 
    153     nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
    154 
    155     nameLength = env->GetStringUTFLength(nameObj);
    156     namePadding = padding_len(nameLength);
    157 
    158     headerSize = sizeof(chunk_header_v1) + nameLength + namePadding;
    159 
    160     pos = lseek(fd, 0, SEEK_CUR);
    161 
    162     lseek(fd, headerSize, SEEK_CUR);
    163 
    164     return (jint) pos;
    165 }
    166 
    167 static jint
    168 writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos)
    169 {
    170     int err;
    171     chunk_header_v1 header;
    172     int namePadding;
    173     int prevPos;
    174     jstring nameObj;
    175     const char* buf;
    176 
    177     int fd = jniGetFDFromFileDescriptor(env, fdObj);
    178     prevPos = lseek(fd, 0, SEEK_CUR);
    179 
    180     nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
    181     header.nameLength = env->GetStringUTFLength(nameObj);
    182     namePadding = padding_len(header.nameLength);
    183 
    184     header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding;
    185     header.version = VERSION_1_HEADER;
    186     header.dataSize = prevPos - (pos + header.headerSize);
    187 
    188     lseek(fd, pos, SEEK_SET);
    189     err = write(fd, &header, sizeof(chunk_header_v1));
    190     if (err != sizeof(chunk_header_v1)) {
    191         return (jint) errno;
    192     }
    193 
    194     buf = env->GetStringUTFChars(nameObj, NULL);
    195     err = write(fd, buf, header.nameLength);
    196     env->ReleaseStringUTFChars(nameObj, buf);
    197     if (err != header.nameLength) {
    198         return (jint) errno;
    199     }
    200 
    201     if (namePadding != 0) {
    202         int zero = 0;
    203         err = write(fd, &zero, namePadding);
    204         if (err != namePadding) {
    205             return (jint) errno;
    206         }
    207     }
    208 
    209     lseek(fd, prevPos, SEEK_SET);
    210     return (jint) 0;
    211 }
    212 
    213 static const JNINativeMethod g_methods[] = {
    214     { "readHeader_native",
    215        "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
    216        (void*)readHeader_native },
    217     { "skipChunk_native",
    218         "(Ljava/io/FileDescriptor;I)I",
    219         (void*)skipChunk_native },
    220     { "allocateHeader_native",
    221         "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
    222         (void*)allocateHeader_native },
    223     { "writeHeader_native",
    224        "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
    225        (void*)writeHeader_native },
    226 };
    227 
    228 int register_android_backup_BackupHelperDispatcher(JNIEnv* env)
    229 {
    230     jclass clazz = env->FindClass("android/app/backup/BackupHelperDispatcher$Header");
    231     LOG_FATAL_IF(clazz == NULL,
    232             "Unable to find class android.app.backup.BackupHelperDispatcher.Header");
    233     s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
    234     LOG_FATAL_IF(s_chunkSizeField == NULL,
    235             "Unable to find chunkSize field in android.app.backup.BackupHelperDispatcher.Header");
    236     s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
    237     LOG_FATAL_IF(s_keyPrefixField == NULL,
    238             "Unable to find keyPrefix field in android.app.backup.BackupHelperDispatcher.Header");
    239 
    240     return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupHelperDispatcher",
    241             g_methods, NELEM(g_methods));
    242 }
    243 
    244 }
    245