• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
readHeader_native(JNIEnv * env,jobject clazz,jobject headerObj,jobject fdObj)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
skipChunk_native(JNIEnv * env,jobject clazz,jobject fdObj,jint bytesToSkip)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
padding_len(int len)137 padding_len(int len)
138 {
139     len = len % 4;
140     return len == 0 ? len : 4 - len;
141 }
142 
143 static jint
allocateHeader_native(JNIEnv * env,jobject clazz,jobject headerObj,jobject fdObj)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
writeHeader_native(JNIEnv * env,jobject clazz,jobject headerObj,jobject fdObj,jint pos)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 
register_android_backup_BackupHelperDispatcher(JNIEnv * env)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