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