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 #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 /* extendExceptionsWithPath == -1: not yet initialized; 92 * 0: do not extend, 1: extend the exception message 93 */ 94 static int extendExceptionsWithPath = -1; 95 96 static int checkExtendExceptionsWithPath(JNIEnv *env) { 97 if (extendExceptionsWithPath == -1) { 98 jboolean res; 99 jmethodID hasPathInExceptionsMethod; 100 jclass cls = (*env)->FindClass(env, "java/io/FileSystem"); 101 if (cls == NULL) return -1; 102 hasPathInExceptionsMethod = (*env)->GetStaticMethodID(env, cls, 103 "hasPathInExceptions", "()Z"); 104 if (hasPathInExceptionsMethod == NULL) return -1; 105 res = (*env)->CallStaticBooleanMethod(env, cls, hasPathInExceptionsMethod); 106 if (res == JNI_TRUE) { 107 extendExceptionsWithPath = 1; 108 } else { 109 extendExceptionsWithPath = 0; 110 } 111 } 112 return extendExceptionsWithPath; 113 } 114 115 /* -- Path operations -- */ 116 117 extern int canonicalize(char *path, const char *out, int len); 118 119 JNIEXPORT jstring JNICALL 120 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this, 121 jstring pathname) 122 { 123 jstring rv = NULL; 124 125 WITH_PLATFORM_STRING(env, pathname, path) { 126 char canonicalPath[PATH_MAX]; 127 if (canonicalize((char *)path, 128 canonicalPath, PATH_MAX) < 0) { 129 if (checkExtendExceptionsWithPath(env) == 1) { 130 JNU_ThrowByNameWithTwoMessagesAndLastError(env, "java/io/IOException", "Bad pathname", (char*) path); 131 } else { 132 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); 133 } 134 } else { 135 #ifdef MACOSX 136 rv = newStringPlatform(env, canonicalPath); 137 #else 138 rv = JNU_NewStringPlatform(env, canonicalPath); 139 #endif 140 } 141 } END_PLATFORM_STRING(env, path); 142 return rv; 143 } 144 145 146 /* -- Attribute accessors -- */ 147 148 149 static jboolean 150 statMode(const char *path, int *mode) 151 { 152 struct stat sb; 153 if (stat(path, &sb) == 0) { 154 *mode = sb.st_mode; 155 return JNI_TRUE; 156 } 157 return JNI_FALSE; 158 } 159 160 161 JNIEXPORT jint JNICALL 162 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this, 163 jobject file) 164 { 165 jint rv = 0; 166 167 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 168 int mode; 169 if (statMode(path, &mode)) { 170 int fmt = mode & S_IFMT; 171 rv = (jint) (java_io_FileSystem_BA_EXISTS 172 | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0) 173 | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0)); 174 } 175 } END_PLATFORM_STRING(env, path); 176 return rv; 177 } 178 179 JNIEXPORT jboolean JNICALL 180 Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this, 181 jobject file, jint a) 182 { 183 jboolean rv = JNI_FALSE; 184 int mode = 0; 185 switch (a) { 186 case java_io_FileSystem_ACCESS_READ: 187 mode = R_OK; 188 break; 189 case java_io_FileSystem_ACCESS_WRITE: 190 mode = W_OK; 191 break; 192 case java_io_FileSystem_ACCESS_EXECUTE: 193 mode = X_OK; 194 break; 195 default: assert(0); 196 } 197 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 198 if (access(path, mode) == 0) { 199 rv = JNI_TRUE; 200 } 201 } END_PLATFORM_STRING(env, path); 202 return rv; 203 } 204 205 206 JNIEXPORT jboolean JNICALL 207 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this, 208 jobject file, 209 jint access, 210 jboolean enable, 211 jboolean owneronly) 212 { 213 jboolean rv = JNI_FALSE; 214 215 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 216 int amode = 0; 217 int mode; 218 switch (access) { 219 case java_io_FileSystem_ACCESS_READ: 220 if (owneronly) 221 amode = S_IRUSR; 222 else 223 amode = S_IRUSR | S_IRGRP | S_IROTH; 224 break; 225 case java_io_FileSystem_ACCESS_WRITE: 226 if (owneronly) 227 amode = S_IWUSR; 228 else 229 amode = S_IWUSR | S_IWGRP | S_IWOTH; 230 break; 231 case java_io_FileSystem_ACCESS_EXECUTE: 232 if (owneronly) 233 amode = S_IXUSR; 234 else 235 amode = S_IXUSR | S_IXGRP | S_IXOTH; 236 break; 237 default: 238 assert(0); 239 } 240 if (statMode(path, &mode)) { 241 if (enable) 242 mode |= amode; 243 else 244 mode &= ~amode; 245 if (chmod(path, mode) >= 0) { 246 rv = JNI_TRUE; 247 } 248 } 249 } END_PLATFORM_STRING(env, path); 250 return rv; 251 } 252 253 JNIEXPORT jlong JNICALL 254 Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this, 255 jobject file) 256 { 257 jlong rv = 0; 258 259 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 260 struct stat sb; 261 if (stat(path, &sb) == 0) { 262 #if defined(_AIX) 263 rv = (jlong)sb.st_mtime * 1000; 264 rv += (jlong)sb.st_mtime_n / 1000000; 265 #elif defined(MACOSX) 266 rv = (jlong)sb.st_mtimespec.tv_sec * 1000; 267 rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000; 268 #else 269 rv = (jlong)sb.st_mtim.tv_sec * 1000; 270 rv += (jlong)sb.st_mtim.tv_nsec / 1000000; 271 #endif 272 } 273 } END_PLATFORM_STRING(env, path); 274 return rv; 275 } 276 277 278 JNIEXPORT jlong JNICALL 279 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this, 280 jobject file) 281 { 282 jlong rv = 0; 283 284 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 285 struct stat sb; 286 if (stat(path, &sb) == 0) { 287 rv = sb.st_size; 288 } 289 } END_PLATFORM_STRING(env, path); 290 return rv; 291 } 292 293 294 /* -- File operations -- */ 295 296 297 JNIEXPORT jboolean JNICALL 298 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls, 299 jstring pathname) 300 { 301 jboolean rv = JNI_FALSE; 302 303 WITH_PLATFORM_STRING(env, pathname, path) { 304 FD fd; 305 /* The root directory always exists */ 306 if (strcmp (path, "/")) { 307 fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666); 308 if (fd < 0) { 309 if (errno != EEXIST) { 310 if (checkExtendExceptionsWithPath(env) == 1) { 311 JNU_ThrowByNameWithTwoMessagesAndLastError(env, "java/io/IOException", "handleOpen failed", path); 312 } else { 313 JNU_ThrowIOExceptionWithLastError(env, path); 314 } 315 } 316 } else { 317 if (close(fd) == -1) { 318 if (checkExtendExceptionsWithPath(env) == 1) { 319 JNU_ThrowByNameWithTwoMessagesAndLastError(env, "java/io/IOException", "close failed", path); 320 } else { 321 JNU_ThrowIOExceptionWithLastError(env, path); 322 } 323 } 324 rv = JNI_TRUE; 325 } 326 } 327 } END_PLATFORM_STRING(env, path); 328 return rv; 329 } 330 331 332 JNIEXPORT jboolean JNICALL 333 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this, 334 jobject file) 335 { 336 jboolean rv = JNI_FALSE; 337 338 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 339 if (remove(path) == 0) { 340 rv = JNI_TRUE; 341 } 342 } END_PLATFORM_STRING(env, path); 343 return rv; 344 } 345 346 347 JNIEXPORT jobjectArray JNICALL 348 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this, 349 jobject file) 350 { 351 DIR *dir = NULL; 352 struct dirent *ptr; 353 int len, maxlen; 354 jobjectArray rv, old; 355 jclass str_class; 356 357 str_class = JNU_ClassString(env); 358 CHECK_NULL_RETURN(str_class, NULL); 359 360 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 361 dir = opendir(path); 362 } END_PLATFORM_STRING(env, path); 363 if (dir == NULL) return NULL; 364 365 /* Allocate an initial String array */ 366 len = 0; 367 maxlen = 16; 368 rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL); 369 if (rv == NULL) goto error; 370 371 /* Scan the directory */ 372 while ((ptr = readdir(dir)) != NULL) { 373 jstring name; 374 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, "..")) 375 continue; 376 if (len == maxlen) { 377 old = rv; 378 rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL); 379 if (rv == NULL) goto error; 380 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; 381 (*env)->DeleteLocalRef(env, old); 382 } 383 #ifdef MACOSX 384 name = newStringPlatform(env, ptr->d_name); 385 #else 386 name = JNU_NewStringPlatform(env, ptr->d_name); 387 #endif 388 if (name == NULL) goto error; 389 (*env)->SetObjectArrayElement(env, rv, len++, name); 390 (*env)->DeleteLocalRef(env, name); 391 } 392 closedir(dir); 393 394 /* Copy the final results into an appropriately-sized array */ 395 old = rv; 396 rv = (*env)->NewObjectArray(env, len, str_class, NULL); 397 if (rv == NULL) { 398 return NULL; 399 } 400 if (JNU_CopyObjectArray(env, rv, old, len) < 0) { 401 return NULL; 402 } 403 return rv; 404 405 error: 406 closedir(dir); 407 return NULL; 408 } 409 410 411 JNIEXPORT jboolean JNICALL 412 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this, 413 jobject file) 414 { 415 jboolean rv = JNI_FALSE; 416 417 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 418 if (mkdir(path, 0777) == 0) { 419 rv = JNI_TRUE; 420 } 421 } END_PLATFORM_STRING(env, path); 422 return rv; 423 } 424 425 426 JNIEXPORT jboolean JNICALL 427 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, 428 jobject from, jobject to) 429 { 430 jboolean rv = JNI_FALSE; 431 432 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { 433 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { 434 if (rename(fromPath, toPath) == 0) { 435 rv = JNI_TRUE; 436 } 437 } END_PLATFORM_STRING(env, toPath); 438 } END_PLATFORM_STRING(env, fromPath); 439 return rv; 440 } 441 442 JNIEXPORT jboolean JNICALL 443 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, 444 jobject file, jlong time) 445 { 446 jboolean rv = JNI_FALSE; 447 448 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 449 struct stat sb; 450 451 if (stat(path, &sb) == 0) { 452 struct timeval tv[2]; 453 454 /* Preserve access time */ 455 #if defined(_AIX) 456 tv[0].tv_sec = sb.st_atime; 457 tv[0].tv_usec = sb.st_atime_n / 1000; 458 #elif defined(MACOSX) 459 tv[0].tv_sec = sb.st_atimespec.tv_sec; 460 tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; 461 #else 462 tv[0].tv_sec = sb.st_atim.tv_sec; 463 tv[0].tv_usec = sb.st_atim.tv_nsec / 1000; 464 #endif 465 /* Change last-modified time */ 466 tv[1].tv_sec = time / 1000; 467 tv[1].tv_usec = (time % 1000) * 1000; 468 469 if (utimes(path, tv) == 0) 470 rv = JNI_TRUE; 471 } 472 } END_PLATFORM_STRING(env, path); 473 474 return rv; 475 } 476 477 478 JNIEXPORT jboolean JNICALL 479 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this, 480 jobject file) 481 { 482 jboolean rv = JNI_FALSE; 483 484 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 485 int mode; 486 if (statMode(path, &mode)) { 487 if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) { 488 rv = JNI_TRUE; 489 } 490 } 491 } END_PLATFORM_STRING(env, path); 492 return rv; 493 } 494 495 JNIEXPORT jlong JNICALL 496 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this, 497 jobject file, jint t) 498 { 499 jlong rv = 0L; 500 501 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 502 #ifdef MACOSX 503 struct statfs fsstat; 504 #else 505 struct statvfs64 fsstat; 506 #endif 507 memset(&fsstat, 0, sizeof(fsstat)); 508 #ifdef MACOSX 509 if (statfs(path, &fsstat) == 0) { 510 switch(t) { 511 case java_io_FileSystem_SPACE_TOTAL: 512 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 513 long_to_jlong(fsstat.f_blocks)); 514 break; 515 case java_io_FileSystem_SPACE_FREE: 516 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 517 long_to_jlong(fsstat.f_bfree)); 518 break; 519 case java_io_FileSystem_SPACE_USABLE: 520 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 521 long_to_jlong(fsstat.f_bavail)); 522 break; 523 default: 524 assert(0); 525 } 526 } 527 #else 528 if (statvfs64(path, &fsstat) == 0) { 529 switch(t) { 530 case java_io_FileSystem_SPACE_TOTAL: 531 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 532 long_to_jlong(fsstat.f_blocks)); 533 break; 534 case java_io_FileSystem_SPACE_FREE: 535 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 536 long_to_jlong(fsstat.f_bfree)); 537 break; 538 case java_io_FileSystem_SPACE_USABLE: 539 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 540 long_to_jlong(fsstat.f_bavail)); 541 break; 542 default: 543 assert(0); 544 } 545 } 546 #endif 547 } END_PLATFORM_STRING(env, path); 548 return rv; 549 } 550 551 JNIEXPORT jlong JNICALL 552 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this, 553 jstring pathname) 554 { 555 jlong length = -1; 556 WITH_PLATFORM_STRING(env, pathname, path) { 557 length = (jlong)pathconf(path, _PC_NAME_MAX); 558 } END_PLATFORM_STRING(env, path); 559 return length != -1 ? length : (jlong)NAME_MAX; 560 }