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 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 readdir
  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     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 = readdir64(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     old = rv;
 358     rv = (*env)->NewObjectArray(env, len, str_class, NULL);
 359     if (rv == NULL) {
 360         return NULL;
 361     }
 362     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
 363         return NULL;
 364     }
 365     return rv;
 366 
 367  error:
 368     closedir(dir);
 369     return NULL;
 370 }
 371 
 372 
 373 JNIEXPORT jboolean JNICALL
 374 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
 375                                             jobject file)
 376 {
 377     jboolean rv = JNI_FALSE;
 378 
 379     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 380         if (mkdir(path, 0777) == 0) {
 381             rv = JNI_TRUE;
 382         }
 383     } END_PLATFORM_STRING(env, path);
 384     return rv;
 385 }
 386 
 387 
 388 JNIEXPORT jboolean JNICALL
 389 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
 390                                     jobject from, jobject to)
 391 {
 392     jboolean rv = JNI_FALSE;
 393 
 394     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
 395         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
 396             if (rename(fromPath, toPath) == 0) {
 397                 rv = JNI_TRUE;
 398             }
 399         } END_PLATFORM_STRING(env, toPath);
 400     } END_PLATFORM_STRING(env, fromPath);
 401     return rv;
 402 }
 403 
 404 JNIEXPORT jboolean JNICALL
 405 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 406                                                 jobject file, jlong time)
 407 {
 408     jboolean rv = JNI_FALSE;
 409 
 410     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 411         struct stat64 sb;
 412 
 413         if (stat64(path, &sb) == 0) {
 414             struct timeval tv[2];
 415 
 416             /* Preserve access time */
 417 #if defined(_AIX)
 418             tv[0].tv_sec = sb.st_atime;
 419             tv[0].tv_usec = sb.st_atime_n / 1000;
 420 #elif defined(MACOSX)
 421             tv[0].tv_sec = sb.st_atimespec.tv_sec;
 422             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
 423 #else
 424             tv[0].tv_sec = sb.st_atim.tv_sec;
 425             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
 426 #endif
 427             /* Change last-modified time */
 428             tv[1].tv_sec = time / 1000;
 429             tv[1].tv_usec = (time % 1000) * 1000;
 430 
 431             if (utimes(path, tv) == 0)
 432                 rv = JNI_TRUE;
 433         }
 434     } END_PLATFORM_STRING(env, path);
 435 
 436     return rv;
 437 }
 438 
 439 
 440 JNIEXPORT jboolean JNICALL
 441 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
 442                                         jobject file)
 443 {
 444     jboolean rv = JNI_FALSE;
 445 
 446     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 447         int mode;
 448         if (statMode(path, &mode)) {
 449             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
 450                 rv = JNI_TRUE;
 451             }
 452         }
 453     } END_PLATFORM_STRING(env, path);
 454     return rv;
 455 }
 456 
 457 JNIEXPORT jlong JNICALL
 458 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
 459                                      jobject file, jint t)
 460 {
 461     jlong rv = 0L;
 462 
 463     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
 464 #ifdef MACOSX
 465         struct statfs fsstat;
 466 #else
 467         struct statvfs64 fsstat;
 468 #endif
 469         memset(&fsstat, 0, sizeof(fsstat));
 470 #ifdef MACOSX
 471         if (statfs(path, &fsstat) == 0) {
 472             switch(t) {
 473                 case java_io_FileSystem_SPACE_TOTAL:
 474                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 475                                    long_to_jlong(fsstat.f_blocks));
 476                     break;
 477                 case java_io_FileSystem_SPACE_FREE:
 478                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 479                                    long_to_jlong(fsstat.f_bfree));
 480                     break;
 481                 case java_io_FileSystem_SPACE_USABLE:
 482                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
 483                                    long_to_jlong(fsstat.f_bavail));
 484                     break;
 485                 default:
 486                     assert(0);
 487             }
 488         }
 489 #else
 490         if (statvfs64(path, &fsstat) == 0) {
 491             switch(t) {
 492             case java_io_FileSystem_SPACE_TOTAL:
 493                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 494                                long_to_jlong(fsstat.f_blocks));
 495                 break;
 496             case java_io_FileSystem_SPACE_FREE:
 497                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 498                                long_to_jlong(fsstat.f_bfree));
 499                 break;
 500             case java_io_FileSystem_SPACE_USABLE:
 501                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
 502                                long_to_jlong(fsstat.f_bavail));
 503                 break;
 504             default:
 505                 assert(0);
 506             }
 507         }
 508 #endif
 509     } END_PLATFORM_STRING(env, path);
 510     return rv;
 511 }
 512 
 513 JNIEXPORT jlong JNICALL
 514 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
 515                                         jstring pathname)
 516 {
 517     jlong length = -1;
 518     WITH_PLATFORM_STRING(env, pathname, path) {
 519         length = (jlong)pathconf(path, _PC_NAME_MAX);
 520     } END_PLATFORM_STRING(env, path);
 521     return length != -1 ? length : (jlong)NAME_MAX;
 522 }