1 /*
   2  * Copyright (c) 2008, 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 #include "jni.h"
  27 #include "jni_util.h"
  28 #include "jvm.h"
  29 #include "jlong.h"
  30 
  31 #include <stdio.h>
  32 #include <string.h>
  33 #include <dlfcn.h>
  34 #include <errno.h>
  35 #include <mntent.h>
  36 
  37 #include "sun_nio_fs_LinuxNativeDispatcher.h"
  38 
  39 typedef size_t fgetxattr_func(int fd, const char* name, void* value, size_t size);
  40 typedef int fsetxattr_func(int fd, const char* name, void* value, size_t size, int flags);
  41 typedef int fremovexattr_func(int fd, const char* name);
  42 typedef int flistxattr_func(int fd, char* list, size_t size);
  43 
  44 fgetxattr_func* my_fgetxattr_func = NULL;
  45 fsetxattr_func* my_fsetxattr_func = NULL;
  46 fremovexattr_func* my_fremovexattr_func = NULL;
  47 flistxattr_func* my_flistxattr_func = NULL;
  48 
  49 static jfieldID entry_name;
  50 static jfieldID entry_dir;
  51 static jfieldID entry_fstype;
  52 static jfieldID entry_options;
  53 
  54 static void throwUnixException(JNIEnv* env, int errnum) {
  55     jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
  56         "(I)V", errnum);
  57     if (x != NULL) {
  58         (*env)->Throw(env, x);
  59     }
  60 }
  61 
  62 JNIEXPORT void JNICALL
  63 Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz)
  64 {
  65     my_fgetxattr_func = (fgetxattr_func*)dlsym(RTLD_DEFAULT, "fgetxattr");
  66     my_fsetxattr_func = (fsetxattr_func*)dlsym(RTLD_DEFAULT, "fsetxattr");
  67     my_fremovexattr_func = (fremovexattr_func*)dlsym(RTLD_DEFAULT, "fremovexattr");
  68     my_flistxattr_func = (flistxattr_func*)dlsym(RTLD_DEFAULT, "flistxattr");
  69 
  70     clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
  71     CHECK_NULL(clazz);
  72     entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
  73     CHECK_NULL(entry_name);
  74     entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B");
  75     CHECK_NULL(entry_dir);
  76     entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B");
  77     CHECK_NULL(entry_fstype);
  78     entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B");
  79     CHECK_NULL(entry_options);
  80 }
  81 
  82 JNIEXPORT jint JNICALL
  83 Java_sun_nio_fs_LinuxNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz,
  84     jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
  85 {
  86     size_t res = -1;
  87     const char* name = jlong_to_ptr(nameAddress);
  88     void* value = jlong_to_ptr(valueAddress);
  89 
  90     if (my_fgetxattr_func == NULL) {
  91         errno = ENOTSUP;
  92     } else {
  93         /* EINTR not documented */
  94         res = (*my_fgetxattr_func)(fd, name, value, valueLen);
  95     }
  96     if (res == (size_t)-1)
  97         throwUnixException(env, errno);
  98     return (jint)res;
  99 }
 100 
 101 JNIEXPORT void JNICALL
 102 Java_sun_nio_fs_LinuxNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz,
 103     jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
 104 {
 105     int res = -1;
 106     const char* name = jlong_to_ptr(nameAddress);
 107     void* value = jlong_to_ptr(valueAddress);
 108 
 109     if (my_fsetxattr_func == NULL) {
 110         errno = ENOTSUP;
 111     } else {
 112         /* EINTR not documented */
 113         res = (*my_fsetxattr_func)(fd, name, value, valueLen, 0);
 114     }
 115     if (res == -1)
 116         throwUnixException(env, errno);
 117 }
 118 
 119 JNIEXPORT void JNICALL
 120 Java_sun_nio_fs_LinuxNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz,
 121     jint fd, jlong nameAddress)
 122 {
 123     int res = -1;
 124     const char* name = jlong_to_ptr(nameAddress);
 125 
 126     if (my_fremovexattr_func == NULL) {
 127         errno = ENOTSUP;
 128     } else {
 129         /* EINTR not documented */
 130         res = (*my_fremovexattr_func)(fd, name);
 131     }
 132     if (res == -1)
 133         throwUnixException(env, errno);
 134 }
 135 
 136 JNIEXPORT jint JNICALL
 137 Java_sun_nio_fs_LinuxNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz,
 138     jint fd, jlong listAddress, jint size)
 139 {
 140     size_t res = -1;
 141     char* list = jlong_to_ptr(listAddress);
 142 
 143     if (my_flistxattr_func == NULL) {
 144         errno = ENOTSUP;
 145     } else {
 146         /* EINTR not documented */
 147         res = (*my_flistxattr_func)(fd, list, (size_t)size);
 148     }
 149     if (res == (size_t)-1)
 150         throwUnixException(env, errno);
 151     return (jint)res;
 152 }
 153 
 154 JNIEXPORT jlong JNICALL
 155 Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0(JNIEnv* env, jclass this, jlong pathAddress,
 156                                                  jlong modeAddress)
 157 {
 158     FILE* fp = NULL;
 159     const char* path = (const char*)jlong_to_ptr(pathAddress);
 160     const char* mode = (const char*)jlong_to_ptr(modeAddress);
 161 
 162     do {
 163         fp = setmntent(path, mode);
 164     } while (fp == NULL && errno == EINTR);
 165     if (fp == NULL) {
 166         throwUnixException(env, errno);
 167     }
 168     return ptr_to_jlong(fp);
 169 }
 170 
 171 JNIEXPORT jint JNICALL
 172 Java_sun_nio_fs_LinuxNativeDispatcher_getmntent0(JNIEnv* env, jclass this,
 173     jlong value, jobject entry, jlong buffer, jint bufLen)
 174 {
 175     struct mntent ent;
 176     char * buf = (char*)jlong_to_ptr(buffer);
 177     struct mntent* m;
 178     FILE* fp = jlong_to_ptr(value);
 179     jsize len;
 180     jbyteArray bytes;
 181     char* name;
 182     char* dir;
 183     char* fstype;
 184     char* options;
 185 
 186     m = getmntent_r(fp, &ent, buf, (int)bufLen);
 187     if (m == NULL)
 188         return -1;
 189     name = m->mnt_fsname;
 190     dir = m->mnt_dir;
 191     fstype = m->mnt_type;
 192     options = m->mnt_opts;
 193 
 194     len = strlen(name);
 195     bytes = (*env)->NewByteArray(env, len);
 196     if (bytes == NULL)
 197         return -1;
 198     (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)name);
 199     (*env)->SetObjectField(env, entry, entry_name, bytes);
 200 
 201     len = strlen(dir);
 202     bytes = (*env)->NewByteArray(env, len);
 203     if (bytes == NULL)
 204         return -1;
 205     (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)dir);
 206     (*env)->SetObjectField(env, entry, entry_dir, bytes);
 207 
 208     len = strlen(fstype);
 209     bytes = (*env)->NewByteArray(env, len);
 210     if (bytes == NULL)
 211         return -1;
 212     (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)fstype);
 213     (*env)->SetObjectField(env, entry, entry_fstype, bytes);
 214 
 215     len = strlen(options);
 216     bytes = (*env)->NewByteArray(env, len);
 217     if (bytes == NULL)
 218         return -1;
 219     (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)options);
 220     (*env)->SetObjectField(env, entry, entry_options, bytes);
 221 
 222     return 0;
 223 }
 224 
 225 JNIEXPORT void JNICALL
 226 Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream)
 227 {
 228     FILE* fp = jlong_to_ptr(stream);
 229     /* FIXME - man page doesn't explain how errors are returned */
 230     endmntent(fp);
 231 }
 232 
 233 /**
 234  * This function returns line length without NUL terminator or -1 on EOF.
 235  */
 236 JNIEXPORT jint JNICALL
 237 Java_sun_nio_fs_LinuxNativeDispatcher_getlinelen(JNIEnv* env, jclass this, jlong stream)
 238 {
 239     FILE* fp = jlong_to_ptr(stream);
 240     size_t lineSize = 0;
 241     char * lineBuffer = NULL;
 242     int saved_errno;
 243 
 244     ssize_t res = getline(&lineBuffer, &lineSize, fp);
 245     saved_errno = errno;
 246 
 247     /* Should free lineBuffer no matter result, according to man page */
 248     if (lineBuffer != NULL)
 249         free(lineBuffer);
 250 
 251     if (feof(fp))
 252         return -1;
 253 
 254     /* On successfull return res >= 0, otherwise res is -1 */
 255     if (res == -1)
 256         throwUnixException(env, saved_errno);
 257 
 258     if (res > INT_MAX)
 259         throwUnixException(env, EOVERFLOW);
 260 
 261     return (jint)res;
 262 }
 263