1 /*
   2  * Copyright (c) 1998, 2020, 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 "jdk_util.h"
  49 #include "io_util.h"
  50 #include "io_util_md.h"
  51 #include "java_io_FileSystem.h"
  52 #include "java_io_UnixFileSystem.h"
  53 
  54 #if defined(_AIX)
  55   #if !defined(NAME_MAX)
  56     #define NAME_MAX MAXNAMLEN
  57   #endif
  58   #define DIR DIR64
  59   #define dirent dirent64
  60   #define opendir opendir64
  61   #define readdir readdir64
  62   #define closedir closedir64
  63   #define stat stat64
  64 #endif
  65 
  66 #if defined(_ALLBSD_SOURCE)
  67   #ifndef MACOSX
  68     #define statvfs64 statvfs
  69     #define stat64 stat
  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 JNIEXPORT jstring JNICALL
  92 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
  93                                           jstring pathname)
  94 {
  95     jstring rv = NULL;
  96 
  97     WITH_PLATFORM_STRING(env, pathname, path) {
  98         char canonicalPath[PATH_MAX];
  99         if (JDK_Canonicalize((char *)path,
 100                          canonicalPath, PATH_MAX) < 0) {
 101             JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
 102         } else {
 103 #ifdef MACOSX
 104             rv = newStringPlatform(env, canonicalPath);
 105 #else
 106             rv = JNU_NewStringPlatform(env, canonicalPath);
 107 #endif
 108         }
 109     } END_PLATFORM_STRING(env, path);
 110     return rv;
 111 }
 112 
 113 
 114 /* -- Attribute accessors -- */
 115 
 116 
 117 static jboolean
 118 statMode(const char *path, int *mode)
 119 {
 120     struct stat64 sb;
 121     if (stat64(path, &sb) == 0) {
 122         *mode = sb.st_mode;
 123         return JNI_TRUE;
 124     }
 125     return JNI_FALSE;
 126 }
 127 
 128 
 129 JNIEXPORT jint JNICALL
 130 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
 131                                                   jobject file)
 132 {
 133     jint rv = 0;
 134 
 135     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 136         int mode;
 137         if (statMode(path, &mode)) {
 138             int fmt = mode & S_IFMT;
 139             rv = (jint) (java_io_FileSystem_BA_EXISTS
 140                   | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
 141                   | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
 142         }
 143     } END_PLATFORM_STRING(env, path);
 144     return rv;
 145 }
 146 
 147 JNIEXPORT jboolean JNICALL
 148 Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
 149                                         jobject file, jint a)
 150 {
 151     jboolean rv = JNI_FALSE;
 152     int mode = 0;
 153     switch (a) {
 154     case java_io_FileSystem_ACCESS_READ:
 155         mode = R_OK;
 156         break;
 157     case java_io_FileSystem_ACCESS_WRITE:
 158         mode = W_OK;
 159         break;
 160     case java_io_FileSystem_ACCESS_EXECUTE:
 161         mode = X_OK;
 162         break;
 163     default: assert(0);
 164     }
 165     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 166         int res;
 167         RESTARTABLE(access(path, mode), res);
 168         if (res == 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         int res;
 189         switch (access) {
 190         case java_io_FileSystem_ACCESS_READ:
 191             if (owneronly)
 192                 amode = S_IRUSR;
 193             else
 194                 amode = S_IRUSR | S_IRGRP | S_IROTH;
 195             break;
 196         case java_io_FileSystem_ACCESS_WRITE:
 197             if (owneronly)
 198                 amode = S_IWUSR;
 199             else
 200                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
 201             break;
 202         case java_io_FileSystem_ACCESS_EXECUTE:
 203             if (owneronly)
 204                 amode = S_IXUSR;
 205             else
 206                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
 207             break;
 208         default:
 209             assert(0);
 210         }
 211         if (statMode(path, &mode)) {
 212             if (enable)
 213                 mode |= amode;
 214             else
 215                 mode &= ~amode;
 216             RESTARTABLE(chmod(path, mode), res);
 217             if (res == 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, "Could not open file");
 283             } else {
 284                 if (close(fd) == -1)
 285                     JNU_ThrowIOExceptionWithLastError(env, "Could not close file");
 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 dirent *ptr;
 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     /* Allocate an initial String array */
 328     len = 0;
 329     maxlen = 16;
 330     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
 331     if (rv == NULL) goto error;
 332 
 333     /* Scan the directory */
 334     while ((ptr = readdir(dir)) != NULL) {
 335         jstring name;
 336         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
 337             continue;
 338         if (len == maxlen) {
 339             old = rv;
 340             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
 341             if (rv == NULL) goto error;
 342             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
 343             (*env)->DeleteLocalRef(env, old);
 344         }
 345 #ifdef MACOSX
 346         name = newStringPlatform(env, ptr->d_name);
 347 #else
 348         name = JNU_NewStringPlatform(env, ptr->d_name);
 349 #endif
 350         if (name == NULL) goto error;
 351         (*env)->SetObjectArrayElement(env, rv, len++, name);
 352         (*env)->DeleteLocalRef(env, name);
 353     }
 354     closedir(dir);
 355 
 356     /* Copy the final results into an appropriately-sized array */
 357     if (len < maxlen) {
 358         old = rv;
 359         rv = (*env)->NewObjectArray(env, len, str_class, NULL);
 360         if (rv == NULL) {
 361             return NULL;
 362         }
 363         if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
 364             return NULL;
 365         }
 366     }
 367     return rv;
 368 
 369  error:
 370     closedir(dir);
 371     return NULL;
 372 }
 373 
 374 
 375 JNIEXPORT jboolean JNICALL
 376 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
 377                                             jobject file)
 378 {
 379     jboolean rv = JNI_FALSE;
 380 
 381     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 382         if (mkdir(path, 0777) == 0) {
 383             rv = JNI_TRUE;
 384         }
 385     } END_PLATFORM_STRING(env, path);
 386     return rv;
 387 }
 388 
 389 
 390 JNIEXPORT jboolean JNICALL
 391 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
 392                                     jobject from, jobject to)
 393 {
 394     jboolean rv = JNI_FALSE;
 395 
 396     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
 397         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
 398             if (rename(fromPath, toPath) == 0) {
 399                 rv = JNI_TRUE;
 400             }
 401         } END_PLATFORM_STRING(env, toPath);
 402     } END_PLATFORM_STRING(env, fromPath);
 403     return rv;
 404 }
 405 
 406 JNIEXPORT jboolean JNICALL
 407 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 408                                                 jobject file, jlong time)
 409 {
 410     jboolean rv = JNI_FALSE;
 411 
 412     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 413         struct stat64 sb;
 414 
 415         if (stat64(path, &sb) == 0) {
 416             struct timeval tv[2];
 417 
 418             /* Preserve access time */
 419 #if defined(_AIX)
 420             tv[0].tv_sec = sb.st_atime;
 421             tv[0].tv_usec = sb.st_atime_n / 1000;
 422 #elif defined(MACOSX)
 423             tv[0].tv_sec = sb.st_atimespec.tv_sec;
 424             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
 425 #else
 426             tv[0].tv_sec = sb.st_atim.tv_sec;
 427             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
 428 #endif
 429             /* Change last-modified time */
 430             tv[1].tv_sec = time / 1000;
 431             tv[1].tv_usec = (time % 1000) * 1000;
 432 
 433             if (utimes(path, tv) == 0)
 434                 rv = JNI_TRUE;
 435         }
 436     } END_PLATFORM_STRING(env, path);
 437 
 438     return rv;
 439 }
 440 
 441 
 442 JNIEXPORT jboolean JNICALL
 443 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
 444                                         jobject file)
 445 {
 446     jboolean rv = JNI_FALSE;
 447 
 448     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 449         int mode;
 450         int res;
 451         if (statMode(path, &mode)) {
 452             RESTARTABLE(chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)), res);
 453             if (res == 0) {
 454                 rv = JNI_TRUE;
 455             }
 456         }
 457     } END_PLATFORM_STRING(env, path);
 458     return rv;
 459 }
 460 
 461 JNIEXPORT jlong JNICALL
 462 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
 463                                      jobject file, jint t)
 464 {
 465     jlong rv = 0L;
 466 
 467     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 468 #ifdef MACOSX
 469         struct statfs fsstat;
 470 #else
 471         struct statvfs64 fsstat;
 472         int res;
 473 #endif
 474         memset(&fsstat, 0, sizeof(fsstat));
 475 #ifdef MACOSX
 476         if (statfs(path, &fsstat) == 0) {
 477             switch(t) {
 478                 case java_io_FileSystem_SPACE_TOTAL:
 479                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 480                                    long_to_jlong(fsstat.f_blocks));
 481                     break;
 482                 case java_io_FileSystem_SPACE_FREE:
 483                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 484                                    long_to_jlong(fsstat.f_bfree));
 485                     break;
 486                 case java_io_FileSystem_SPACE_USABLE:
 487                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 488                                    long_to_jlong(fsstat.f_bavail));
 489                     break;
 490                 default:
 491                     assert(0);
 492             }
 493         }
 494 #else
 495         RESTARTABLE(statvfs64(path, &fsstat), res);
 496         if (res == 0) {
 497             switch(t) {
 498             case java_io_FileSystem_SPACE_TOTAL:
 499                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 500                                long_to_jlong(fsstat.f_blocks));
 501                 break;
 502             case java_io_FileSystem_SPACE_FREE:
 503                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 504                                long_to_jlong(fsstat.f_bfree));
 505                 break;
 506             case java_io_FileSystem_SPACE_USABLE:
 507                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 508                                long_to_jlong(fsstat.f_bavail));
 509                 break;
 510             default:
 511                 assert(0);
 512             }
 513         }
 514 #endif
 515     } END_PLATFORM_STRING(env, path);
 516     return rv;
 517 }
 518 
 519 JNIEXPORT jlong JNICALL
 520 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
 521                                         jstring pathname)
 522 {
 523     jlong length = -1;
 524     WITH_PLATFORM_STRING(env, pathname, path) {
 525         length = (jlong)pathconf(path, _PC_NAME_MAX);
 526     } END_PLATFORM_STRING(env, path);
 527     return length != -1 ? length : (jlong)NAME_MAX;
 528 }