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 42 #include "jni.h" 43 #include "jni_util.h" 44 #include "jlong.h" 45 #include "jvm.h" 46 #include "io_util.h" 47 #include "io_util_md.h" 48 #include "java_io_FileSystem.h" 49 #include "java_io_UnixFileSystem.h" 50 51 #if defined(_AIX) 52 #if !defined(NAME_MAX) 53 #define NAME_MAX MAXNAMLEN 54 #endif 55 #define DIR DIR64 56 #define opendir opendir64 57 #define closedir closedir64 58 #endif 59 60 #if defined(__solaris__) && !defined(NAME_MAX) 61 #define NAME_MAX MAXNAMLEN 62 #endif 63 64 #if defined(_ALLBSD_SOURCE) 65 #define dirent64 dirent 66 #define readdir64_r readdir_r 67 #define stat64 stat 68 #ifndef MACOSX 69 #define statvfs64 statvfs 70 #endif 71 #endif 72 73 /* -- Field IDs -- */ 74 75 static struct { 76 jfieldID path; 77 } ids; 78 79 80 JNIEXPORT void JNICALL 81 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls) 82 { 83 jclass fileClass = (*env)->FindClass(env, "java/io/File"); 84 if (!fileClass) return; 85 ids.path = (*env)->GetFieldID(env, fileClass, 86 "path", "Ljava/lang/String;"); 87 } 88 89 /* -- Path operations -- */ 90 91 extern int canonicalize(char *path, const char *out, int len); 92 93 JNIEXPORT jstring JNICALL 94 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this, 95 jstring pathname) 96 { 97 jstring rv = NULL; 98 99 WITH_PLATFORM_STRING(env, pathname, path) { 100 char canonicalPath[JVM_MAXPATHLEN]; 101 if (canonicalize((char *)path, 102 canonicalPath, JVM_MAXPATHLEN) < 0) { 103 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); 104 } else { 105 #ifdef MACOSX 106 rv = newStringPlatform(env, canonicalPath); 107 #else 108 rv = JNU_NewStringPlatform(env, canonicalPath); 109 #endif 110 } 111 } END_PLATFORM_STRING(env, path); 112 return rv; 113 } 114 115 116 /* -- Attribute accessors -- */ 117 118 119 static jboolean 120 statMode(const char *path, int *mode) 121 { 122 struct stat64 sb; 123 if (stat64(path, &sb) == 0) { 124 *mode = sb.st_mode; 125 return JNI_TRUE; 126 } 127 return JNI_FALSE; 128 } 129 130 131 JNIEXPORT jint JNICALL 132 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this, 133 jobject file) 134 { 135 jint rv = 0; 136 137 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 138 int mode; 139 if (statMode(path, &mode)) { 140 int fmt = mode & S_IFMT; 141 rv = (jint) (java_io_FileSystem_BA_EXISTS 142 | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0) 143 | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0)); 144 } 145 } END_PLATFORM_STRING(env, path); 146 return rv; 147 } 148 149 JNIEXPORT jboolean JNICALL 150 Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this, 151 jobject file, jint a) 152 { 153 jboolean rv = JNI_FALSE; 154 int mode = 0; 155 switch (a) { 156 case java_io_FileSystem_ACCESS_READ: 157 mode = R_OK; 158 break; 159 case java_io_FileSystem_ACCESS_WRITE: 160 mode = W_OK; 161 break; 162 case java_io_FileSystem_ACCESS_EXECUTE: 163 mode = X_OK; 164 break; 165 default: assert(0); 166 } 167 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 168 if (access(path, mode) == 0) { 169 rv = JNI_TRUE; 170 } 171 } END_PLATFORM_STRING(env, path); 172 return rv; 173 } 174 175 176 JNIEXPORT jboolean JNICALL 177 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this, 178 jobject file, 179 jint access, 180 jboolean enable, 181 jboolean owneronly) 182 { 183 jboolean rv = JNI_FALSE; 184 185 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 186 int amode = 0; 187 int mode; 188 switch (access) { 189 case java_io_FileSystem_ACCESS_READ: 190 if (owneronly) 191 amode = S_IRUSR; 192 else 193 amode = S_IRUSR | S_IRGRP | S_IROTH; 194 break; 195 case java_io_FileSystem_ACCESS_WRITE: 196 if (owneronly) 197 amode = S_IWUSR; 198 else 199 amode = S_IWUSR | S_IWGRP | S_IWOTH; 200 break; 201 case java_io_FileSystem_ACCESS_EXECUTE: 202 if (owneronly) 203 amode = S_IXUSR; 204 else 205 amode = S_IXUSR | S_IXGRP | S_IXOTH; 206 break; 207 default: 208 assert(0); 209 } 210 if (statMode(path, &mode)) { 211 if (enable) 212 mode |= amode; 213 else 214 mode &= ~amode; 215 if (chmod(path, mode) >= 0) { 216 rv = JNI_TRUE; 217 } 218 } 219 } END_PLATFORM_STRING(env, path); 220 return rv; 221 } 222 223 JNIEXPORT jlong JNICALL 224 Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this, 225 jobject file) 226 { 227 jlong rv = 0; 228 229 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 230 struct stat64 sb; 231 if (stat64(path, &sb) == 0) { 232 #if defined(_AIX) 233 /* AIX 5.3 backward compatibility. */ 234 rv = (jlong)sb.st_mtime * 1000; 235 rv += (jlong)sb.st_mtime_n / 1000000; 236 #elif defined(MACOSX) 237 rv = (jlong)sb.st_mtimespec.tv_sec * 1000; 238 rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000; 239 #else 240 rv = (jlong)sb.st_mtim.tv_sec * 1000; 241 rv += (jlong)sb.st_mtim.tv_nsec / 1000000; 242 #endif 243 } 244 } END_PLATFORM_STRING(env, path); 245 return rv; 246 } 247 248 249 JNIEXPORT jlong JNICALL 250 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this, 251 jobject file) 252 { 253 jlong rv = 0; 254 255 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 256 struct stat64 sb; 257 if (stat64(path, &sb) == 0) { 258 rv = sb.st_size; 259 } 260 } END_PLATFORM_STRING(env, path); 261 return rv; 262 } 263 264 265 /* -- File operations -- */ 266 267 268 JNIEXPORT jboolean JNICALL 269 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls, 270 jstring pathname) 271 { 272 jboolean rv = JNI_FALSE; 273 274 WITH_PLATFORM_STRING(env, pathname, path) { 275 FD fd; 276 /* The root directory always exists */ 277 if (strcmp (path, "/")) { 278 fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666); 279 if (fd < 0) { 280 if (errno != EEXIST) 281 JNU_ThrowIOExceptionWithLastError(env, path); 282 } else { 283 if (close(fd) == -1) 284 JNU_ThrowIOExceptionWithLastError(env, path); 285 rv = JNI_TRUE; 286 } 287 } 288 } END_PLATFORM_STRING(env, path); 289 return rv; 290 } 291 292 293 JNIEXPORT jboolean JNICALL 294 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this, 295 jobject file) 296 { 297 jboolean rv = JNI_FALSE; 298 299 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 300 if (remove(path) == 0) { 301 rv = JNI_TRUE; 302 } 303 } END_PLATFORM_STRING(env, path); 304 return rv; 305 } 306 307 308 JNIEXPORT jobjectArray JNICALL 309 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this, 310 jobject file) 311 { 312 DIR *dir = NULL; 313 struct dirent64 *ptr; 314 struct dirent64 *result; 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 ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1)); 328 if (ptr == NULL) { 329 JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); 330 closedir(dir); 331 return NULL; 332 } 333 334 /* Allocate an initial String array */ 335 len = 0; 336 maxlen = 16; 337 rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL); 338 if (rv == NULL) goto error; 339 340 /* Scan the directory */ 341 while ((readdir64_r(dir, ptr, &result) == 0) && (result != NULL)) { 342 jstring name; 343 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, "..")) 344 continue; 345 if (len == maxlen) { 346 old = rv; 347 rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL); 348 if (rv == NULL) goto error; 349 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; 350 (*env)->DeleteLocalRef(env, old); 351 } 352 #ifdef MACOSX 353 name = newStringPlatform(env, ptr->d_name); 354 #else 355 name = JNU_NewStringPlatform(env, ptr->d_name); 356 #endif 357 if (name == NULL) goto error; 358 (*env)->SetObjectArrayElement(env, rv, len++, name); 359 (*env)->DeleteLocalRef(env, name); 360 } 361 closedir(dir); 362 free(ptr); 363 364 /* Copy the final results into an appropriately-sized array */ 365 old = rv; 366 rv = (*env)->NewObjectArray(env, len, str_class, NULL); 367 if (rv == NULL) { 368 return NULL; 369 } 370 if (JNU_CopyObjectArray(env, rv, old, len) < 0) { 371 return NULL; 372 } 373 return rv; 374 375 error: 376 closedir(dir); 377 free(ptr); 378 return NULL; 379 } 380 381 382 JNIEXPORT jboolean JNICALL 383 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this, 384 jobject file) 385 { 386 jboolean rv = JNI_FALSE; 387 388 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 389 if (mkdir(path, 0777) == 0) { 390 rv = JNI_TRUE; 391 } 392 } END_PLATFORM_STRING(env, path); 393 return rv; 394 } 395 396 397 JNIEXPORT jboolean JNICALL 398 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, 399 jobject from, jobject to) 400 { 401 jboolean rv = JNI_FALSE; 402 403 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { 404 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { 405 if (rename(fromPath, toPath) == 0) { 406 rv = JNI_TRUE; 407 } 408 } END_PLATFORM_STRING(env, toPath); 409 } END_PLATFORM_STRING(env, fromPath); 410 return rv; 411 } 412 413 JNIEXPORT jboolean JNICALL 414 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, 415 jobject file, jlong time) 416 { 417 jboolean rv = JNI_FALSE; 418 419 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 420 struct stat64 sb; 421 422 if (stat64(path, &sb) == 0) { 423 struct timeval tv[2]; 424 425 /* Preserve access time */ 426 #if defined(_AIX) 427 tv[0].tv_sec = sb.st_atime; 428 tv[0].tv_usec = sb.st_atime_n / 1000; 429 #elif defined(MACOSX) 430 tv[0].tv_sec = sb.st_atimespec.tv_sec; 431 tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; 432 #else 433 tv[0].tv_sec = sb.st_atim.tv_sec; 434 tv[0].tv_usec = sb.st_atim.tv_nsec / 1000; 435 #endif 436 /* Change last-modified time */ 437 tv[1].tv_sec = time / 1000; 438 tv[1].tv_usec = (time % 1000) * 1000; 439 440 if (utimes(path, tv) == 0) 441 rv = JNI_TRUE; 442 } 443 } END_PLATFORM_STRING(env, path); 444 445 return rv; 446 } 447 448 449 JNIEXPORT jboolean JNICALL 450 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this, 451 jobject file) 452 { 453 jboolean rv = JNI_FALSE; 454 455 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 456 int mode; 457 if (statMode(path, &mode)) { 458 if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) { 459 rv = JNI_TRUE; 460 } 461 } 462 } END_PLATFORM_STRING(env, path); 463 return rv; 464 } 465 466 JNIEXPORT jlong JNICALL 467 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this, 468 jobject file, jint t) 469 { 470 jlong rv = 0L; 471 472 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 473 #ifdef MACOSX 474 struct statfs fsstat; 475 #else 476 struct statvfs64 fsstat; 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 if (statvfs64(path, &fsstat) == 0) { 500 switch(t) { 501 case java_io_FileSystem_SPACE_TOTAL: 502 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 503 long_to_jlong(fsstat.f_blocks)); 504 break; 505 case java_io_FileSystem_SPACE_FREE: 506 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 507 long_to_jlong(fsstat.f_bfree)); 508 break; 509 case java_io_FileSystem_SPACE_USABLE: 510 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 511 long_to_jlong(fsstat.f_bavail)); 512 break; 513 default: 514 assert(0); 515 } 516 } 517 #endif 518 } END_PLATFORM_STRING(env, path); 519 return rv; 520 } 521 522 JNIEXPORT jlong JNICALL 523 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this, 524 jstring pathname) 525 { 526 jlong length = -1; 527 WITH_PLATFORM_STRING(env, pathname, path) { 528 length = (jlong)pathconf(path, _PC_NAME_MAX); 529 } END_PLATFORM_STRING(env, path); 530 return length != -1 ? length : (jlong)NAME_MAX; 531 }