libcore_io_Linux.cpp revision dedaccdfa07c370a58cba08b096133ad9eec0ec3
1/*
2 * Copyright (C) 2011 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 "Posix"
18
19#include "JNIHelp.h"
20#include "JniConstants.h"
21#include "JniException.h"
22#include "ScopedUtfChars.h"
23#include "toStringArray.h"
24
25#include <errno.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <sys/stat.h>
29
30static void throwErrnoException(JNIEnv* env, const char* name, int errnum) {
31    jthrowable cause = NULL;
32    if (env->ExceptionCheck()) {
33        cause = env->ExceptionOccurred();
34        env->ExceptionClear();
35    }
36
37    ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
38    if (javaName.get() == NULL) {
39        // Not really much we can do here. We're probably dead in the water,
40        // but let's try to stumble on...
41        env->ExceptionClear();
42    }
43
44    jobject exception;
45    if (cause != NULL) {
46        static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass, "<init>",
47                "(Ljava/lang/String;ILjava/lang/Throwable;)V");
48        exception = env->NewObject(JniConstants::errnoExceptionClass, ctor,
49                javaName.get(), errnum, cause);
50    } else {
51        static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass, "<init>",
52                "(Ljava/lang/String;I)V");
53        exception = env->NewObject(JniConstants::errnoExceptionClass, ctor, javaName.get(), errnum);
54    }
55    env->Throw(reinterpret_cast<jthrowable>(exception));
56}
57
58template <typename rc_t>
59static rc_t throwIfMinusOne(JNIEnv* env, const char* name, int errnum, rc_t rc) {
60    if (rc == rc_t(-1)) {
61        throwErrnoException(env, name, errnum);
62    }
63    return rc;
64}
65
66static jobject makeStructStat(JNIEnv* env, const struct stat& sb) {
67    static jmethodID ctor = env->GetMethodID(JniConstants::structStatClass, "<init>",
68            "(JJIJIIJJJJJJJ)V");
69    return env->NewObject(JniConstants::structStatClass, ctor,
70            jlong(sb.st_dev), jlong(sb.st_ino), jint(sb.st_mode), jlong(sb.st_nlink),
71            jint(sb.st_uid), jint(sb.st_gid), jlong(sb.st_rdev), jlong(sb.st_size),
72            jlong(sb.st_atime), jlong(sb.st_mtime), jlong(sb.st_ctime),
73            jlong(sb.st_blksize), jlong(sb.st_blocks));
74}
75
76static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) {
77    ScopedUtfChars path(env, javaPath);
78    if (path.c_str() == NULL) {
79        return NULL;
80    }
81    struct stat sb;
82    int rc = isLstat ? TEMP_FAILURE_RETRY(lstat(path.c_str(), &sb))
83                     : TEMP_FAILURE_RETRY(stat(path.c_str(), &sb));
84    if (rc == -1) {
85        throwErrnoException(env, isLstat ? "lstat" : "stat", errno);
86        return NULL;
87    }
88    return makeStructStat(env, sb);
89}
90
91static jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode) {
92    ScopedUtfChars path(env, javaPath);
93    if (path.c_str() == NULL) {
94        return JNI_FALSE;
95    }
96    int rc = TEMP_FAILURE_RETRY(access(path.c_str(), mode));
97    if (rc == -1) {
98        throwErrnoException(env, "access", errno);
99    }
100    return (rc == 0);
101}
102
103static jobjectArray Posix_environ(JNIEnv* env, jobject) {
104    extern char** environ; // Standard, but not in any header file.
105    return toStringArray(env, environ);
106}
107
108static void Posix_fdatasync(JNIEnv* env, jobject, jobject javaFd) {
109    int fd = jniGetFDFromFileDescriptor(env, javaFd);
110    throwIfMinusOne(env, "fdatasync", errno, TEMP_FAILURE_RETRY(fdatasync(fd)));
111}
112
113static jobject Posix_fstat(JNIEnv* env, jobject, jobject javaFd) {
114    int fd = jniGetFDFromFileDescriptor(env, javaFd);
115    struct stat sb;
116    int rc = TEMP_FAILURE_RETRY(fstat(fd, &sb));
117    if (rc == -1) {
118        throwErrnoException(env, "fstat", errno);
119        return NULL;
120    }
121    return makeStructStat(env, sb);
122}
123
124static void Posix_fsync(JNIEnv* env, jobject, jobject javaFd) {
125    int fd = jniGetFDFromFileDescriptor(env, javaFd);
126    throwIfMinusOne(env, "fsync", errno, TEMP_FAILURE_RETRY(fsync(fd)));
127}
128
129static void Posix_ftruncate(JNIEnv* env, jobject, jobject javaFd, jlong length) {
130    int fd = jniGetFDFromFileDescriptor(env, javaFd);
131    throwIfMinusOne(env, "ftruncate", errno, TEMP_FAILURE_RETRY(ftruncate64(fd, length)));
132}
133
134static jstring Posix_getenv(JNIEnv* env, jobject, jstring javaName) {
135    ScopedUtfChars name(env, javaName);
136    if (name.c_str() == NULL) {
137        return NULL;
138    }
139    return env->NewStringUTF(getenv(name.c_str()));
140}
141
142static jlong Posix_lseek(JNIEnv* env, jobject, jobject javaFd, jlong offset, jint whence) {
143    int fd = jniGetFDFromFileDescriptor(env, javaFd);
144    return throwIfMinusOne(env, "lseek", errno, TEMP_FAILURE_RETRY(lseek64(fd, offset, whence)));
145}
146
147static jobject Posix_lstat(JNIEnv* env, jobject, jstring javaPath) {
148    return doStat(env, javaPath, true);
149}
150
151static jobject Posix_stat(JNIEnv* env, jobject, jstring javaPath) {
152    return doStat(env, javaPath, false);
153}
154
155static jstring Posix_strerror(JNIEnv* env, jobject, jint errnum) {
156    char buffer[BUFSIZ];
157    const char* message = jniStrError(errnum, buffer, sizeof(buffer));
158    return env->NewStringUTF(message);
159}
160
161static jlong Posix_sysconf(JNIEnv* env, jobject, jint name) {
162    // Since -1 is a valid result from sysconf(3), detecting failure is a little more awkward.
163    errno = 0;
164    long result = sysconf(name);
165    if (result == -1L && errno == EINVAL) {
166        throwErrnoException(env, "sysconf", errno);
167    }
168    return result;
169}
170
171static JNINativeMethod gMethods[] = {
172    NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
173    NATIVE_METHOD(Posix, environ, "()[Ljava/lang/String;"),
174    NATIVE_METHOD(Posix, fdatasync, "(Ljava/io/FileDescriptor;)V"),
175    NATIVE_METHOD(Posix, fstat, "(Ljava/io/FileDescriptor;)Llibcore/io/StructStat;"),
176    NATIVE_METHOD(Posix, fsync, "(Ljava/io/FileDescriptor;)V"),
177    NATIVE_METHOD(Posix, ftruncate, "(Ljava/io/FileDescriptor;J)V"),
178    NATIVE_METHOD(Posix, getenv, "(Ljava/lang/String;)Ljava/lang/String;"),
179    NATIVE_METHOD(Posix, lseek, "(Ljava/io/FileDescriptor;JI)J"),
180    NATIVE_METHOD(Posix, lstat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
181    NATIVE_METHOD(Posix, stat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
182    NATIVE_METHOD(Posix, strerror, "(I)Ljava/lang/String;"),
183    NATIVE_METHOD(Posix, sysconf, "(I)J"),
184};
185int register_libcore_io_Posix(JNIEnv* env) {
186    return jniRegisterNativeMethods(env, "libcore/io/Posix", gMethods, NELEM(gMethods));
187}
188