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 #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 opendir opendir64
  59   #define closedir closedir64
  60 #endif
  61 
  62 #if defined(__solaris__) && !defined(NAME_MAX)
  63   #define NAME_MAX MAXNAMLEN
  64 #endif
  65 
  66 #if defined(_ALLBSD_SOURCE)
  67   #define dirent64 dirent
  68   #define readdir64_r readdir_r
  69   #define stat64 stat
  70   #ifndef MACOSX
  71     #define statvfs64 statvfs
  72   #endif
  73 #endif
  74 
  75 /* -- Field IDs -- */
  76 
  77 static struct {
  78     jfieldID path;
  79 } ids;
  80 
  81 
  82 JNIEXPORT void JNICALL
  83 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
  84 {
  85     jclass fileClass = (*env)->FindClass(env, "java/io/File");
  86     if (!fileClass) return;
  87     ids.path = (*env)->GetFieldID(env, fileClass,
  88                                   "path", "Ljava/lang/String;");
  89 }
  90 
  91 /* -- Path operations -- */
  92 
  93 extern int canonicalize(char *path, const char *out, int len);
  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 (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         if (access(path, mode) == 0) {
 171             rv = JNI_TRUE;
 172         }
 173     } END_PLATFORM_STRING(env, path);
 174     return rv;
 175 }
 176 
 177 
 178 JNIEXPORT jboolean JNICALL
 179 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
 180                                           jobject file,
 181                                           jint access,
 182                                           jboolean enable,
 183                                           jboolean owneronly)
 184 {
 185     jboolean rv = JNI_FALSE;
 186 
 187     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 188         int amode = 0;
 189         int mode;
 190         switch (access) {
 191         case java_io_FileSystem_ACCESS_READ:
 192             if (owneronly)
 193                 amode = S_IRUSR;
 194             else
 195                 amode = S_IRUSR | S_IRGRP | S_IROTH;
 196             break;
 197         case java_io_FileSystem_ACCESS_WRITE:
 198             if (owneronly)
 199                 amode = S_IWUSR;
 200             else
 201                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
 202             break;
 203         case java_io_FileSystem_ACCESS_EXECUTE:
 204             if (owneronly)
 205                 amode = S_IXUSR;
 206             else
 207                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
 208             break;
 209         default:
 210             assert(0);
 211         }
 212         if (statMode(path, &mode)) {
 213             if (enable)
 214                 mode |= amode;
 215             else
 216                 mode &= ~amode;
 217             if (chmod(path, mode) >= 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, path);
 283             } else {
 284                 if (close(fd) == -1)
 285                     JNU_ThrowIOExceptionWithLastError(env, path);
 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 dirent64 *ptr;
 315     struct dirent64 *result;
 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     ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
 329     if (ptr == NULL) {
 330         JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
 331         closedir(dir);
 332         return NULL;
 333     }
 334 
 335     /* Allocate an initial String array */
 336     len = 0;
 337     maxlen = 16;
 338     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
 339     if (rv == NULL) goto error;
 340 
 341     /* Scan the directory */
 342 #pragma GCC diagnostic push
 343 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 344     while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
 345 #pragma GCC diagnostic pop
 346         jstring name;
 347         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
 348             continue;
 349         if (len == maxlen) {
 350             old = rv;
 351             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
 352             if (rv == NULL) goto error;
 353             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
 354             (*env)->DeleteLocalRef(env, old);
 355         }
 356 #ifdef MACOSX
 357         name = newStringPlatform(env, ptr->d_name);
 358 #else
 359         name = JNU_NewStringPlatform(env, ptr->d_name);
 360 #endif
 361         if (name == NULL) goto error;
 362         (*env)->SetObjectArrayElement(env, rv, len++, name);
 363         (*env)->DeleteLocalRef(env, name);
 364     }
 365     closedir(dir);
 366     free(ptr);
 367 
 368     /* Copy the final results into an appropriately-sized array */
 369     old = rv;
 370     rv = (*env)->NewObjectArray(env, len, str_class, NULL);
 371     if (rv == NULL) {
 372         return NULL;
 373     }
 374     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
 375         return NULL;
 376     }
 377     return rv;
 378 
 379  error:
 380     closedir(dir);
 381     free(ptr);
 382     return NULL;
 383 }
 384 
 385 
 386 JNIEXPORT jboolean JNICALL
 387 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
 388                                             jobject file)
 389 {
 390     jboolean rv = JNI_FALSE;
 391 
 392     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 393         if (mkdir(path, 0777) == 0) {
 394             rv = JNI_TRUE;
 395         }
 396     } END_PLATFORM_STRING(env, path);
 397     return rv;
 398 }
 399 
 400 
 401 JNIEXPORT jboolean JNICALL
 402 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
 403                                     jobject from, jobject to)
 404 {
 405     jboolean rv = JNI_FALSE;
 406 
 407     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
 408         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
 409             if (rename(fromPath, toPath) == 0) {
 410                 rv = JNI_TRUE;
 411             }
 412         } END_PLATFORM_STRING(env, toPath);
 413     } END_PLATFORM_STRING(env, fromPath);
 414     return rv;
 415 }
 416 
 417 JNIEXPORT jboolean JNICALL
 418 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 419                                                 jobject file, jlong time)
 420 {
 421     jboolean rv = JNI_FALSE;
 422 
 423     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 424         struct stat64 sb;
 425 
 426         if (stat64(path, &sb) == 0) {
 427             struct timeval tv[2];
 428 
 429             /* Preserve access time */
 430 #if defined(_AIX)
 431             tv[0].tv_sec = sb.st_atime;
 432             tv[0].tv_usec = sb.st_atime_n / 1000;
 433 #elif defined(MACOSX)
 434             tv[0].tv_sec = sb.st_atimespec.tv_sec;
 435             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
 436 #else
 437             tv[0].tv_sec = sb.st_atim.tv_sec;
 438             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
 439 #endif
 440             /* Change last-modified time */
 441             tv[1].tv_sec = time / 1000;
 442             tv[1].tv_usec = (time % 1000) * 1000;
 443 
 444             if (utimes(path, tv) == 0)
 445                 rv = JNI_TRUE;
 446         }
 447     } END_PLATFORM_STRING(env, path);
 448 
 449     return rv;
 450 }
 451 
 452 
 453 JNIEXPORT jboolean JNICALL
 454 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
 455                                         jobject file)
 456 {
 457     jboolean rv = JNI_FALSE;
 458 
 459     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 460         int mode;
 461         if (statMode(path, &mode)) {
 462             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
 463                 rv = JNI_TRUE;
 464             }
 465         }
 466     } END_PLATFORM_STRING(env, path);
 467     return rv;
 468 }
 469 
 470 JNIEXPORT jlong JNICALL
 471 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
 472                                      jobject file, jint t)
 473 {
 474     jlong rv = 0L;
 475 
 476     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 477 #ifdef MACOSX
 478         struct statfs fsstat;
 479 #else
 480         struct statvfs64 fsstat;
 481 #endif
 482         memset(&fsstat, 0, sizeof(fsstat));
 483 #ifdef MACOSX
 484         if (statfs(path, &fsstat) == 0) {
 485             switch(t) {
 486                 case java_io_FileSystem_SPACE_TOTAL:
 487                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 488                                    long_to_jlong(fsstat.f_blocks));
 489                     break;
 490                 case java_io_FileSystem_SPACE_FREE:
 491                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 492                                    long_to_jlong(fsstat.f_bfree));
 493                     break;
 494                 case java_io_FileSystem_SPACE_USABLE:
 495                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 496                                    long_to_jlong(fsstat.f_bavail));
 497                     break;
 498                 default:
 499                     assert(0);
 500             }
 501         }
 502 #else
 503         if (statvfs64(path, &fsstat) == 0) {
 504             switch(t) {
 505             case java_io_FileSystem_SPACE_TOTAL:
 506                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 507                                long_to_jlong(fsstat.f_blocks));
 508                 break;
 509             case java_io_FileSystem_SPACE_FREE:
 510                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 511                                long_to_jlong(fsstat.f_bfree));
 512                 break;
 513             case java_io_FileSystem_SPACE_USABLE:
 514                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 515                                long_to_jlong(fsstat.f_bavail));
 516                 break;
 517             default:
 518                 assert(0);
 519             }
 520         }
 521 #endif
 522     } END_PLATFORM_STRING(env, path);
 523     return rv;
 524 }
 525 
 526 JNIEXPORT jlong JNICALL
 527 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
 528                                         jstring pathname)
 529 {
 530     jlong length = -1;
 531     WITH_PLATFORM_STRING(env, pathname, path) {
 532         length = (jlong)pathconf(path, _PC_NAME_MAX);
 533     } END_PLATFORM_STRING(env, path);
 534     return length != -1 ? length : (jlong)NAME_MAX;
 535 }