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