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