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