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