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 "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(__solaris__) && !defined(NAME_MAX)
  67   #define NAME_MAX MAXNAMLEN
  68 #endif
  69 
  70 #if defined(_ALLBSD_SOURCE)
  71   #ifndef MACOSX
  72     #define statvfs64 statvfs
  73     #define stat64 stat
  74   #endif
  75 #endif
  76 
  77 /* -- Field IDs -- */
  78 
  79 static struct {
  80     jfieldID path;
  81 } ids;
  82 
  83 
  84 JNIEXPORT void JNICALL
  85 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
  86 {
  87     jclass fileClass = (*env)->FindClass(env, "java/io/File");
  88     if (!fileClass) return;
  89     ids.path = (*env)->GetFieldID(env, fileClass,
  90                                   "path", "Ljava/lang/String;");
  91 }
  92 
  93 /* -- Path operations -- */
  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 (JDK_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         int res;
 171         RESTARTABLE(access(path, mode), res);
 172         if (res == 0) {
 173             rv = JNI_TRUE;
 174         }
 175     } END_PLATFORM_STRING(env, path);
 176     return rv;
 177 }
 178 
 179 
 180 JNIEXPORT jboolean JNICALL
 181 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
 182                                           jobject file,
 183                                           jint access,
 184                                           jboolean enable,
 185                                           jboolean owneronly)
 186 {
 187     jboolean rv = JNI_FALSE;
 188 
 189     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 190         int amode = 0;
 191         int mode;
 192         int res;
 193         switch (access) {
 194         case java_io_FileSystem_ACCESS_READ:
 195             if (owneronly)
 196                 amode = S_IRUSR;
 197             else
 198                 amode = S_IRUSR | S_IRGRP | S_IROTH;
 199             break;
 200         case java_io_FileSystem_ACCESS_WRITE:
 201             if (owneronly)
 202                 amode = S_IWUSR;
 203             else
 204                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
 205             break;
 206         case java_io_FileSystem_ACCESS_EXECUTE:
 207             if (owneronly)
 208                 amode = S_IXUSR;
 209             else
 210                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
 211             break;
 212         default:
 213             assert(0);
 214         }
 215         if (statMode(path, &mode)) {
 216             if (enable)
 217                 mode |= amode;
 218             else
 219                 mode &= ~amode;
 220             RESTARTABLE(chmod(path, mode), res);
 221             if (res == 0) {
 222                 rv = JNI_TRUE;
 223             }
 224         }
 225     } END_PLATFORM_STRING(env, path);
 226     return rv;
 227 }
 228 
 229 JNIEXPORT jlong JNICALL
 230 Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
 231                                                 jobject file)
 232 {
 233     jlong rv = 0;
 234 
 235     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 236         struct stat64 sb;
 237         if (stat64(path, &sb) == 0) {
 238 #if defined(_AIX)
 239             rv =  (jlong)sb.st_mtime * 1000;
 240             rv += (jlong)sb.st_mtime_n / 1000000;
 241 #elif defined(MACOSX)
 242             rv  = (jlong)sb.st_mtimespec.tv_sec * 1000;
 243             rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000;
 244 #else
 245             rv  = (jlong)sb.st_mtim.tv_sec * 1000;
 246             rv += (jlong)sb.st_mtim.tv_nsec / 1000000;
 247 #endif
 248         }
 249     } END_PLATFORM_STRING(env, path);
 250     return rv;
 251 }
 252 
 253 
 254 JNIEXPORT jlong JNICALL
 255 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
 256                                       jobject file)
 257 {
 258     jlong rv = 0;
 259 
 260     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 261         struct stat64 sb;
 262         if (stat64(path, &sb) == 0) {
 263             rv = sb.st_size;
 264         }
 265     } END_PLATFORM_STRING(env, path);
 266     return rv;
 267 }
 268 
 269 
 270 /* -- File operations -- */
 271 
 272 
 273 JNIEXPORT jboolean JNICALL
 274 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
 275                                                   jstring pathname)
 276 {
 277     jboolean rv = JNI_FALSE;
 278 
 279     WITH_PLATFORM_STRING(env, pathname, path) {
 280         FD fd;
 281         /* The root directory always exists */
 282         if (strcmp (path, "/")) {
 283             fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
 284             if (fd < 0) {
 285                 if (errno != EEXIST)
 286                     JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
 287             } else {
 288                 if (close(fd) == -1)
 289                     JNU_ThrowIOExceptionWithLastError(env, "Could not close file");
 290                 rv = JNI_TRUE;
 291             }
 292         }
 293     } END_PLATFORM_STRING(env, path);
 294     return rv;
 295 }
 296 
 297 
 298 JNIEXPORT jboolean JNICALL
 299 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
 300                                     jobject file)
 301 {
 302     jboolean rv = JNI_FALSE;
 303 
 304     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 305         if (remove(path) == 0) {
 306             rv = JNI_TRUE;
 307         }
 308     } END_PLATFORM_STRING(env, path);
 309     return rv;
 310 }
 311 
 312 
 313 JNIEXPORT jobjectArray JNICALL
 314 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
 315                                  jobject file)
 316 {
 317     DIR *dir = NULL;
 318     struct dirent *ptr;
 319     int len, maxlen;
 320     jobjectArray rv, old;
 321     jclass str_class;
 322 
 323     str_class = JNU_ClassString(env);
 324     CHECK_NULL_RETURN(str_class, NULL);
 325 
 326     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 327         dir = opendir(path);
 328     } END_PLATFORM_STRING(env, path);
 329     if (dir == NULL) return NULL;
 330 
 331     /* Allocate an initial String array */
 332     len = 0;
 333     maxlen = 16;
 334     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
 335     if (rv == NULL) goto error;
 336 
 337     /* Scan the directory */
 338     while ((ptr = readdir(dir)) != NULL) {
 339         jstring name;
 340         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
 341             continue;
 342         if (len == maxlen) {
 343             old = rv;
 344             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
 345             if (rv == NULL) goto error;
 346             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
 347             (*env)->DeleteLocalRef(env, old);
 348         }
 349 #ifdef MACOSX
 350         name = newStringPlatform(env, ptr->d_name);
 351 #else
 352         name = JNU_NewStringPlatform(env, ptr->d_name);
 353 #endif
 354         if (name == NULL) goto error;
 355         (*env)->SetObjectArrayElement(env, rv, len++, name);
 356         (*env)->DeleteLocalRef(env, name);
 357     }
 358     closedir(dir);
 359 
 360     /* Copy the final results into an appropriately-sized array */
 361     if (len < maxlen) {
 362         old = rv;
 363         rv = (*env)->NewObjectArray(env, len, str_class, NULL);
 364         if (rv == NULL) {
 365             return NULL;
 366         }
 367         if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
 368             return NULL;
 369         }
 370     }
 371     return rv;
 372 
 373  error:
 374     closedir(dir);
 375     return NULL;
 376 }
 377 
 378 
 379 JNIEXPORT jboolean JNICALL
 380 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
 381                                             jobject file)
 382 {
 383     jboolean rv = JNI_FALSE;
 384 
 385     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 386         if (mkdir(path, 0777) == 0) {
 387             rv = JNI_TRUE;
 388         }
 389     } END_PLATFORM_STRING(env, path);
 390     return rv;
 391 }
 392 
 393 
 394 JNIEXPORT jboolean JNICALL
 395 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
 396                                     jobject from, jobject to)
 397 {
 398     jboolean rv = JNI_FALSE;
 399 
 400     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
 401         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
 402             if (rename(fromPath, toPath) == 0) {
 403                 rv = JNI_TRUE;
 404             }
 405         } END_PLATFORM_STRING(env, toPath);
 406     } END_PLATFORM_STRING(env, fromPath);
 407     return rv;
 408 }
 409 
 410 JNIEXPORT jboolean JNICALL
 411 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 412                                                 jobject file, jlong time)
 413 {
 414     jboolean rv = JNI_FALSE;
 415 
 416     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 417         struct stat64 sb;
 418 
 419         if (stat64(path, &sb) == 0) {
 420             struct timeval tv[2];
 421 
 422             /* Preserve access time */
 423 #if defined(_AIX)
 424             tv[0].tv_sec = sb.st_atime;
 425             tv[0].tv_usec = sb.st_atime_n / 1000;
 426 #elif defined(MACOSX)
 427             tv[0].tv_sec = sb.st_atimespec.tv_sec;
 428             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
 429 #else
 430             tv[0].tv_sec = sb.st_atim.tv_sec;
 431             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
 432 #endif
 433             /* Change last-modified time */
 434             tv[1].tv_sec = time / 1000;
 435             tv[1].tv_usec = (time % 1000) * 1000;
 436 
 437             if (utimes(path, tv) == 0)
 438                 rv = JNI_TRUE;
 439         }
 440     } END_PLATFORM_STRING(env, path);
 441 
 442     return rv;
 443 }
 444 
 445 
 446 JNIEXPORT jboolean JNICALL
 447 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
 448                                         jobject file)
 449 {
 450     jboolean rv = JNI_FALSE;
 451 
 452     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 453         int mode;
 454         int res;
 455         if (statMode(path, &mode)) {
 456             RESTARTABLE(chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)), res);
 457             if (res == 0) {
 458                 rv = JNI_TRUE;
 459             }
 460         }
 461     } END_PLATFORM_STRING(env, path);
 462     return rv;
 463 }
 464 
 465 JNIEXPORT jlong JNICALL
 466 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
 467                                      jobject file, jint t)
 468 {
 469     jlong rv = 0L;
 470 
 471     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 472 #ifdef MACOSX
 473         struct statfs fsstat;
 474 #else
 475         struct statvfs64 fsstat;
 476         int res;
 477 #endif
 478         memset(&fsstat, 0, sizeof(fsstat));
 479 #ifdef MACOSX
 480         if (statfs(path, &fsstat) == 0) {
 481             switch(t) {
 482                 case java_io_FileSystem_SPACE_TOTAL:
 483                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 484                                    long_to_jlong(fsstat.f_blocks));
 485                     break;
 486                 case java_io_FileSystem_SPACE_FREE:
 487                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 488                                    long_to_jlong(fsstat.f_bfree));
 489                     break;
 490                 case java_io_FileSystem_SPACE_USABLE:
 491                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 492                                    long_to_jlong(fsstat.f_bavail));
 493                     break;
 494                 default:
 495                     assert(0);
 496             }
 497         }
 498 #else
 499         RESTARTABLE(statvfs64(path, &fsstat), res);
 500         if (res == 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 }