1/*
2 * Copyright (c) 1994, 2013, 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#include <stdlib.h>
27#include <string.h>
28#include <stddef.h>
29
30#include "jni.h"
31#include "jni_util.h"
32#include "jvm.h"
33#include "io_util.h"
34#include "io_util_md.h"
35
36/* IO helper functions */
37
38jint
39readSingle(JNIEnv *env, jobject this, jfieldID fid) {
40    jint nread;
41    char ret;
42    FD fd = GET_FD(this, fid);
43    if (fd == -1) {
44        JNU_ThrowIOException(env, "Stream Closed");
45        return -1;
46    }
47    nread = IO_Read(fd, &ret, 1);
48    if (nread == 0) { /* EOF */
49        return -1;
50    } else if (nread == -1) { /* error */
51        JNU_ThrowIOExceptionWithLastError(env, "Read error");
52    }
53    return ret & 0xFF;
54}
55
56/* The maximum size of a stack-allocated buffer.
57 */
58#define BUF_SIZE 8192
59
60/*
61 * Returns true if the array slice defined by the given offset and length
62 * is out of bounds.
63 */
64static int
65outOfBounds(JNIEnv *env, jint off, jint len, jbyteArray array) {
66    return ((off < 0) ||
67            (len < 0) ||
68            // We are very careful to avoid signed integer overflow,
69            // the result of which is undefined in C.
70            ((*env)->GetArrayLength(env, array) - off < len));
71}
72
73jint
74readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
75          jint off, jint len, jfieldID fid)
76{
77    jint nread;
78    char stackBuf[BUF_SIZE];
79    char *buf = NULL;
80    FD fd;
81
82    if (IS_NULL(bytes)) {
83        JNU_ThrowNullPointerException(env, NULL);
84        return -1;
85    }
86
87    if (outOfBounds(env, off, len, bytes)) {
88        JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", NULL);
89        return -1;
90    }
91
92    if (len == 0) {
93        return 0;
94    } else if (len > BUF_SIZE) {
95        buf = malloc(len);
96        if (buf == NULL) {
97            JNU_ThrowOutOfMemoryError(env, NULL);
98            return 0;
99        }
100    } else {
101        buf = stackBuf;
102    }
103
104    fd = GET_FD(this, fid);
105    if (fd == -1) {
106        JNU_ThrowIOException(env, "Stream Closed");
107        nread = -1;
108    } else {
109        nread = IO_Read(fd, buf, len);
110        if (nread > 0) {
111            (*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf);
112        } else if (nread == -1) {
113            JNU_ThrowIOExceptionWithLastError(env, "Read error");
114        } else { /* EOF */
115            nread = -1;
116        }
117    }
118
119    if (buf != stackBuf) {
120        free(buf);
121    }
122    return nread;
123}
124
125void
126writeSingle(JNIEnv *env, jobject this, jint byte, jboolean append, jfieldID fid) {
127    // Discard the 24 high-order bits of byte. See OutputStream#write(int)
128    char c = (char) byte;
129    jint n;
130    FD fd = GET_FD(this, fid);
131    if (fd == -1) {
132        JNU_ThrowIOException(env, "Stream Closed");
133        return;
134    }
135    if (append == JNI_TRUE) {
136        n = IO_Append(fd, &c, 1);
137    } else {
138        n = IO_Write(fd, &c, 1);
139    }
140    if (n == -1) {
141        JNU_ThrowIOExceptionWithLastError(env, "Write error");
142    }
143}
144
145void
146writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
147           jint off, jint len, jboolean append, jfieldID fid)
148{
149    jint n;
150    char stackBuf[BUF_SIZE];
151    char *buf = NULL;
152    FD fd;
153
154    if (IS_NULL(bytes)) {
155        JNU_ThrowNullPointerException(env, NULL);
156        return;
157    }
158
159    if (outOfBounds(env, off, len, bytes)) {
160        JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", NULL);
161        return;
162    }
163
164    if (len == 0) {
165        return;
166    } else if (len > BUF_SIZE) {
167        buf = malloc(len);
168        if (buf == NULL) {
169            JNU_ThrowOutOfMemoryError(env, NULL);
170            return;
171        }
172    } else {
173        buf = stackBuf;
174    }
175
176    (*env)->GetByteArrayRegion(env, bytes, off, len, (jbyte *)buf);
177
178    if (!(*env)->ExceptionOccurred(env)) {
179        off = 0;
180        while (len > 0) {
181            fd = GET_FD(this, fid);
182            if (fd == -1) {
183                JNU_ThrowIOException(env, "Stream Closed");
184                break;
185            }
186            if (append == JNI_TRUE) {
187                n = IO_Append(fd, buf+off, len);
188            } else {
189                n = IO_Write(fd, buf+off, len);
190            }
191            if (n == -1) {
192                JNU_ThrowIOExceptionWithLastError(env, "Write error");
193                break;
194            }
195            off += n;
196            len -= n;
197        }
198    }
199    if (buf != stackBuf) {
200        free(buf);
201    }
202}
203
204void
205throwFileNotFoundException(JNIEnv *env, jstring path)
206{
207    char buf[256];
208    size_t n;
209    jobject x;
210    jstring why = NULL;
211
212    n = getLastErrorString(buf, sizeof(buf));
213    if (n > 0) {
214#ifdef WIN32
215        why = (*env)->NewStringUTF(env, buf);
216#else
217        why = JNU_NewStringPlatform(env, buf);
218#endif
219        CHECK_NULL(why);
220    }
221    x = JNU_NewObjectByName(env,
222                            "java/io/FileNotFoundException",
223                            "(Ljava/lang/String;Ljava/lang/String;)V",
224                            path, why);
225    if (x != NULL) {
226        (*env)->Throw(env, x);
227    }
228}
229