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 "java_io_FileSystem.h"
  42 #include "java_io_UnixFileSystem.h"
  43 
  44 
  45 /* -- Field IDs -- */
  46 
  47 static struct {
  48     jfieldID path;
  49 } ids;
  50 
  51 
  52 JNIEXPORT void JNICALL
  53 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
  54 {
  55     jclass fileClass = (*env)->FindClass(env, "java/io/File");
  56     if (!fileClass) return;
  57     ids.path = (*env)->GetFieldID(env, fileClass,
  58                                   "path", "Ljava/lang/String;");
  59 }
  60 
  61 /* -- Path operations -- */
  62 
  63 extern int canonicalize(char *path, const char *out, int len);
  64 
  65 JNIEXPORT jstring JNICALL
  66 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
  67                                           jstring pathname)
  68 {
  69     jstring rv = NULL;
  70 
  71     WITH_PLATFORM_STRING(env, pathname, path) {
  72         char canonicalPath[JVM_MAXPATHLEN];
  73         if (canonicalize(JVM_NativePath((char *)path),
  74                          canonicalPath, JVM_MAXPATHLEN) < 0) {
  75             JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
  76         } else {
  77             rv = JNU_NewStringPlatform(env, canonicalPath);
  78         }
  79     } END_PLATFORM_STRING(env, path);
  80     return rv;
  81 }
  82 
  83 
  84 /* -- Attribute accessors -- */
  85 
  86 
  87 static jboolean
  88 statMode(const char *path, int *mode)
  89 {
  90     struct stat64 sb;
  91     if (stat64(path, &sb) == 0) {
  92         *mode = sb.st_mode;
  93         return JNI_TRUE;
  94     }
  95     return JNI_FALSE;
  96 }
  97 
  98 
  99 JNIEXPORT jint JNICALL
 100 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
 101                                                   jobject file)
 102 {
 103     jint rv = 0;
 104 
 105     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 106         int mode;
 107         if (statMode(path, &mode)) {
 108             int fmt = mode & S_IFMT;
 109             rv = (jint) (java_io_FileSystem_BA_EXISTS
 110                   | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
 111                   | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
 112         }
 113     } END_PLATFORM_STRING(env, path);
 114     return rv;
 115 }
 116 
 117 JNIEXPORT jboolean JNICALL
 118 Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
 119                                         jobject file, jint a)
 120 {
 121     jboolean rv = JNI_FALSE;
 122     int mode = 0;
 123     switch (a) {
 124     case java_io_FileSystem_ACCESS_READ:
 125         mode = R_OK;
 126         break;
 127     case java_io_FileSystem_ACCESS_WRITE:
 128         mode = W_OK;
 129         break;
 130     case java_io_FileSystem_ACCESS_EXECUTE:
 131         mode = X_OK;
 132         break;
 133     default: assert(0);
 134     }
 135     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 136         if (access(path, mode) == 0) {
 137             rv = JNI_TRUE;
 138         }
 139     } END_PLATFORM_STRING(env, path);
 140     return rv;
 141 }
 142 
 143 
 144 JNIEXPORT jboolean JNICALL
 145 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
 146                                           jobject file,
 147                                           jint access,
 148                                           jboolean enable,
 149                                           jboolean owneronly)
 150 {
 151     jboolean rv = JNI_FALSE;
 152 
 153     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 154         int amode = 0;
 155         int mode;
 156         switch (access) {
 157         case java_io_FileSystem_ACCESS_READ:
 158             if (owneronly)
 159                 amode = S_IRUSR;
 160             else
 161                 amode = S_IRUSR | S_IRGRP | S_IROTH;
 162             break;
 163         case java_io_FileSystem_ACCESS_WRITE:
 164             if (owneronly)
 165                 amode = S_IWUSR;
 166             else
 167                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
 168             break;
 169         case java_io_FileSystem_ACCESS_EXECUTE:
 170             if (owneronly)
 171                 amode = S_IXUSR;
 172             else
 173                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
 174             break;
 175         default:
 176             assert(0);
 177         }
 178         if (statMode(path, &mode)) {
 179             if (enable)
 180                 mode |= amode;
 181             else
 182                 mode &= ~amode;
 183             if (chmod(path, mode) >= 0) {
 184                 rv = JNI_TRUE;
 185             }
 186         }
 187     } END_PLATFORM_STRING(env, path);
 188     return rv;
 189 }
 190 
 191 JNIEXPORT jlong JNICALL
 192 Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
 193                                                 jobject file)
 194 {
 195     jlong rv = 0;
 196 
 197     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 198         struct stat64 sb;
 199         if (stat64(path, &sb) == 0) {
 200             rv = 1000 * (jlong)sb.st_mtime;
 201         }
 202     } END_PLATFORM_STRING(env, path);
 203     return rv;
 204 }
 205 
 206 
 207 JNIEXPORT jlong JNICALL
 208 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
 209                                       jobject file)
 210 {
 211     jlong rv = 0;
 212 
 213     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 214         struct stat64 sb;
 215         if (stat64(path, &sb) == 0) {
 216             rv = sb.st_size;
 217         }
 218     } END_PLATFORM_STRING(env, path);
 219     return rv;
 220 }
 221 
 222 
 223 /* -- File operations -- */
 224 
 225 
 226 JNIEXPORT jboolean JNICALL
 227 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
 228                                                   jstring pathname)
 229 {
 230     jboolean rv = JNI_FALSE;
 231 
 232     WITH_PLATFORM_STRING(env, pathname, path) {
 233         int fd;
 234         if (!strcmp (path, "/")) {
 235             fd = JVM_EEXIST;    /* The root directory always exists */
 236         } else {
 237             fd = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666);
 238         }
 239         if (fd < 0) {
 240             if (fd != JVM_EEXIST) {
 241                 JNU_ThrowIOExceptionWithLastError(env, path);
 242             }
 243         } else {
 244             JVM_Close(fd);
 245             rv = JNI_TRUE;
 246         }
 247     } END_PLATFORM_STRING(env, path);
 248     return rv;
 249 }
 250 
 251 
 252 JNIEXPORT jboolean JNICALL
 253 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
 254                                     jobject file)
 255 {
 256     jboolean rv = JNI_FALSE;
 257 
 258     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 259         if (remove(path) == 0) {
 260             rv = JNI_TRUE;
 261         }
 262     } END_PLATFORM_STRING(env, path);
 263     return rv;
 264 }
 265 
 266 
 267 JNIEXPORT jobjectArray JNICALL
 268 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
 269                                  jobject file)
 270 {
 271     DIR *dir = NULL;
 272     struct dirent64 *ptr;
 273     struct dirent64 *result;
 274     int len, maxlen;
 275     jobjectArray rv, old;
 276 
 277     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 278         dir = opendir(path);
 279     } END_PLATFORM_STRING(env, path);
 280     if (dir == NULL) return NULL;
 281 
 282     ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
 283     if (ptr == NULL) {
 284         JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
 285         closedir(dir);
 286         return NULL;
 287     }
 288 
 289     /* Allocate an initial String array */
 290     len = 0;
 291     maxlen = 16;
 292     rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
 293     if (rv == NULL) goto error;
 294 
 295     /* Scan the directory */
 296     while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
 297         jstring name;
 298         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
 299             continue;
 300         if (len == maxlen) {
 301             old = rv;
 302             rv = (*env)->NewObjectArray(env, maxlen <<= 1,
 303                                         JNU_ClassString(env), NULL);
 304             if (rv == NULL) goto error;
 305             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
 306             (*env)->DeleteLocalRef(env, old);
 307         }
 308         name = JNU_NewStringPlatform(env, ptr->d_name);
 309         if (name == NULL) goto error;
 310         (*env)->SetObjectArrayElement(env, rv, len++, name);
 311         (*env)->DeleteLocalRef(env, name);
 312     }
 313     closedir(dir);
 314     free(ptr);
 315 
 316     /* Copy the final results into an appropriately-sized array */
 317     old = rv;
 318     rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
 319     if (rv == NULL) {
 320         return NULL;
 321     }
 322     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
 323         return NULL;
 324     }
 325     return rv;
 326 
 327  error:
 328     closedir(dir);
 329     free(ptr);
 330     return NULL;
 331 }
 332 
 333 
 334 JNIEXPORT jboolean JNICALL
 335 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
 336                                             jobject file)
 337 {
 338     jboolean rv = JNI_FALSE;
 339 
 340     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 341         if (mkdir(path, 0777) == 0) {
 342             rv = JNI_TRUE;
 343         }
 344     } END_PLATFORM_STRING(env, path);
 345     return rv;
 346 }
 347 
 348 
 349 JNIEXPORT jboolean JNICALL
 350 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
 351                                     jobject from, jobject to)
 352 {
 353     jboolean rv = JNI_FALSE;
 354 
 355     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
 356         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
 357             if (rename(fromPath, toPath) == 0) {
 358                 rv = JNI_TRUE;
 359             }
 360         } END_PLATFORM_STRING(env, toPath);
 361     } END_PLATFORM_STRING(env, fromPath);
 362     return rv;
 363 }
 364 
 365 JNIEXPORT jboolean JNICALL
 366 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 367                                                 jobject file, jlong time)
 368 {
 369     jboolean rv = JNI_FALSE;
 370 
 371     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 372         struct stat64 sb;
 373 
 374         if (stat64(path, &sb) == 0) {
 375             struct timeval tv[2];
 376 
 377             /* Preserve access time */
 378             tv[0].tv_sec = sb.st_atime;
 379             tv[0].tv_usec = 0;
 380 
 381             /* Change last-modified time */
 382             tv[1].tv_sec = time / 1000;
 383             tv[1].tv_usec = (time % 1000) * 1000;
 384 
 385             if (utimes(path, tv) == 0)
 386                 rv = JNI_TRUE;
 387         }
 388     } END_PLATFORM_STRING(env, path);
 389 
 390     return rv;
 391 }
 392 
 393 
 394 JNIEXPORT jboolean JNICALL
 395 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
 396                                         jobject file)
 397 {
 398     jboolean rv = JNI_FALSE;
 399 
 400     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 401         int mode;
 402         if (statMode(path, &mode)) {
 403             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
 404                 rv = JNI_TRUE;
 405             }
 406         }
 407     } END_PLATFORM_STRING(env, path);
 408     return rv;
 409 }
 410 
 411 JNIEXPORT jlong JNICALL
 412 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
 413                                      jobject file, jint t)
 414 {
 415     jlong rv = 0L;
 416 
 417     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 418         struct statvfs64 fsstat;
 419         memset(&fsstat, 0, sizeof(fsstat));
 420         if (statvfs64(path, &fsstat) == 0) {
 421             switch(t) {
 422             case java_io_FileSystem_SPACE_TOTAL:
 423                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 424                                long_to_jlong(fsstat.f_blocks));
 425                 break;
 426             case java_io_FileSystem_SPACE_FREE:
 427                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 428                                long_to_jlong(fsstat.f_bfree));
 429                 break;
 430             case java_io_FileSystem_SPACE_USABLE:
 431                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 432                                long_to_jlong(fsstat.f_bavail));
 433                 break;
 434             default:
 435                 assert(0);
 436             }
 437         }
 438     } END_PLATFORM_STRING(env, path);
 439     return rv;
 440 }