• 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 static jfieldID s_chunkSizeField = 0;
41 static jfieldID s_keyPrefixField = 0;
42 
43 static int
readHeader_native(JNIEnv * env,jobject clazz,jobject headerObj,jobject fdObj)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         LOGW("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         LOGW("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         LOGW("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     LOGD("chunk header:");
89     LOGD("  headerSize=%d", flattenedHeader.headerSize);
90     LOGD("  version=0x%08x", flattenedHeader.version);
91     LOGD("  dataSize=%d", flattenedHeader.dataSize);
92     LOGD("  nameLength=%d", flattenedHeader.nameLength);
93 #endif
94 
95     if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 ||
96             remainingHeader < flattenedHeader.nameLength) {
97         LOGW("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         LOGW("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
skipChunk_native(JNIEnv * env,jobject clazz,jobject fdObj,jint bytesToSkip)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
padding_len(int len)136 padding_len(int len)
137 {
138     len = len % 4;
139     return len == 0 ? len : 4 - len;
140 }
141 
142 static int
allocateHeader_native(JNIEnv * env,jobject clazz,jobject headerObj,jobject fdObj)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
writeHeader_native(JNIEnv * env,jobject clazz,jobject headerObj,jobject fdObj,jint pos)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 
register_android_backup_BackupHelperDispatcher(JNIEnv * env)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