1 /* 2 * Copyright (c) 1998, 2020, 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(_ALLBSD_SOURCE) 67 #ifndef MACOSX 68 #define statvfs64 statvfs 69 #define stat64 stat 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 JNIEXPORT jstring JNICALL 92 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this, 93 jstring pathname) 94 { 95 jstring rv = NULL; 96 97 WITH_PLATFORM_STRING(env, pathname, path) { 98 char canonicalPath[PATH_MAX]; 99 if (JDK_Canonicalize((char *)path, 100 canonicalPath, PATH_MAX) < 0) { 101 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); 102 } else { 103 #ifdef MACOSX 104 rv = newStringPlatform(env, canonicalPath); 105 #else 106 rv = JNU_NewStringPlatform(env, canonicalPath); 107 #endif 108 } 109 } END_PLATFORM_STRING(env, path); 110 return rv; 111 } 112 113 114 /* -- Attribute accessors -- */ 115 116 117 static jboolean 118 statMode(const char *path, int *mode) 119 { 120 struct stat64 sb; 121 if (stat64(path, &sb) == 0) { 122 *mode = sb.st_mode; 123 return JNI_TRUE; 124 } 125 return JNI_FALSE; 126 } 127 128 129 JNIEXPORT jint JNICALL 130 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this, 131 jobject file) 132 { 133 jint rv = 0; 134 135 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 136 int mode; 137 if (statMode(path, &mode)) { 138 int fmt = mode & S_IFMT; 139 rv = (jint) (java_io_FileSystem_BA_EXISTS 140 | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0) 141 | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0)); 142 } 143 } END_PLATFORM_STRING(env, path); 144 return rv; 145 } 146 147 JNIEXPORT jboolean JNICALL 148 Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this, 149 jobject file, jint a) 150 { 151 jboolean rv = JNI_FALSE; 152 int mode = 0; 153 switch (a) { 154 case java_io_FileSystem_ACCESS_READ: 155 mode = R_OK; 156 break; 157 case java_io_FileSystem_ACCESS_WRITE: 158 mode = W_OK; 159 break; 160 case java_io_FileSystem_ACCESS_EXECUTE: 161 mode = X_OK; 162 break; 163 default: assert(0); 164 } 165 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 166 int res; 167 RESTARTABLE(access(path, mode), res); 168 if (res == 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 int res; 189 switch (access) { 190 case java_io_FileSystem_ACCESS_READ: 191 if (owneronly) 192 amode = S_IRUSR; 193 else 194 amode = S_IRUSR | S_IRGRP | S_IROTH; 195 break; 196 case java_io_FileSystem_ACCESS_WRITE: 197 if (owneronly) 198 amode = S_IWUSR; 199 else 200 amode = S_IWUSR | S_IWGRP | S_IWOTH; 201 break; 202 case java_io_FileSystem_ACCESS_EXECUTE: 203 if (owneronly) 204 amode = S_IXUSR; 205 else 206 amode = S_IXUSR | S_IXGRP | S_IXOTH; 207 break; 208 default: 209 assert(0); 210 } 211 if (statMode(path, &mode)) { 212 if (enable) 213 mode |= amode; 214 else 215 mode &= ~amode; 216 RESTARTABLE(chmod(path, mode), res); 217 if (res == 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, "Could not open file"); 283 } else { 284 if (close(fd) == -1) 285 JNU_ThrowIOExceptionWithLastError(env, "Could not close file"); 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 dirent *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 = readdir(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 if (len < maxlen) { 358 old = rv; 359 rv = (*env)->NewObjectArray(env, len, str_class, NULL); 360 if (rv == NULL) { 361 return NULL; 362 } 363 if (JNU_CopyObjectArray(env, rv, old, len) < 0) { 364 return NULL; 365 } 366 } 367 return rv; 368 369 error: 370 closedir(dir); 371 return NULL; 372 } 373 374 375 JNIEXPORT jboolean JNICALL 376 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this, 377 jobject file) 378 { 379 jboolean rv = JNI_FALSE; 380 381 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 382 if (mkdir(path, 0777) == 0) { 383 rv = JNI_TRUE; 384 } 385 } END_PLATFORM_STRING(env, path); 386 return rv; 387 } 388 389 390 JNIEXPORT jboolean JNICALL 391 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, 392 jobject from, jobject to) 393 { 394 jboolean rv = JNI_FALSE; 395 396 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { 397 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { 398 if (rename(fromPath, toPath) == 0) { 399 rv = JNI_TRUE; 400 } 401 } END_PLATFORM_STRING(env, toPath); 402 } END_PLATFORM_STRING(env, fromPath); 403 return rv; 404 } 405 406 JNIEXPORT jboolean JNICALL 407 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, 408 jobject file, jlong time) 409 { 410 jboolean rv = JNI_FALSE; 411 412 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 413 struct stat64 sb; 414 415 if (stat64(path, &sb) == 0) { 416 struct timeval tv[2]; 417 418 /* Preserve access time */ 419 #if defined(_AIX) 420 tv[0].tv_sec = sb.st_atime; 421 tv[0].tv_usec = sb.st_atime_n / 1000; 422 #elif defined(MACOSX) 423 tv[0].tv_sec = sb.st_atimespec.tv_sec; 424 tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; 425 #else 426 tv[0].tv_sec = sb.st_atim.tv_sec; 427 tv[0].tv_usec = sb.st_atim.tv_nsec / 1000; 428 #endif 429 /* Change last-modified time */ 430 tv[1].tv_sec = time / 1000; 431 tv[1].tv_usec = (time % 1000) * 1000; 432 433 if (utimes(path, tv) == 0) 434 rv = JNI_TRUE; 435 } 436 } END_PLATFORM_STRING(env, path); 437 438 return rv; 439 } 440 441 442 JNIEXPORT jboolean JNICALL 443 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this, 444 jobject file) 445 { 446 jboolean rv = JNI_FALSE; 447 448 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 449 int mode; 450 int res; 451 if (statMode(path, &mode)) { 452 RESTARTABLE(chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)), res); 453 if (res == 0) { 454 rv = JNI_TRUE; 455 } 456 } 457 } END_PLATFORM_STRING(env, path); 458 return rv; 459 } 460 461 JNIEXPORT jlong JNICALL 462 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this, 463 jobject file, jint t) 464 { 465 jlong rv = 0L; 466 467 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 468 #ifdef MACOSX 469 struct statfs fsstat; 470 #else 471 struct statvfs64 fsstat; 472 int res; 473 #endif 474 memset(&fsstat, 0, sizeof(fsstat)); 475 #ifdef MACOSX 476 if (statfs(path, &fsstat) == 0) { 477 switch(t) { 478 case java_io_FileSystem_SPACE_TOTAL: 479 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 480 long_to_jlong(fsstat.f_blocks)); 481 break; 482 case java_io_FileSystem_SPACE_FREE: 483 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 484 long_to_jlong(fsstat.f_bfree)); 485 break; 486 case java_io_FileSystem_SPACE_USABLE: 487 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 488 long_to_jlong(fsstat.f_bavail)); 489 break; 490 default: 491 assert(0); 492 } 493 } 494 #else 495 RESTARTABLE(statvfs64(path, &fsstat), res); 496 if (res == 0) { 497 switch(t) { 498 case java_io_FileSystem_SPACE_TOTAL: 499 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 500 long_to_jlong(fsstat.f_blocks)); 501 break; 502 case java_io_FileSystem_SPACE_FREE: 503 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 504 long_to_jlong(fsstat.f_bfree)); 505 break; 506 case java_io_FileSystem_SPACE_USABLE: 507 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 508 long_to_jlong(fsstat.f_bavail)); 509 break; 510 default: 511 assert(0); 512 } 513 } 514 #endif 515 } END_PLATFORM_STRING(env, path); 516 return rv; 517 } 518 519 JNIEXPORT jlong JNICALL 520 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this, 521 jstring pathname) 522 { 523 jlong length = -1; 524 WITH_PLATFORM_STRING(env, pathname, path) { 525 length = (jlong)pathconf(path, _PC_NAME_MAX); 526 } END_PLATFORM_STRING(env, path); 527 return length != -1 ? length : (jlong)NAME_MAX; 528 }