1 /*
2 * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * Native method support for java.util.zip.Inflater
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <nativehelper/JNIHelp.h>
35 #include "jlong.h"
36 #include "jni.h"
37 #include "jvm.h"
38 #include "jni_util.h"
39 #include <zlib.h>
40
41 #define NATIVE_METHOD(className, functionName, signature) \
42 { #functionName, signature, (void*)(className ## _ ## functionName) }
43
44 #define ThrowDataFormatException(env, msg) \
45 JNU_ThrowByName(env, "java/util/zip/DataFormatException", msg)
46
47 static jfieldID inputConsumedID;
48 static jfieldID outputConsumedID;
49
50 JNIEXPORT void JNICALL
Inflater_initIDs(JNIEnv * env)51 static Inflater_initIDs(JNIEnv *env)
52 {
53 jclass cls = (*env)->FindClass(env, "java/util/zip/Inflater");
54 inputConsumedID = (*env)->GetFieldID(env, cls, "inputConsumed", "I");
55 outputConsumedID = (*env)->GetFieldID(env, cls, "outputConsumed", "I");
56 CHECK_NULL(inputConsumedID);
57 CHECK_NULL(outputConsumedID);
58 }
59
60 JNIEXPORT jlong JNICALL
Inflater_init(JNIEnv * env,jclass cls,jboolean nowrap)61 Inflater_init(JNIEnv *env, jclass cls, jboolean nowrap)
62 {
63 z_stream *strm = calloc(1, sizeof(z_stream));
64
65 if (strm == NULL) {
66 JNU_ThrowOutOfMemoryError(env, 0);
67 return jlong_zero;
68 } else {
69 const char *msg;
70 int ret = inflateInit2(strm, nowrap ? -MAX_WBITS : MAX_WBITS);
71 switch (ret) {
72 case Z_OK:
73 return ptr_to_jlong(strm);
74 case Z_MEM_ERROR:
75 free(strm);
76 JNU_ThrowOutOfMemoryError(env, 0);
77 return jlong_zero;
78 default:
79 msg = ((strm->msg != NULL) ? strm->msg :
80 (ret == Z_VERSION_ERROR) ?
81 "zlib returned Z_VERSION_ERROR: "
82 "compile time and runtime zlib implementations differ" :
83 (ret == Z_STREAM_ERROR) ?
84 "inflateInit2 returned Z_STREAM_ERROR" :
85 "unknown error initializing zlib library");
86 free(strm);
87 JNU_ThrowInternalError(env, msg);
88 return jlong_zero;
89 }
90 }
91 }
92
checkSetDictionaryResult(JNIEnv * env,jlong addr,int res)93 static void checkSetDictionaryResult(JNIEnv *env, jlong addr, int res)
94 {
95 switch (res) {
96 case Z_OK:
97 break;
98 case Z_STREAM_ERROR:
99 case Z_DATA_ERROR:
100 JNU_ThrowIllegalArgumentException(env, ((z_stream *)jlong_to_ptr(addr))->msg);
101 break;
102 default:
103 JNU_ThrowInternalError(env, ((z_stream *)jlong_to_ptr(addr))->msg);
104 break;
105 }
106 }
107
108 JNIEXPORT void JNICALL
Inflater_setDictionary(JNIEnv * env,jclass cls,jlong addr,jbyteArray b,jint off,jint len)109 Inflater_setDictionary(JNIEnv *env, jclass cls, jlong addr,
110 jbyteArray b, jint off, jint len)
111 {
112 jint res;
113 Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
114 if (buf == NULL) /* out of memory */
115 return;
116 res = inflateSetDictionary(jlong_to_ptr(addr), buf + off, len);
117 (*env)->ReleasePrimitiveArrayCritical(env, b, buf, 0);
118 checkSetDictionaryResult(env, addr, res);
119 }
120
121 JNIEXPORT void JNICALL
Inflater_setDictionaryBuffer(JNIEnv * env,jclass cls,jlong addr,jlong bufferAddr,jint len)122 Inflater_setDictionaryBuffer(JNIEnv *env, jclass cls, jlong addr,
123 jlong bufferAddr, jint len)
124 {
125 jint res;
126 Bytef *buf = jlong_to_ptr(bufferAddr);
127 res = inflateSetDictionary(jlong_to_ptr(addr), buf, len);
128 checkSetDictionaryResult(env, addr, res);
129 }
130
doInflate(jlong addr,jbyte * input,jint inputLen,jbyte * output,jint outputLen)131 static jint doInflate(jlong addr,
132 jbyte *input, jint inputLen,
133 jbyte *output, jint outputLen)
134 {
135 jint ret;
136 z_stream *strm = jlong_to_ptr(addr);
137
138 strm->next_in = (Bytef *) input;
139 strm->next_out = (Bytef *) output;
140 strm->avail_in = inputLen;
141 strm->avail_out = outputLen;
142
143 ret = inflate(strm, Z_PARTIAL_FLUSH);
144 return ret;
145 }
146
checkInflateStatus(JNIEnv * env,jobject this,jlong addr,jint inputLen,jint outputLen,jint ret)147 static jlong checkInflateStatus(JNIEnv *env, jobject this, jlong addr,
148 jint inputLen, jint outputLen, jint ret )
149 {
150 z_stream *strm = jlong_to_ptr(addr);
151 jint inputUsed = 0, outputUsed = 0;
152 int finished = 0;
153 int needDict = 0;
154
155 switch (ret) {
156 case Z_STREAM_END:
157 finished = 1;
158 /* fall through */
159 case Z_OK:
160 inputUsed = inputLen - strm->avail_in;
161 outputUsed = outputLen - strm->avail_out;
162 break;
163 case Z_NEED_DICT:
164 needDict = 1;
165 /* Might have consumed some input here! */
166 inputUsed = inputLen - strm->avail_in;
167 /* zlib is unclear about whether output may be produced */
168 outputUsed = outputLen - strm->avail_out;
169 break;
170 case Z_BUF_ERROR:
171 break;
172 case Z_DATA_ERROR:
173 inputUsed = inputLen - strm->avail_in;
174 (*env)->SetIntField(env, this, inputConsumedID, inputUsed);
175 outputUsed = outputLen - strm->avail_out;
176 (*env)->SetIntField(env, this, outputConsumedID, outputUsed);
177 ThrowDataFormatException(env, strm->msg);
178 break;
179 case Z_MEM_ERROR:
180 JNU_ThrowOutOfMemoryError(env, 0);
181 break;
182 default:
183 JNU_ThrowInternalError(env, strm->msg);
184 break;
185 }
186 return ((jlong)inputUsed) | (((jlong)outputUsed) << 31) | (((jlong)finished) << 62) | (((jlong)needDict) << 63);
187 }
188
189 JNIEXPORT jlong JNICALL
Inflater_inflateBytesBytes(JNIEnv * env,jobject this,jlong addr,jbyteArray inputArray,jint inputOff,jint inputLen,jbyteArray outputArray,jint outputOff,jint outputLen)190 Inflater_inflateBytesBytes(JNIEnv *env, jobject this, jlong addr,
191 jbyteArray inputArray, jint inputOff, jint inputLen,
192 jbyteArray outputArray, jint outputOff, jint outputLen)
193 {
194 jbyte *input = (*env)->GetPrimitiveArrayCritical(env, inputArray, 0);
195 jbyte *output;
196 jint ret;
197 jlong retVal;
198
199 if (input == NULL) {
200 if (inputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
201 JNU_ThrowOutOfMemoryError(env, 0);
202 return 0L;
203 }
204 output = (*env)->GetPrimitiveArrayCritical(env, outputArray, 0);
205 if (output == NULL) {
206 (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
207 if (outputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
208 JNU_ThrowOutOfMemoryError(env, 0);
209 return 0L;
210 }
211
212 ret = doInflate(addr, input + inputOff, inputLen, output + outputOff,
213 outputLen);
214
215 (*env)->ReleasePrimitiveArrayCritical(env, outputArray, output, 0);
216 (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
217
218 retVal = checkInflateStatus(env, this, addr, inputLen, outputLen, ret );
219 return retVal;
220 }
221
222 JNIEXPORT jlong JNICALL
Inflater_inflateBytesBuffer(JNIEnv * env,jobject this,jlong addr,jbyteArray inputArray,jint inputOff,jint inputLen,jlong outputBuffer,jint outputLen)223 Inflater_inflateBytesBuffer(JNIEnv *env, jobject this, jlong addr,
224 jbyteArray inputArray, jint inputOff, jint inputLen,
225 jlong outputBuffer, jint outputLen)
226 {
227 jbyte *input = (*env)->GetPrimitiveArrayCritical(env, inputArray, 0);
228 jbyte *output;
229 jint ret;
230 jlong retVal;
231
232 if (input == NULL) {
233 if (inputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
234 JNU_ThrowOutOfMemoryError(env, 0);
235 return 0L;
236 }
237 output = jlong_to_ptr(outputBuffer);
238
239 ret = doInflate(addr, input + inputOff, inputLen, output, outputLen);
240
241 (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
242 retVal = checkInflateStatus(env, this, addr, inputLen, outputLen, ret );
243
244 return retVal;
245 }
246
247 JNIEXPORT jlong JNICALL
Inflater_inflateBufferBytes(JNIEnv * env,jobject this,jlong addr,jlong inputBuffer,jint inputLen,jbyteArray outputArray,jint outputOff,jint outputLen)248 Inflater_inflateBufferBytes(JNIEnv *env, jobject this, jlong addr,
249 jlong inputBuffer, jint inputLen,
250 jbyteArray outputArray, jint outputOff, jint outputLen)
251 {
252 jbyte *input = jlong_to_ptr(inputBuffer);
253 jbyte *output = (*env)->GetPrimitiveArrayCritical(env, outputArray, 0);
254 jint ret;
255 jlong retVal;
256
257 if (output == NULL) {
258 if (outputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
259 JNU_ThrowOutOfMemoryError(env, 0);
260 return 0L;
261 }
262
263 ret = doInflate(addr, input, inputLen, output + outputOff, outputLen);
264
265 (*env)->ReleasePrimitiveArrayCritical(env, outputArray, output, 0);
266 retVal = checkInflateStatus(env, this, addr, inputLen, outputLen, ret );
267
268 return retVal;
269 }
270
271 JNIEXPORT jlong JNICALL
Inflater_inflateBufferBuffer(JNIEnv * env,jobject this,jlong addr,jlong inputBuffer,jint inputLen,jlong outputBuffer,jint outputLen)272 Inflater_inflateBufferBuffer(JNIEnv *env, jobject this, jlong addr,
273 jlong inputBuffer, jint inputLen,
274 jlong outputBuffer, jint outputLen)
275 {
276 jbyte *input = jlong_to_ptr(inputBuffer);
277 jbyte *output = jlong_to_ptr(outputBuffer);
278 jint ret;
279 jlong retVal;
280
281 ret = doInflate(addr, input, inputLen, output, outputLen);
282 retVal = checkInflateStatus(env, this, addr, inputLen, outputLen, ret);
283 return retVal;
284 }
285
286 JNIEXPORT jint JNICALL
Inflater_getAdler(JNIEnv * env,jclass cls,jlong addr)287 Inflater_getAdler(JNIEnv *env, jclass cls, jlong addr)
288 {
289 return ((z_stream *)jlong_to_ptr(addr))->adler;
290 }
291
292 JNIEXPORT void JNICALL
Inflater_reset(JNIEnv * env,jclass cls,jlong addr)293 Inflater_reset(JNIEnv *env, jclass cls, jlong addr)
294 {
295 if (inflateReset(jlong_to_ptr(addr)) != Z_OK) {
296 JNU_ThrowInternalError(env, 0);
297 }
298 }
299
300 JNIEXPORT void JNICALL
Inflater_end(JNIEnv * env,jclass cls,jlong addr)301 Inflater_end(JNIEnv *env, jclass cls, jlong addr)
302 {
303 if (inflateEnd(jlong_to_ptr(addr)) == Z_STREAM_ERROR) {
304 JNU_ThrowInternalError(env, 0);
305 } else {
306 free(jlong_to_ptr(addr));
307 }
308 }
309
310 static JNINativeMethod gMethods[] = {
311 NATIVE_METHOD(Inflater, init, "(Z)J"),
312 NATIVE_METHOD(Inflater, setDictionary, "(J[BII)V"),
313 NATIVE_METHOD(Inflater, setDictionaryBuffer, "(JJI)V"),
314 NATIVE_METHOD(Inflater, inflateBytesBytes, "(J[BII[BII)J"),
315 NATIVE_METHOD(Inflater, inflateBytesBuffer, "(J[BIIJI)J"),
316 NATIVE_METHOD(Inflater, inflateBufferBytes, "(JJI[BII)J"),
317 NATIVE_METHOD(Inflater, inflateBufferBuffer, "(JJIJI)J"),
318 NATIVE_METHOD(Inflater, getAdler, "(J)I"),
319 NATIVE_METHOD(Inflater, reset, "(J)V"),
320 NATIVE_METHOD(Inflater, end, "(J)V"),
321 };
322
register_java_util_zip_Inflater(JNIEnv * env)323 void register_java_util_zip_Inflater(JNIEnv* env) {
324 jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
325
326 Inflater_initIDs(env);
327 }
328