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 #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