1 /* 2 * Copyright (c) 1998, 2018, 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 if (access(path, mode) == 0) { 172 rv = JNI_TRUE; 173 } 174 } END_PLATFORM_STRING(env, path); 175 return rv; 176 } 177 178 179 JNIEXPORT jboolean JNICALL 180 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this, 181 jobject file, 182 jint access, 183 jboolean enable, 184 jboolean owneronly) 185 { 186 jboolean rv = JNI_FALSE; 187 188 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 189 int amode = 0; 190 int mode; 191 switch (access) { 192 case java_io_FileSystem_ACCESS_READ: 193 if (owneronly) 194 amode = S_IRUSR; 195 else 196 amode = S_IRUSR | S_IRGRP | S_IROTH; 197 break; 198 case java_io_FileSystem_ACCESS_WRITE: 199 if (owneronly) 200 amode = S_IWUSR; 201 else 202 amode = S_IWUSR | S_IWGRP | S_IWOTH; 203 break; 204 case java_io_FileSystem_ACCESS_EXECUTE: 205 if (owneronly) 206 amode = S_IXUSR; 207 else 208 amode = S_IXUSR | S_IXGRP | S_IXOTH; 209 break; 210 default: 211 assert(0); 212 } 213 if (statMode(path, &mode)) { 214 if (enable) 215 mode |= amode; 216 else 217 mode &= ~amode; 218 if (chmod(path, mode) >= 0) { 219 rv = JNI_TRUE; 220 } 221 } 222 } END_PLATFORM_STRING(env, path); 223 return rv; 224 } 225 226 JNIEXPORT jlong JNICALL 227 Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this, 228 jobject file) 229 { 230 jlong rv = 0; 231 232 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 233 struct stat64 sb; 234 if (stat64(path, &sb) == 0) { 235 #if defined(_AIX) 236 rv = (jlong)sb.st_mtime * 1000; 237 rv += (jlong)sb.st_mtime_n / 1000000; 238 #elif defined(MACOSX) 239 rv = (jlong)sb.st_mtimespec.tv_sec * 1000; 240 rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000; 241 #else 242 rv = (jlong)sb.st_mtim.tv_sec * 1000; 243 rv += (jlong)sb.st_mtim.tv_nsec / 1000000; 244 #endif 245 } 246 } END_PLATFORM_STRING(env, path); 247 return rv; 248 } 249 250 251 JNIEXPORT jlong JNICALL 252 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this, 253 jobject file) 254 { 255 jlong rv = 0; 256 257 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 258 struct stat64 sb; 259 if (stat64(path, &sb) == 0) { 260 rv = sb.st_size; 261 } 262 } END_PLATFORM_STRING(env, path); 263 return rv; 264 } 265 266 267 /* -- File operations -- */ 268 269 270 JNIEXPORT jboolean JNICALL 271 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls, 272 jstring pathname) 273 { 274 jboolean rv = JNI_FALSE; 275 276 WITH_PLATFORM_STRING(env, pathname, path) { 277 FD fd; 278 /* The root directory always exists */ 279 if (strcmp (path, "/")) { 280 fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666); 281 if (fd < 0) { 282 if (errno != EEXIST) 283 JNU_ThrowIOExceptionWithLastError(env, path); 284 } else { 285 if (close(fd) == -1) 286 JNU_ThrowIOExceptionWithLastError(env, path); 287 rv = JNI_TRUE; 288 } 289 } 290 } END_PLATFORM_STRING(env, path); 291 return rv; 292 } 293 294 295 JNIEXPORT jboolean JNICALL 296 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this, 297 jobject file) 298 { 299 jboolean rv = JNI_FALSE; 300 301 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 302 if (remove(path) == 0) { 303 rv = JNI_TRUE; 304 } 305 } END_PLATFORM_STRING(env, path); 306 return rv; 307 } 308 309 310 JNIEXPORT jobjectArray JNICALL 311 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this, 312 jobject file) 313 { 314 DIR *dir = NULL; 315 struct dirent *ptr; 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 /* Allocate an initial String array */ 329 len = 0; 330 maxlen = 16; 331 rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL); 332 if (rv == NULL) goto error; 333 334 /* Scan the directory */ 335 while ((ptr = readdir(dir)) != NULL) { 336 jstring name; 337 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, "..")) 338 continue; 339 if (len == maxlen) { 340 old = rv; 341 rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL); 342 if (rv == NULL) goto error; 343 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; 344 (*env)->DeleteLocalRef(env, old); 345 } 346 #ifdef MACOSX 347 name = newStringPlatform(env, ptr->d_name); 348 #else 349 name = JNU_NewStringPlatform(env, ptr->d_name); 350 #endif 351 if (name == NULL) goto error; 352 (*env)->SetObjectArrayElement(env, rv, len++, name); 353 (*env)->DeleteLocalRef(env, name); 354 } 355 closedir(dir); 356 357 /* Copy the final results into an appropriately-sized array */ 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 return rv; 367 368 error: 369 closedir(dir); 370 return NULL; 371 } 372 373 374 JNIEXPORT jboolean JNICALL 375 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this, 376 jobject file) 377 { 378 jboolean rv = JNI_FALSE; 379 380 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 381 if (mkdir(path, 0777) == 0) { 382 rv = JNI_TRUE; 383 } 384 } END_PLATFORM_STRING(env, path); 385 return rv; 386 } 387 388 389 JNIEXPORT jboolean JNICALL 390 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, 391 jobject from, jobject to) 392 { 393 jboolean rv = JNI_FALSE; 394 395 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { 396 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { 397 if (rename(fromPath, toPath) == 0) { 398 rv = JNI_TRUE; 399 } 400 } END_PLATFORM_STRING(env, toPath); 401 } END_PLATFORM_STRING(env, fromPath); 402 return rv; 403 } 404 405 JNIEXPORT jboolean JNICALL 406 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, 407 jobject file, jlong time) 408 { 409 jboolean rv = JNI_FALSE; 410 411 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 412 struct stat64 sb; 413 414 if (stat64(path, &sb) == 0) { 415 struct timeval tv[2]; 416 417 /* Preserve access time */ 418 #if defined(_AIX) 419 tv[0].tv_sec = sb.st_atime; 420 tv[0].tv_usec = sb.st_atime_n / 1000; 421 #elif defined(MACOSX) 422 tv[0].tv_sec = sb.st_atimespec.tv_sec; 423 tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; 424 #else 425 tv[0].tv_sec = sb.st_atim.tv_sec; 426 tv[0].tv_usec = sb.st_atim.tv_nsec / 1000; 427 #endif 428 /* Change last-modified time */ 429 tv[1].tv_sec = time / 1000; 430 tv[1].tv_usec = (time % 1000) * 1000; 431 432 if (utimes(path, tv) == 0) 433 rv = JNI_TRUE; 434 } 435 } END_PLATFORM_STRING(env, path); 436 437 return rv; 438 } 439 440 441 JNIEXPORT jboolean JNICALL 442 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this, 443 jobject file) 444 { 445 jboolean rv = JNI_FALSE; 446 447 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 448 int mode; 449 if (statMode(path, &mode)) { 450 if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) { 451 rv = JNI_TRUE; 452 } 453 } 454 } END_PLATFORM_STRING(env, path); 455 return rv; 456 } 457 458 JNIEXPORT jlong JNICALL 459 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this, 460 jobject file, jint t) 461 { 462 jlong rv = 0L; 463 464 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 465 #ifdef MACOSX 466 struct statfs fsstat; 467 #else 468 struct statvfs64 fsstat; 469 #endif 470 memset(&fsstat, 0, sizeof(fsstat)); 471 #ifdef MACOSX 472 if (statfs(path, &fsstat) == 0) { 473 switch(t) { 474 case java_io_FileSystem_SPACE_TOTAL: 475 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 476 long_to_jlong(fsstat.f_blocks)); 477 break; 478 case java_io_FileSystem_SPACE_FREE: 479 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 480 long_to_jlong(fsstat.f_bfree)); 481 break; 482 case java_io_FileSystem_SPACE_USABLE: 483 rv = jlong_mul(long_to_jlong(fsstat.f_bsize), 484 long_to_jlong(fsstat.f_bavail)); 485 break; 486 default: 487 assert(0); 488 } 489 } 490 #else 491 if (statvfs64(path, &fsstat) == 0) { 492 switch(t) { 493 case java_io_FileSystem_SPACE_TOTAL: 494 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 495 long_to_jlong(fsstat.f_blocks)); 496 break; 497 case java_io_FileSystem_SPACE_FREE: 498 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 499 long_to_jlong(fsstat.f_bfree)); 500 break; 501 case java_io_FileSystem_SPACE_USABLE: 502 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 503 long_to_jlong(fsstat.f_bavail)); 504 break; 505 default: 506 assert(0); 507 } 508 } 509 #endif 510 } END_PLATFORM_STRING(env, path); 511 return rv; 512 } 513 514 JNIEXPORT jlong JNICALL 515 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this, 516 jstring pathname) 517 { 518 jlong length = -1; 519 WITH_PLATFORM_STRING(env, pathname, path) { 520 length = (jlong)pathconf(path, _PC_NAME_MAX); 521 } END_PLATFORM_STRING(env, path); 522 return length != -1 ? length : (jlong)NAME_MAX; 523 }