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