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 #ifndef MACOSX
 233             rv  = (jlong)sb.st_mtim.tv_sec * 1000;
 234             rv += (jlong)sb.st_mtim.tv_nsec / 1000000;
 235 #else
 236             rv  = (jlong)sb.st_mtimespec.tv_sec * 1000;
 237             rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000;
 238 #endif
 239         }
 240     } END_PLATFORM_STRING(env, path);
 241     return rv;
 242 }
 243 
 244 
 245 JNIEXPORT jlong JNICALL
 246 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
 247                                       jobject file)
 248 {
 249     jlong rv = 0;
 250 
 251     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 252         struct stat64 sb;
 253         if (stat64(path, &sb) == 0) {
 254             rv = sb.st_size;
 255         }
 256     } END_PLATFORM_STRING(env, path);
 257     return rv;
 258 }
 259 
 260 
 261 /* -- File operations -- */
 262 
 263 
 264 JNIEXPORT jboolean JNICALL
 265 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
 266                                                   jstring pathname)
 267 {
 268     jboolean rv = JNI_FALSE;
 269 
 270     WITH_PLATFORM_STRING(env, pathname, path) {
 271         FD fd;
 272         /* The root directory always exists */
 273         if (strcmp (path, "/")) {
 274             fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
 275             if (fd < 0) {
 276                 if (errno != EEXIST)
 277                     JNU_ThrowIOExceptionWithLastError(env, path);
 278             } else {
 279                 if (close(fd) == -1)
 280                     JNU_ThrowIOExceptionWithLastError(env, path);
 281                 rv = JNI_TRUE;
 282             }
 283         }
 284     } END_PLATFORM_STRING(env, path);
 285     return rv;
 286 }
 287 
 288 
 289 JNIEXPORT jboolean JNICALL
 290 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
 291                                     jobject file)
 292 {
 293     jboolean rv = JNI_FALSE;
 294 
 295     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 296         if (remove(path) == 0) {
 297             rv = JNI_TRUE;
 298         }
 299     } END_PLATFORM_STRING(env, path);
 300     return rv;
 301 }
 302 
 303 
 304 JNIEXPORT jobjectArray JNICALL
 305 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
 306                                  jobject file)
 307 {
 308     DIR *dir = NULL;
 309     struct dirent64 *ptr;
 310     struct dirent64 *result;
 311     int len, maxlen;
 312     jobjectArray rv, old;
 313     jclass str_class;
 314 
 315     str_class = JNU_ClassString(env);
 316     CHECK_NULL_RETURN(str_class, NULL);
 317 
 318     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 319         dir = opendir(path);
 320     } END_PLATFORM_STRING(env, path);
 321     if (dir == NULL) return NULL;
 322 
 323     ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
 324     if (ptr == NULL) {
 325         JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
 326         closedir(dir);
 327         return NULL;
 328     }
 329 
 330     /* Allocate an initial String array */
 331     len = 0;
 332     maxlen = 16;
 333     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
 334     if (rv == NULL) goto error;
 335 
 336     /* Scan the directory */
 337     while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
 338         jstring name;
 339         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
 340             continue;
 341         if (len == maxlen) {
 342             old = rv;
 343             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
 344             if (rv == NULL) goto error;
 345             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
 346             (*env)->DeleteLocalRef(env, old);
 347         }
 348 #ifdef MACOSX
 349         name = newStringPlatform(env, ptr->d_name);
 350 #else
 351         name = JNU_NewStringPlatform(env, ptr->d_name);
 352 #endif
 353         if (name == NULL) goto error;
 354         (*env)->SetObjectArrayElement(env, rv, len++, name);
 355         (*env)->DeleteLocalRef(env, name);
 356     }
 357     closedir(dir);
 358     free(ptr);
 359 
 360     /* Copy the final results into an appropriately-sized array */
 361     old = rv;
 362     rv = (*env)->NewObjectArray(env, len, str_class, NULL);
 363     if (rv == NULL) {
 364         return NULL;
 365     }
 366     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
 367         return NULL;
 368     }
 369     return rv;
 370 
 371  error:
 372     closedir(dir);
 373     free(ptr);
 374     return NULL;
 375 }
 376 
 377 
 378 JNIEXPORT jboolean JNICALL
 379 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
 380                                             jobject file)
 381 {
 382     jboolean rv = JNI_FALSE;
 383 
 384     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 385         if (mkdir(path, 0777) == 0) {
 386             rv = JNI_TRUE;
 387         }
 388     } END_PLATFORM_STRING(env, path);
 389     return rv;
 390 }
 391 
 392 
 393 JNIEXPORT jboolean JNICALL
 394 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
 395                                     jobject from, jobject to)
 396 {
 397     jboolean rv = JNI_FALSE;
 398 
 399     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
 400         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
 401             if (rename(fromPath, toPath) == 0) {
 402                 rv = JNI_TRUE;
 403             }
 404         } END_PLATFORM_STRING(env, toPath);
 405     } END_PLATFORM_STRING(env, fromPath);
 406     return rv;
 407 }
 408 
 409 JNIEXPORT jboolean JNICALL
 410 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 411                                                 jobject file, jlong time)
 412 {
 413     jboolean rv = JNI_FALSE;
 414 
 415     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 416         struct stat64 sb;
 417 
 418         if (stat64(path, &sb) == 0) {
 419             struct timeval tv[2];
 420 
 421             /* Preserve access time */
 422 #ifndef MACOSX
 423             tv[0].tv_sec = sb.st_atim.tv_sec;
 424             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
 425 #else
 426             tv[0].tv_sec = sb.st_atimespec.tv_sec;
 427             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
 428 #endif
 429 
 430             /* Change last-modified time */
 431             tv[1].tv_sec = time / 1000;
 432             tv[1].tv_usec = (time % 1000) * 1000;
 433 
 434             if (utimes(path, tv) == 0)
 435                 rv = JNI_TRUE;
 436         }
 437     } END_PLATFORM_STRING(env, path);
 438 
 439     return rv;
 440 }
 441 
 442 
 443 JNIEXPORT jboolean JNICALL
 444 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
 445                                         jobject file)
 446 {
 447     jboolean rv = JNI_FALSE;
 448 
 449     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 450         int mode;
 451         if (statMode(path, &mode)) {
 452             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
 453                 rv = JNI_TRUE;
 454             }
 455         }
 456     } END_PLATFORM_STRING(env, path);
 457     return rv;
 458 }
 459 
 460 JNIEXPORT jlong JNICALL
 461 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
 462                                      jobject file, jint t)
 463 {
 464     jlong rv = 0L;
 465 
 466     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 467 #ifdef MACOSX
 468         struct statfs fsstat;
 469 #else
 470         struct statvfs64 fsstat;
 471 #endif
 472         memset(&fsstat, 0, sizeof(fsstat));
 473 #ifdef MACOSX
 474         if (statfs(path, &fsstat) == 0) {
 475             switch(t) {
 476                 case java_io_FileSystem_SPACE_TOTAL:
 477                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 478                                    long_to_jlong(fsstat.f_blocks));
 479                     break;
 480                 case java_io_FileSystem_SPACE_FREE:
 481                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 482                                    long_to_jlong(fsstat.f_bfree));
 483                     break;
 484                 case java_io_FileSystem_SPACE_USABLE:
 485                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 486                                    long_to_jlong(fsstat.f_bavail));
 487                     break;
 488                 default:
 489                     assert(0);
 490             }
 491         }
 492 #else
 493         if (statvfs64(path, &fsstat) == 0) {
 494             switch(t) {
 495             case java_io_FileSystem_SPACE_TOTAL:
 496                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 497                                long_to_jlong(fsstat.f_blocks));
 498                 break;
 499             case java_io_FileSystem_SPACE_FREE:
 500                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 501                                long_to_jlong(fsstat.f_bfree));
 502                 break;
 503             case java_io_FileSystem_SPACE_USABLE:
 504                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 505                                long_to_jlong(fsstat.f_bavail));
 506                 break;
 507             default:
 508                 assert(0);
 509             }
 510         }
 511 #endif
 512     } END_PLATFORM_STRING(env, path);
 513     return rv;
 514 }
 515 
 516 JNIEXPORT jlong JNICALL
 517 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
 518                                         jstring pathname)
 519 {
 520     jlong length = -1;
 521     WITH_PLATFORM_STRING(env, pathname, path) {
 522         length = (jlong)pathconf(path, _PC_NAME_MAX);
 523     } END_PLATFORM_STRING(env, path);
 524     return length != -1 ? length : (jlong)NAME_MAX;
 525 }