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