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 int 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 -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 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 -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 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 -1; 100 } 101 102 buf = keyPrefix.lockBuffer(flattenedHeader.nameLength); 103 if (buf == NULL) { 104 ALOGW("unable to allocate %d bytes", flattenedHeader.nameLength); 105 return -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 0; 123 } 124 125 static int 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 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 int 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 pos; 165 } 166 167 static int 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 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 errno; 199 } 200 201 if (namePadding != 0) { 202 int zero = 0; 203 err = write(fd, &zero, namePadding); 204 if (err != namePadding) { 205 return errno; 206 } 207 } 208 209 lseek(fd, prevPos, SEEK_SET); 210 return 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