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(JVM_NativePath((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 JNIEXPORT jboolean JNICALL 137 Java_java_io_UnixFileSystem_checkAccess0(JNIEnv *env, jobject this, 138 jobject file, jint a) 139 { 140 jboolean rv = JNI_FALSE; 141 int mode = 0; 142 switch (a) { 143 case java_io_FileSystem_ACCESS_OK: 144 mode = F_OK; 145 break; 146 case java_io_FileSystem_ACCESS_READ: 147 mode = R_OK; 148 break; 149 case java_io_FileSystem_ACCESS_WRITE: 150 mode = W_OK; 151 break; 152 case java_io_FileSystem_ACCESS_EXECUTE: 153 mode = X_OK; 154 break; 155 default: assert(0); 156 } 157 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 158 if (access(path, mode) == 0) { 159 rv = JNI_TRUE; 160 } 161 } END_PLATFORM_STRING(env, path); 162 return rv; 163 } 164 165 166 JNIEXPORT jboolean JNICALL 167 Java_java_io_UnixFileSystem_setPermission0(JNIEnv *env, jobject this, 168 jobject file, 169 jint access, 170 jboolean enable, 171 jboolean owneronly) 172 { 173 jboolean rv = JNI_FALSE; 174 175 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 176 int amode = 0; 177 int mode; 178 switch (access) { 179 case java_io_FileSystem_ACCESS_READ: 180 if (owneronly) 181 amode = S_IRUSR; 182 else 183 amode = S_IRUSR | S_IRGRP | S_IROTH; 184 break; 185 case java_io_FileSystem_ACCESS_WRITE: 186 if (owneronly) 187 amode = S_IWUSR; 188 else 189 amode = S_IWUSR | S_IWGRP | S_IWOTH; 190 break; 191 case java_io_FileSystem_ACCESS_EXECUTE: 192 if (owneronly) 193 amode = S_IXUSR; 194 else 195 amode = S_IXUSR | S_IXGRP | S_IXOTH; 196 break; 197 default: 198 assert(0); 199 } 200 if (statMode(path, &mode)) { 201 if (enable) 202 mode |= amode; 203 else 204 mode &= ~amode; 205 if (chmod(path, mode) >= 0) { 206 rv = JNI_TRUE; 207 } 208 } 209 } END_PLATFORM_STRING(env, path); 210 return rv; 211 } 212 213 JNIEXPORT jlong JNICALL 214 Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv *env, jobject this, 215 jobject file) 216 { 217 jlong rv = 0; 218 219 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 220 struct stat64 sb; 221 if (stat64(path, &sb) == 0) { 222 rv = 1000 * (jlong)sb.st_mtime; 223 } 224 } END_PLATFORM_STRING(env, path); 225 return rv; 226 } 227 228 229 JNIEXPORT jlong JNICALL 230 Java_java_io_UnixFileSystem_getLength0(JNIEnv *env, jobject this, 231 jobject file) 232 { 233 jlong rv = 0; 234 235 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 236 struct stat64 sb; 237 if (stat64(path, &sb) == 0) { 238 rv = sb.st_size; 239 } 240 } END_PLATFORM_STRING(env, path); 241 return rv; 242 } 243 244 245 /* -- File operations -- */ 246 247 248 JNIEXPORT jboolean JNICALL 249 Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls, 250 jstring pathname) 251 { 252 jboolean rv = JNI_FALSE; 253 254 WITH_PLATFORM_STRING(env, pathname, path) { 255 int fd; 256 if (!strcmp (path, "/")) { 257 fd = JVM_EEXIST; /* The root directory always exists */ 258 } else { 259 fd = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666); 260 } 261 if (fd < 0) { 262 if (fd != JVM_EEXIST) { 263 JNU_ThrowIOExceptionWithLastError(env, path); 264 } 265 } else { 266 JVM_Close(fd); 267 rv = JNI_TRUE; 268 } 269 } END_PLATFORM_STRING(env, path); 270 return rv; 271 } 272 273 274 JNIEXPORT jboolean JNICALL 275 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this, 276 jobject file) 277 { 278 jboolean rv = JNI_FALSE; 279 280 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 281 if (remove(path) == 0) { 282 rv = JNI_TRUE; 283 } 284 } END_PLATFORM_STRING(env, path); 285 return rv; 286 } 287 288 289 JNIEXPORT jobjectArray JNICALL 290 Java_java_io_UnixFileSystem_list0(JNIEnv *env, jobject this, 291 jobject file) 292 { 293 DIR *dir = NULL; 294 struct dirent64 *ptr; 295 struct dirent64 *result; 296 int len, maxlen; 297 jobjectArray rv, old; 298 299 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 300 dir = opendir(path); 301 } END_PLATFORM_STRING(env, path); 302 if (dir == NULL) return NULL; 303 304 ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1)); 305 if (ptr == NULL) { 306 JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); 307 closedir(dir); 308 return NULL; 309 } 310 311 /* Allocate an initial String array */ 312 len = 0; 313 maxlen = 16; 314 rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL); 315 if (rv == NULL) goto error; 316 317 /* Scan the directory */ 318 while ((readdir64_r(dir, ptr, &result) == 0) && (result != NULL)) { 319 jstring name; 320 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, "..")) 321 continue; 322 if (len == maxlen) { 323 old = rv; 324 rv = (*env)->NewObjectArray(env, maxlen <<= 1, 325 JNU_ClassString(env), NULL); 326 if (rv == NULL) goto error; 327 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; 328 (*env)->DeleteLocalRef(env, old); 329 } 330 #ifdef MACOSX 331 name = newStringPlatform(env, ptr->d_name); 332 #else 333 name = JNU_NewStringPlatform(env, ptr->d_name); 334 #endif 335 if (name == NULL) goto error; 336 (*env)->SetObjectArrayElement(env, rv, len++, name); 337 (*env)->DeleteLocalRef(env, name); 338 } 339 closedir(dir); 340 free(ptr); 341 342 /* Copy the final results into an appropriately-sized array */ 343 old = rv; 344 rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL); 345 if (rv == NULL) { 346 return NULL; 347 } 348 if (JNU_CopyObjectArray(env, rv, old, len) < 0) { 349 return NULL; 350 } 351 return rv; 352 353 error: 354 closedir(dir); 355 free(ptr); 356 return NULL; 357 } 358 359 360 JNIEXPORT jboolean JNICALL 361 Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this, 362 jobject file) 363 { 364 jboolean rv = JNI_FALSE; 365 366 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 367 if (mkdir(path, 0777) == 0) { 368 rv = JNI_TRUE; 369 } 370 } END_PLATFORM_STRING(env, path); 371 return rv; 372 } 373 374 375 JNIEXPORT jboolean JNICALL 376 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, 377 jobject from, jobject to) 378 { 379 jboolean rv = JNI_FALSE; 380 381 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { 382 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { 383 if (rename(fromPath, toPath) == 0) { 384 rv = JNI_TRUE; 385 } 386 } END_PLATFORM_STRING(env, toPath); 387 } END_PLATFORM_STRING(env, fromPath); 388 return rv; 389 } 390 391 JNIEXPORT jboolean JNICALL 392 Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this, 393 jobject file, jlong time) 394 { 395 jboolean rv = JNI_FALSE; 396 397 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 398 struct stat64 sb; 399 400 if (stat64(path, &sb) == 0) { 401 struct timeval tv[2]; 402 403 /* Preserve access time */ 404 tv[0].tv_sec = sb.st_atime; 405 tv[0].tv_usec = 0; 406 407 /* Change last-modified time */ 408 tv[1].tv_sec = time / 1000; 409 tv[1].tv_usec = (time % 1000) * 1000; 410 411 if (utimes(path, tv) == 0) 412 rv = JNI_TRUE; 413 } 414 } END_PLATFORM_STRING(env, path); 415 416 return rv; 417 } 418 419 420 JNIEXPORT jboolean JNICALL 421 Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this, 422 jobject file) 423 { 424 jboolean rv = JNI_FALSE; 425 426 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 427 int mode; 428 if (statMode(path, &mode)) { 429 if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) { 430 rv = JNI_TRUE; 431 } 432 } 433 } END_PLATFORM_STRING(env, path); 434 return rv; 435 } 436 437 JNIEXPORT jlong JNICALL 438 Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this, 439 jobject file, jint t) 440 { 441 jlong rv = 0L; 442 443 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 444 struct statvfs64 fsstat; 445 memset(&fsstat, 0, sizeof(fsstat)); 446 if (statvfs64(path, &fsstat) == 0) { 447 switch(t) { 448 case java_io_FileSystem_SPACE_TOTAL: 449 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 450 long_to_jlong(fsstat.f_blocks)); 451 break; 452 case java_io_FileSystem_SPACE_FREE: 453 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 454 long_to_jlong(fsstat.f_bfree)); 455 break; 456 case java_io_FileSystem_SPACE_USABLE: 457 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 458 long_to_jlong(fsstat.f_bavail)); 459 break; 460 default: 461 assert(0); 462 } 463 } 464 } END_PLATFORM_STRING(env, path); 465 return rv; 466 } 467 468 static JNINativeMethod gMethods[] = { 469 NATIVE_METHOD(UnixFileSystem, initIDs, "()V"), 470 NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"), 471 NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"), 472 NATIVE_METHOD(UnixFileSystem, checkAccess0, "(Ljava/io/File;I)Z"), 473 NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"), 474 NATIVE_METHOD(UnixFileSystem, getLastModifiedTime0, "(Ljava/io/File;)J"), 475 NATIVE_METHOD(UnixFileSystem, getLength0, "(Ljava/io/File;)J"), 476 NATIVE_METHOD(UnixFileSystem, createFileExclusively0, "(Ljava/lang/String;)Z"), 477 NATIVE_METHOD(UnixFileSystem, delete0, "(Ljava/io/File;)Z"), 478 NATIVE_METHOD(UnixFileSystem, list0, "(Ljava/io/File;)[Ljava/lang/String;"), 479 NATIVE_METHOD(UnixFileSystem, createDirectory0, "(Ljava/io/File;)Z"), 480 NATIVE_METHOD(UnixFileSystem, rename0, "(Ljava/io/File;Ljava/io/File;)Z"), 481 NATIVE_METHOD(UnixFileSystem, setLastModifiedTime0, "(Ljava/io/File;J)Z"), 482 NATIVE_METHOD(UnixFileSystem, setReadOnly0, "(Ljava/io/File;)Z"), 483 NATIVE_METHOD(UnixFileSystem, getSpace0, "(Ljava/io/File;I)J"), 484 }; 485 486 void register_java_io_UnixFileSystem(JNIEnv* env) { 487 jniRegisterNativeMethods(env, "java/io/UnixFileSystem", gMethods, NELEM(gMethods)); 488 } 489