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 }