Home | History | Annotate | Download | only in native
      1 /*
      2  * Copyright (c) 1998, 2010, 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 <assert.h>
     27 #include <sys/types.h>
     28 #include <sys/time.h>
     29 #include <sys/stat.h>
     30 #include <sys/statvfs.h>
     31 #include <string.h>
     32 #include <stdlib.h>
     33 #include <dlfcn.h>
     34 #include <limits.h>
     35 
     36 #include "jni.h"
     37 #include "jni_util.h"
     38 #include "jlong.h"
     39 #include "jvm.h"
     40 #include "io_util.h"
     41 #include "io_util_md.h"
     42 #include "java_io_FileSystem.h"
     43 #include "java_io_UnixFileSystem.h"
     44 
     45 #include "JNIHelp.h"
     46 
     47 #if defined(_ALLBSD_SOURCE)
     48 #define dirent64 dirent
     49 #define readdir64_r readdir_r
     50 #define stat64 stat
     51 #define statvfs64 statvfs
     52 #endif
     53 
     54 /* -- Field IDs -- */
     55 
     56 static struct {
     57     jfieldID path;
     58 } ids;
     59 
     60 
     61 #define NATIVE_METHOD(className, functionName, signature) \
     62 { #functionName, signature, (void*)(Java_java_io_ ## className ## _ ## functionName) }
     63 
     64 JNIEXPORT void JNICALL
     65 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
     66 {
     67     jclass fileClass = (*env)->FindClass(env, "java/io/File");
     68     if (!fileClass) return;
     69     ids.path = (*env)->GetFieldID(env, fileClass,
     70                                   "path", "Ljava/lang/String;");
     71 }
     72 
     73 /* -- Path operations -- */
     74 
     75 extern int canonicalize(char *path, const char *out, int len);
     76 
     77 JNIEXPORT jstring JNICALL
     78 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
     79                                           jstring pathname)
     80 {
     81     jstring rv = NULL;
     82 
     83     WITH_PLATFORM_STRING(env, pathname, path) {
     84         char canonicalPath[JVM_MAXPATHLEN];
     85         if (canonicalize((char *)path,
     86                          canonicalPath, JVM_MAXPATHLEN) < 0) {
     87             JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
     88         } else {
     89 #ifdef MACOSX
     90             rv = newStringPlatform(env, canonicalPath);
     91 #else
     92             rv = JNU_NewStringPlatform(env, canonicalPath);
     93 #endif
     94         }
     95     } END_PLATFORM_STRING(env, path);
     96     return rv;
     97 }
     98 
     99 
    100 /* -- Attribute accessors -- */
    101 
    102 
    103 static jboolean
    104 statMode(const char *path, int *mode)
    105 {
    106     struct stat64 sb;
    107     if (stat64(path, &sb) == 0) {
    108         *mode = sb.st_mode;
    109         return JNI_TRUE;
    110     }
    111     return JNI_FALSE;
    112 }
    113 
    114 
    115 JNIEXPORT jint JNICALL
    116 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
    117                                                   jstring abspath)
    118 {
    119     jint rv = 0;
    120 
    121     /* ----- BEGIN android -----
    122     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {*/
    123     WITH_PLATFORM_STRING(env, abspath, path) {
    124     // ----- END android -----
    125         int mode;
    126         if (statMode(path, &mode)) {
    127             int fmt = mode & S_IFMT;
    128             rv = (jint) (java_io_FileSystem_BA_EXISTS
    129                   | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
    130                   | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
    131         }
    132     } END_PLATFORM_STRING(env, path);
    133     return rv;
    134 }
    135 
    136 // Android-changed: Name changed because of added thread policy check
    137 JNIEXPORT jboolean JNICALL
    138 Java_java_io_UnixFileSystem_checkAccess0(JNIEnv *env, jobject this,
    139                                          jobject file, jint a)
    140 {
    141     jboolean rv = JNI_FALSE;
    142     int mode = 0;
    143     switch (a) {
    144     // Android-changed: Added ACCESS_OK case
    145     case java_io_FileSystem_ACCESS_OK:
    146         mode = F_OK;
    147         break;
    148     case java_io_FileSystem_ACCESS_READ:
    149         mode = R_OK;
    150         break;
    151     case java_io_FileSystem_ACCESS_WRITE:
    152         mode = W_OK;
    153         break;
    154     case java_io_FileSystem_ACCESS_EXECUTE:
    155         mode = X_OK;
    156         break;
    157     default: assert(0);
    158     }
    159     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    160         if (access(path, mode) == 0) {
    161             rv = JNI_TRUE;
    162         }
    163     } END_PLATFORM_STRING(env, path);
    164     return rv;
    165 }
    166 
    167 // Android-changed: Name changed because of added thread policy check
    168 JNIEXPORT jboolean JNICALL
    169 Java_java_io_UnixFileSystem_setPermission0(JNIEnv *env, jobject this,
    170                                            jobject file,
    171                                            jint access,
    172                                            jboolean enable,
    173                                            jboolean owneronly)
    174 {
    175     jboolean rv = JNI_FALSE;
    176 
    177     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    178         int amode = 0;
    179         int mode;
    180         switch (access) {
    181         case java_io_FileSystem_ACCESS_READ:
    182             if (owneronly)
    183                 amode = S_IRUSR;
    184             else
    185                 amode = S_IRUSR | S_IRGRP | S_IROTH;
    186             break;
    187         case java_io_FileSystem_ACCESS_WRITE:
    188             if (owneronly)
    189                 amode = S_IWUSR;
    190             else
    191                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
    192             break;
    193         case java_io_FileSystem_ACCESS_EXECUTE:
    194             if (owneronly)
    195                 amode = S_IXUSR;
    196             else
    197                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
    198             break;
    199         default:
    200             assert(0);
    201         }
    202         if (statMode(path, &mode)) {
    203             if (enable)
    204                 mode |= amode;
    205             else
    206                 mode &= ~amode;
    207             if (chmod(path, mode) >= 0) {
    208                 rv = JNI_TRUE;
    209             }
    210         }
    211     } END_PLATFORM_STRING(env, path);
    212     return rv;
    213 }
    214 
    215 // Android-changed: Name changed because of added thread policy check
    216 JNIEXPORT jlong JNICALL
    217 Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv *env, jobject this,
    218                                                  jobject file)
    219 {
    220     jlong rv = 0;
    221 
    222     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    223         struct stat64 sb;
    224         if (stat64(path, &sb) == 0) {
    225             rv = 1000 * (jlong)sb.st_mtime;
    226         }
    227     } END_PLATFORM_STRING(env, path);
    228     return rv;
    229 }
    230 
    231 // Android-changed: Name changed because of added thread policy check
    232 JNIEXPORT jlong JNICALL
    233 Java_java_io_UnixFileSystem_getLength0(JNIEnv *env, jobject this,
    234                                        jobject file)
    235 {
    236     jlong rv = 0;
    237 
    238     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    239         struct stat64 sb;
    240         if (stat64(path, &sb) == 0) {
    241             rv = sb.st_size;
    242         }
    243     } END_PLATFORM_STRING(env, path);
    244     return rv;
    245 }
    246 
    247 
    248 /* -- File operations -- */
    249 
    250 // Android-changed: Name changed because of added thread policy check
    251 JNIEXPORT jboolean JNICALL
    252 Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls,
    253                                                    jstring pathname)
    254 {
    255     jboolean rv = JNI_FALSE;
    256 
    257     WITH_PLATFORM_STRING(env, pathname, path) {
    258         FD fd;
    259         /* The root directory always exists */
    260         if (strcmp (path, "/")) {
    261             fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
    262             if (fd < 0) {
    263                 if (errno != EEXIST)
    264                     JNU_ThrowIOExceptionWithLastError(env, path);
    265             } else {
    266                 if (close(fd) == -1)
    267                     JNU_ThrowIOExceptionWithLastError(env, path);
    268                 rv = JNI_TRUE;
    269             }
    270         }
    271     } END_PLATFORM_STRING(env, path);
    272     return rv;
    273 }
    274 
    275 
    276 JNIEXPORT jboolean JNICALL
    277 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
    278                                     jobject file)
    279 {
    280     jboolean rv = JNI_FALSE;
    281 
    282     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    283         if (remove(path) == 0) {
    284             rv = JNI_TRUE;
    285         }
    286     } END_PLATFORM_STRING(env, path);
    287     return rv;
    288 }
    289 
    290 // Android-changed: Name changed because of added thread policy check
    291 JNIEXPORT jobjectArray JNICALL
    292 Java_java_io_UnixFileSystem_list0(JNIEnv *env, jobject this,
    293                                   jobject file)
    294 {
    295     DIR *dir = NULL;
    296     struct dirent64 *ptr;
    297     struct dirent64 *result;
    298     int len, maxlen;
    299     jobjectArray rv, old;
    300     jclass str_class;
    301 
    302     str_class = JNU_ClassString(env);
    303     CHECK_NULL_RETURN(str_class, NULL);
    304 
    305 
    306     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    307         dir = opendir(path);
    308     } END_PLATFORM_STRING(env, path);
    309     if (dir == NULL) return NULL;
    310 
    311     ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
    312     if (ptr == NULL) {
    313         JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
    314         closedir(dir);
    315         return NULL;
    316     }
    317 
    318     /* Allocate an initial String array */
    319     len = 0;
    320     maxlen = 16;
    321     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
    322     if (rv == NULL) goto error;
    323 
    324     /* Scan the directory */
    325     while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
    326         jstring name;
    327         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
    328             continue;
    329         if (len == maxlen) {
    330             old = rv;
    331             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
    332             if (rv == NULL) goto error;
    333             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
    334             (*env)->DeleteLocalRef(env, old);
    335         }
    336 #ifdef MACOSX
    337         name = newStringPlatform(env, ptr->d_name);
    338 #else
    339         name = JNU_NewStringPlatform(env, ptr->d_name);
    340 #endif
    341         if (name == NULL) goto error;
    342         (*env)->SetObjectArrayElement(env, rv, len++, name);
    343         (*env)->DeleteLocalRef(env, name);
    344     }
    345     closedir(dir);
    346     free(ptr);
    347 
    348     /* Copy the final results into an appropriately-sized array */
    349     old = rv;
    350     rv = (*env)->NewObjectArray(env, len, str_class, NULL);
    351     if (rv == NULL) {
    352         return NULL;
    353     }
    354     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
    355         return NULL;
    356     }
    357     return rv;
    358 
    359  error:
    360     closedir(dir);
    361     free(ptr);
    362     return NULL;
    363 }
    364 
    365 // Android-changed: Name changed because of added thread policy check
    366 JNIEXPORT jboolean JNICALL
    367 Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this,
    368                                              jobject file)
    369 {
    370     jboolean rv = JNI_FALSE;
    371 
    372     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    373         if (mkdir(path, 0777) == 0) {
    374             rv = JNI_TRUE;
    375         }
    376     } END_PLATFORM_STRING(env, path);
    377     return rv;
    378 }
    379 
    380 
    381 JNIEXPORT jboolean JNICALL
    382 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
    383                                     jobject from, jobject to)
    384 {
    385     jboolean rv = JNI_FALSE;
    386 
    387     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
    388         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
    389             if (rename(fromPath, toPath) == 0) {
    390                 rv = JNI_TRUE;
    391             }
    392         } END_PLATFORM_STRING(env, toPath);
    393     } END_PLATFORM_STRING(env, fromPath);
    394     return rv;
    395 }
    396 
    397 // Android-changed: Name changed because of added thread policy check
    398 JNIEXPORT jboolean JNICALL
    399 Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this,
    400                                                  jobject file, jlong time)
    401 {
    402     jboolean rv = JNI_FALSE;
    403 
    404     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    405         struct stat64 sb;
    406 
    407         if (stat64(path, &sb) == 0) {
    408             struct timeval tv[2];
    409 
    410             /* Preserve access time */
    411             tv[0].tv_sec = sb.st_atime;
    412             tv[0].tv_usec = 0;
    413 
    414             /* Change last-modified time */
    415             tv[1].tv_sec = time / 1000;
    416             tv[1].tv_usec = (time % 1000) * 1000;
    417 
    418             if (utimes(path, tv) == 0)
    419                 rv = JNI_TRUE;
    420         }
    421     } END_PLATFORM_STRING(env, path);
    422 
    423     return rv;
    424 }
    425 
    426 // Android-changed: Name changed because of added thread policy check
    427 JNIEXPORT jboolean JNICALL
    428 Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this,
    429                                          jobject file)
    430 {
    431     jboolean rv = JNI_FALSE;
    432 
    433     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    434         int mode;
    435         if (statMode(path, &mode)) {
    436             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
    437                 rv = JNI_TRUE;
    438             }
    439         }
    440     } END_PLATFORM_STRING(env, path);
    441     return rv;
    442 }
    443 
    444 // Android-changed: Name changed because of added thread policy check
    445 JNIEXPORT jlong JNICALL
    446 Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
    447                                       jobject file, jint t)
    448 {
    449     jlong rv = 0L;
    450 
    451     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    452         struct statvfs64 fsstat;
    453         memset(&fsstat, 0, sizeof(fsstat));
    454         if (statvfs64(path, &fsstat) == 0) {
    455             switch(t) {
    456             case java_io_FileSystem_SPACE_TOTAL:
    457                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
    458                                long_to_jlong(fsstat.f_blocks));
    459                 break;
    460             case java_io_FileSystem_SPACE_FREE:
    461                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
    462                                long_to_jlong(fsstat.f_bfree));
    463                 break;
    464             case java_io_FileSystem_SPACE_USABLE:
    465                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
    466                                long_to_jlong(fsstat.f_bavail));
    467                 break;
    468             default:
    469                 assert(0);
    470             }
    471         }
    472     } END_PLATFORM_STRING(env, path);
    473     return rv;
    474 }
    475 
    476 static JNINativeMethod gMethods[] = {
    477     NATIVE_METHOD(UnixFileSystem, initIDs, "()V"),
    478     NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"),
    479     NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"),
    480     NATIVE_METHOD(UnixFileSystem, checkAccess0, "(Ljava/io/File;I)Z"),
    481     NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"),
    482     NATIVE_METHOD(UnixFileSystem, getLastModifiedTime0, "(Ljava/io/File;)J"),
    483     NATIVE_METHOD(UnixFileSystem, getLength0, "(Ljava/io/File;)J"),
    484     NATIVE_METHOD(UnixFileSystem, createFileExclusively0, "(Ljava/lang/String;)Z"),
    485     NATIVE_METHOD(UnixFileSystem, delete0, "(Ljava/io/File;)Z"),
    486     NATIVE_METHOD(UnixFileSystem, list0, "(Ljava/io/File;)[Ljava/lang/String;"),
    487     NATIVE_METHOD(UnixFileSystem, createDirectory0, "(Ljava/io/File;)Z"),
    488     NATIVE_METHOD(UnixFileSystem, rename0, "(Ljava/io/File;Ljava/io/File;)Z"),
    489     NATIVE_METHOD(UnixFileSystem, setLastModifiedTime0, "(Ljava/io/File;J)Z"),
    490     NATIVE_METHOD(UnixFileSystem, setReadOnly0, "(Ljava/io/File;)Z"),
    491     NATIVE_METHOD(UnixFileSystem, getSpace0, "(Ljava/io/File;I)J"),
    492 };
    493 
    494 void register_java_io_UnixFileSystem(JNIEnv* env) {
    495     jniRegisterNativeMethods(env, "java/io/UnixFileSystem", gMethods, NELEM(gMethods));
    496 }
    497