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