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