1 /* 2 * Copyright (c) 1998, 2019, 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 "jdk_util.h" 49 #include "io_util.h" 50 #include "io_util_md.h" 51 #include "java_io_FileSystem.h" 52 #include "java_io_UnixFileSystem.h" 53 54 #if defined(_AIX) 55 #if !defined(NAME_MAX) 56 #define NAME_MAX MAXNAMLEN 57 #endif 58 #define DIR DIR64 59 #define dirent dirent64 60 #define opendir opendir64 61 #define readdir readdir64 62 #define closedir closedir64 63 #define stat stat64 64 #endif 65 66 #if defined(__solaris__) && !defined(NAME_MAX) 67 #define NAME_MAX MAXNAMLEN 68 #endif 69 70 #if defined(_ALLBSD_SOURCE) 71 #ifndef MACOSX 72 #define statvfs64 statvfs 73 #define stat64 stat 74 #endif 75 #endif 76 77 /* -- Field IDs -- */ 78 79 static struct { 80 jfieldID path; 81 } ids; 82 83 84 JNIEXPORT void JNICALL 85 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls) 86 { 87 jclass fileClass = (*env)->FindClass(env, "java/io/File"); 88 if (!fileClass) return; 89 ids.path = (*env)->GetFieldID(env, fileClass, 90 "path", "Ljava/lang/String;"); 91 } 92 93 /* -- Path operations -- */ 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 (JDK_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 int res; 171 RESTARTABLE(access(path, mode), res); 172 if (res == 0) { 173 rv = JNI_TRUE; 174 } 175 } END_PLATFORM_STRING(env, path); 176 return rv; 177 } 178 179 180 JNIEXPORT jboolean JNICALL 181 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this, 182 jobject file, 183 jint access, 184 jboolean enable, 185 jboolean owneronly) 186 { 187 jboolean rv = JNI_FALSE; 188 189 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 190 int amode = 0; 191 int mode; 192 int res; 193 switch (access) { 194 case java_io_FileSystem_ACCESS_READ: 195 if (owneronly) 196 amode = S_IRUSR; 197 else 198 amode = S_IRUSR | S_IRGRP | S_IROTH; 199 break; 200 case java_io_FileSystem_ACCESS_WRITE: 201 if (owneronly) 202 amode = S_IWUSR; 203 else 204 amode = S_IWUSR | S_IWGRP | S_IWOTH; 205 break; 206 case java_io_FileSystem_ACCESS_EXECUTE: 207 if (owneronly) 208 amode = S_IXUSR; 209 else 210 amode = S_IXUSR | S_IXGRP | S_IXOTH; 211 break; 212 default: 213 assert(0); 214 } 215 if (statMode(path, &mode)) { 216 if (enable) 217 mode |= amode; 218 else 219 mode &= ~amode; 220 RESTARTABLE(chmod(path, mode), res); 221 if (res == 0) { 222 rv = JNI_TRUE; 223 } 224 } 225 } END_PLATFORM_STRING(env, path); 226 return rv; 227 } 228 229 JNIEXPORT jlong JNICALL 230 Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this, 231 jobject file) 232 { 233 jlong rv = 0; 234 235 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 236 struct stat64 sb; 237 if (stat64(path, &sb) == 0) { 238 #if defined(_AIX) 239 rv = (jlong)sb.st_mtime * 1000; 240 rv += (jlong)sb.st_mtime_n / 1000000; 241 #elif defined(MACOSX) 242 rv = (jlong)sb.st_mtimespec.tv_sec * 1000; 243 rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000; 244 #else 245 rv = (jlong)sb.st_mtim.tv_sec * 1000; 246 rv += (jlong)sb.st_mtim.tv_nsec / 1000000; 247 #endif 248 } 249 } END_PLATFORM_STRING(env, path); 250 return rv; 251 } 252 253 254 JNIEXPORT jlong JNICALL 255 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this, 256 jobject file) 257 { 258 jlong rv = 0; 259 260 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 261 struct stat64 sb; 262 if (stat64(path, &sb) == 0) { 263 rv = sb.st_size; 264 } 265 } END_PLATFORM_STRING(env, path); 266 return rv; 267 } 268 269 270 /* -- File operations -- */ 271 272 273 JNIEXPORT jboolean JNICALL 274 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls, 275 jstring pathname) 276 { 277 jboolean rv = JNI_FALSE; 278 279 WITH_PLATFORM_STRING(env, pathname, path) { 280 FD fd; 281 /* The root directory always exists */ 282 if (strcmp (path, "/")) { 283 fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666); 284 if (fd < 0) { 285 if (errno != EEXIST) 286 JNU_ThrowIOExceptionWithLastError(env, "Could not open file"); 287 } else { 288 if (close(fd) == -1) 289 JNU_ThrowIOExceptionWithLastError(env, "Could not close file"); 290 rv = JNI_TRUE; 291 } 292 } 293 } END_PLATFORM_STRING(env, path); 294 return rv; 295 } 296 297 298 JNIEXPORT jboolean JNICALL 299 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this, 300 jobject file) 301 { 302 jboolean rv = JNI_FALSE; 303 304 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 305 if (remove(path) == 0) { 306 rv = JNI_TRUE; 307 } 308 } END_PLATFORM_STRING(env, path); 309 return rv; 310 } 311 312 313 JNIEXPORT jobjectArray JNICALL 314 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this, 315 jobject file) 316 { 317 DIR *dir = NULL; 318 struct dirent *ptr; 319 int len, maxlen; 320 jobjectArray rv, old; 321 jclass str_class; 322 323 str_class = JNU_ClassString(env); 324 CHECK_NULL_RETURN(str_class, NULL); 325 326 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 327 dir = opendir(path); 328 } END_PLATFORM_STRING(env, path); 329 if (dir == NULL) return NULL; 330 331 /* Allocate an initial String array */ 332 len = 0; 333 maxlen = 16; 334 rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL); 335 if (rv == NULL) goto error; 336 337 /* Scan the directory */ 338 while ((ptr = readdir(dir)) != NULL) { 339 jstring name; 340 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, "..")) 341 continue; 342 if (len == maxlen) { 343 old = rv; 344 rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL); 345 if (rv == NULL) goto error; 346 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; 347 (*env)->DeleteLocalRef(env, old); 348 } 349 #ifdef MACOSX 350 name = newStringPlatform(env, ptr->d_name); 351 #else 352 name = JNU_NewStringPlatform(env, ptr->d_name); 353 #endif 354 if (name == NULL) goto error; 355 (*env)->SetObjectArrayElement(env, rv, len++, name); 356 (*env)->DeleteLocalRef(env, name); 357 } 358 closedir(dir); 359 360 /* Copy the final results into an appropriately-sized array */ 361 if (len < maxlen) { 362 old = rv; 363 rv = (*env)->NewObjectArray(env, len, str_class, NULL); 364 if (rv == NULL) { 365 return NULL; 366 } 367 if (JNU_CopyObjectArray(env, rv, old, len) < 0) { 368 return NULL; 369 } 370 } 371 return rv; 372 373 error: 374 closedir(dir); 375 return NULL; 376 } 377 378 379 JNIEXPORT jboolean JNICALL 380 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this, 381 jobject file) 382 { 383 jboolean rv = JNI_FALSE; 384 385 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 386 if (mkdir(path, 0777) == 0) { 387 rv = JNI_TRUE; 388 } 389 } END_PLATFORM_STRING(env, path); 390 return rv; 391 } 392 393 394 JNIEXPORT jboolean JNICALL 395 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, 396 jobject from, jobject to) 397 { 398 jboolean rv = JNI_FALSE; 399 400 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { 401 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { 402 if (rename(fromPath, toPath) == 0) { 403 rv = JNI_TRUE; 404 } 405 } END_PLATFORM_STRING(env, toPath); 406 } END_PLATFORM_STRING(env, fromPath); 407 return rv; 408 } 409 410 JNIEXPORT jboolean JNICALL 411 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, 412 jobject file, jlong time) 413 { 414 jboolean rv = JNI_FALSE; 415 416 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 417 struct stat64 sb; 418 419 if (stat64(path, &sb) == 0) { 420 struct timeval tv[2]; 421 422 /* Preserve access time */ 423 #if defined(_AIX) 424 tv[0].tv_sec = sb.st_atime; 425 tv[0].tv_usec = sb.st_atime_n / 1000; 426 #elif defined(MACOSX) 427 tv[0].tv_sec = sb.st_atimespec.tv_sec; 428 tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; 429 #else 430 tv[0].tv_sec = sb.st_atim.tv_sec; 431 tv[0].tv_usec = sb.st_atim.tv_nsec / 1000; 432 #endif 433 /* Change last-modified time */ 434 tv[1].tv_sec = time / 1000; 435 tv[1].tv_usec = (time % 1000) * 1000; 436 437 if (utimes(path, tv) == 0) 438 rv = JNI_TRUE; 439 } 440 } END_PLATFORM_STRING(env, path); 441 442 return rv; 443 } 444 445 446 JNIEXPORT jboolean JNICALL 447 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this, 448 jobject file) 449 { 450 jboolean rv = JNI_FALSE; 451 452 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 453 int mode; 454 int res; 455 if (statMode(path, &mode)) { 456 RESTARTABLE(chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)), res); 457 if (res == 0) { 458 rv = JNI_TRUE; 459 } 460 } 461 } END_PLATFORM_STRING(env, path); 462 return rv; 463 } 464 465 JNIEXPORT jlong JNICALL 466 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this, 467 jobject file, jint t) 468 { 469 jlong rv = 0L; 470 471 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 472 #ifdef MACOSX 473 struct statfs fsstat; 474 #else 475 struct statvfs64 fsstat; 476 int res; 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 RESTARTABLE(statvfs64(path, &fsstat), res); 500 if (res == 0) { 501 switch(t) { 502 case java_io_FileSystem_SPACE_TOTAL: 503 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 504 long_to_jlong(fsstat.f_blocks)); 505 break; 506 case java_io_FileSystem_SPACE_FREE: 507 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 508 long_to_jlong(fsstat.f_bfree)); 509 break; 510 case java_io_FileSystem_SPACE_USABLE: 511 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 512 long_to_jlong(fsstat.f_bavail)); 513 break; 514 default: 515 assert(0); 516 } 517 } 518 #endif 519 } END_PLATFORM_STRING(env, path); 520 return rv; 521 } 522 523 JNIEXPORT jlong JNICALL 524 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this, 525 jstring pathname) 526 { 527 jlong length = -1; 528 WITH_PLATFORM_STRING(env, pathname, path) { 529 length = (jlong)pathconf(path, _PC_NAME_MAX); 530 } END_PLATFORM_STRING(env, path); 531 return length != -1 ? length : (jlong)NAME_MAX; 532 }