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(JVM_NativePath((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 JNIEXPORT jboolean JNICALL
    137 Java_java_io_UnixFileSystem_checkAccess0(JNIEnv *env, jobject this,
    138                                          jobject file, jint a)
    139 {
    140     jboolean rv = JNI_FALSE;
    141     int mode = 0;
    142     switch (a) {
    143     case java_io_FileSystem_ACCESS_OK:
    144         mode = F_OK;
    145         break;
    146     case java_io_FileSystem_ACCESS_READ:
    147         mode = R_OK;
    148         break;
    149     case java_io_FileSystem_ACCESS_WRITE:
    150         mode = W_OK;
    151         break;
    152     case java_io_FileSystem_ACCESS_EXECUTE:
    153         mode = X_OK;
    154         break;
    155     default: assert(0);
    156     }
    157     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    158         if (access(path, mode) == 0) {
    159             rv = JNI_TRUE;
    160         }
    161     } END_PLATFORM_STRING(env, path);
    162     return rv;
    163 }
    164 
    165 
    166 JNIEXPORT jboolean JNICALL
    167 Java_java_io_UnixFileSystem_setPermission0(JNIEnv *env, jobject this,
    168                                            jobject file,
    169                                            jint access,
    170                                            jboolean enable,
    171                                            jboolean owneronly)
    172 {
    173     jboolean rv = JNI_FALSE;
    174 
    175     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    176         int amode = 0;
    177         int mode;
    178         switch (access) {
    179         case java_io_FileSystem_ACCESS_READ:
    180             if (owneronly)
    181                 amode = S_IRUSR;
    182             else
    183                 amode = S_IRUSR | S_IRGRP | S_IROTH;
    184             break;
    185         case java_io_FileSystem_ACCESS_WRITE:
    186             if (owneronly)
    187                 amode = S_IWUSR;
    188             else
    189                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
    190             break;
    191         case java_io_FileSystem_ACCESS_EXECUTE:
    192             if (owneronly)
    193                 amode = S_IXUSR;
    194             else
    195                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
    196             break;
    197         default:
    198             assert(0);
    199         }
    200         if (statMode(path, &mode)) {
    201             if (enable)
    202                 mode |= amode;
    203             else
    204                 mode &= ~amode;
    205             if (chmod(path, mode) >= 0) {
    206                 rv = JNI_TRUE;
    207             }
    208         }
    209     } END_PLATFORM_STRING(env, path);
    210     return rv;
    211 }
    212 
    213 JNIEXPORT jlong JNICALL
    214 Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv *env, jobject this,
    215                                                  jobject file)
    216 {
    217     jlong rv = 0;
    218 
    219     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    220         struct stat64 sb;
    221         if (stat64(path, &sb) == 0) {
    222             rv = 1000 * (jlong)sb.st_mtime;
    223         }
    224     } END_PLATFORM_STRING(env, path);
    225     return rv;
    226 }
    227 
    228 
    229 JNIEXPORT jlong JNICALL
    230 Java_java_io_UnixFileSystem_getLength0(JNIEnv *env, jobject this,
    231                                        jobject file)
    232 {
    233     jlong rv = 0;
    234 
    235     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    236         struct stat64 sb;
    237         if (stat64(path, &sb) == 0) {
    238             rv = sb.st_size;
    239         }
    240     } END_PLATFORM_STRING(env, path);
    241     return rv;
    242 }
    243 
    244 
    245 /* -- File operations -- */
    246 
    247 
    248 JNIEXPORT jboolean JNICALL
    249 Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls,
    250                                                    jstring pathname)
    251 {
    252     jboolean rv = JNI_FALSE;
    253 
    254     WITH_PLATFORM_STRING(env, pathname, path) {
    255         int fd;
    256         if (!strcmp (path, "/")) {
    257             fd = JVM_EEXIST;    /* The root directory always exists */
    258         } else {
    259             fd = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666);
    260         }
    261         if (fd < 0) {
    262             if (fd != JVM_EEXIST) {
    263                 JNU_ThrowIOExceptionWithLastError(env, path);
    264             }
    265         } else {
    266             JVM_Close(fd);
    267             rv = JNI_TRUE;
    268         }
    269     } END_PLATFORM_STRING(env, path);
    270     return rv;
    271 }
    272 
    273 
    274 JNIEXPORT jboolean JNICALL
    275 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
    276                                     jobject file)
    277 {
    278     jboolean rv = JNI_FALSE;
    279 
    280     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    281         if (remove(path) == 0) {
    282             rv = JNI_TRUE;
    283         }
    284     } END_PLATFORM_STRING(env, path);
    285     return rv;
    286 }
    287 
    288 
    289 JNIEXPORT jobjectArray JNICALL
    290 Java_java_io_UnixFileSystem_list0(JNIEnv *env, jobject this,
    291                                   jobject file)
    292 {
    293     DIR *dir = NULL;
    294     struct dirent64 *ptr;
    295     struct dirent64 *result;
    296     int len, maxlen;
    297     jobjectArray rv, old;
    298 
    299     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    300         dir = opendir(path);
    301     } END_PLATFORM_STRING(env, path);
    302     if (dir == NULL) return NULL;
    303 
    304     ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
    305     if (ptr == NULL) {
    306         JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
    307         closedir(dir);
    308         return NULL;
    309     }
    310 
    311     /* Allocate an initial String array */
    312     len = 0;
    313     maxlen = 16;
    314     rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
    315     if (rv == NULL) goto error;
    316 
    317     /* Scan the directory */
    318     while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
    319         jstring name;
    320         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
    321             continue;
    322         if (len == maxlen) {
    323             old = rv;
    324             rv = (*env)->NewObjectArray(env, maxlen <<= 1,
    325                                         JNU_ClassString(env), NULL);
    326             if (rv == NULL) goto error;
    327             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
    328             (*env)->DeleteLocalRef(env, old);
    329         }
    330 #ifdef MACOSX
    331         name = newStringPlatform(env, ptr->d_name);
    332 #else
    333         name = JNU_NewStringPlatform(env, ptr->d_name);
    334 #endif
    335         if (name == NULL) goto error;
    336         (*env)->SetObjectArrayElement(env, rv, len++, name);
    337         (*env)->DeleteLocalRef(env, name);
    338     }
    339     closedir(dir);
    340     free(ptr);
    341 
    342     /* Copy the final results into an appropriately-sized array */
    343     old = rv;
    344     rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
    345     if (rv == NULL) {
    346         return NULL;
    347     }
    348     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
    349         return NULL;
    350     }
    351     return rv;
    352 
    353  error:
    354     closedir(dir);
    355     free(ptr);
    356     return NULL;
    357 }
    358 
    359 
    360 JNIEXPORT jboolean JNICALL
    361 Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this,
    362                                              jobject file)
    363 {
    364     jboolean rv = JNI_FALSE;
    365 
    366     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    367         if (mkdir(path, 0777) == 0) {
    368             rv = JNI_TRUE;
    369         }
    370     } END_PLATFORM_STRING(env, path);
    371     return rv;
    372 }
    373 
    374 
    375 JNIEXPORT jboolean JNICALL
    376 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
    377                                     jobject from, jobject to)
    378 {
    379     jboolean rv = JNI_FALSE;
    380 
    381     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
    382         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
    383             if (rename(fromPath, toPath) == 0) {
    384                 rv = JNI_TRUE;
    385             }
    386         } END_PLATFORM_STRING(env, toPath);
    387     } END_PLATFORM_STRING(env, fromPath);
    388     return rv;
    389 }
    390 
    391 JNIEXPORT jboolean JNICALL
    392 Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this,
    393                                                  jobject file, jlong time)
    394 {
    395     jboolean rv = JNI_FALSE;
    396 
    397     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    398         struct stat64 sb;
    399 
    400         if (stat64(path, &sb) == 0) {
    401             struct timeval tv[2];
    402 
    403             /* Preserve access time */
    404             tv[0].tv_sec = sb.st_atime;
    405             tv[0].tv_usec = 0;
    406 
    407             /* Change last-modified time */
    408             tv[1].tv_sec = time / 1000;
    409             tv[1].tv_usec = (time % 1000) * 1000;
    410 
    411             if (utimes(path, tv) == 0)
    412                 rv = JNI_TRUE;
    413         }
    414     } END_PLATFORM_STRING(env, path);
    415 
    416     return rv;
    417 }
    418 
    419 
    420 JNIEXPORT jboolean JNICALL
    421 Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this,
    422                                          jobject file)
    423 {
    424     jboolean rv = JNI_FALSE;
    425 
    426     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    427         int mode;
    428         if (statMode(path, &mode)) {
    429             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
    430                 rv = JNI_TRUE;
    431             }
    432         }
    433     } END_PLATFORM_STRING(env, path);
    434     return rv;
    435 }
    436 
    437 JNIEXPORT jlong JNICALL
    438 Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
    439                                       jobject file, jint t)
    440 {
    441     jlong rv = 0L;
    442 
    443     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
    444         struct statvfs64 fsstat;
    445         memset(&fsstat, 0, sizeof(fsstat));
    446         if (statvfs64(path, &fsstat) == 0) {
    447             switch(t) {
    448             case java_io_FileSystem_SPACE_TOTAL:
    449                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
    450                                long_to_jlong(fsstat.f_blocks));
    451                 break;
    452             case java_io_FileSystem_SPACE_FREE:
    453                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
    454                                long_to_jlong(fsstat.f_bfree));
    455                 break;
    456             case java_io_FileSystem_SPACE_USABLE:
    457                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
    458                                long_to_jlong(fsstat.f_bavail));
    459                 break;
    460             default:
    461                 assert(0);
    462             }
    463         }
    464     } END_PLATFORM_STRING(env, path);
    465     return rv;
    466 }
    467 
    468 static JNINativeMethod gMethods[] = {
    469     NATIVE_METHOD(UnixFileSystem, initIDs, "()V"),
    470     NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"),
    471     NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"),
    472     NATIVE_METHOD(UnixFileSystem, checkAccess0, "(Ljava/io/File;I)Z"),
    473     NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"),
    474     NATIVE_METHOD(UnixFileSystem, getLastModifiedTime0, "(Ljava/io/File;)J"),
    475     NATIVE_METHOD(UnixFileSystem, getLength0, "(Ljava/io/File;)J"),
    476     NATIVE_METHOD(UnixFileSystem, createFileExclusively0, "(Ljava/lang/String;)Z"),
    477     NATIVE_METHOD(UnixFileSystem, delete0, "(Ljava/io/File;)Z"),
    478     NATIVE_METHOD(UnixFileSystem, list0, "(Ljava/io/File;)[Ljava/lang/String;"),
    479     NATIVE_METHOD(UnixFileSystem, createDirectory0, "(Ljava/io/File;)Z"),
    480     NATIVE_METHOD(UnixFileSystem, rename0, "(Ljava/io/File;Ljava/io/File;)Z"),
    481     NATIVE_METHOD(UnixFileSystem, setLastModifiedTime0, "(Ljava/io/File;J)Z"),
    482     NATIVE_METHOD(UnixFileSystem, setReadOnly0, "(Ljava/io/File;)Z"),
    483     NATIVE_METHOD(UnixFileSystem, getSpace0, "(Ljava/io/File;I)J"),
    484 };
    485 
    486 void register_java_io_UnixFileSystem(JNIEnv* env) {
    487     jniRegisterNativeMethods(env, "java/io/UnixFileSystem", gMethods, NELEM(gMethods));
    488 }
    489