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