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