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