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