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 56static 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 64JNIEXPORT void JNICALL 65Java_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 75extern int canonicalize(char *path, const char *out, int len); 76 77JNIEXPORT jstring JNICALL 78Java_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 103static jboolean 104statMode(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 115JNIEXPORT jint JNICALL 116Java_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 136JNIEXPORT jboolean JNICALL 137Java_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 166JNIEXPORT jboolean JNICALL 167Java_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 213JNIEXPORT jlong JNICALL 214Java_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 229JNIEXPORT jlong JNICALL 230Java_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 248JNIEXPORT jboolean JNICALL 249Java_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 274JNIEXPORT jboolean JNICALL 275Java_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 289JNIEXPORT jobjectArray JNICALL 290Java_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 360JNIEXPORT jboolean JNICALL 361Java_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 375JNIEXPORT jboolean JNICALL 376Java_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 391JNIEXPORT jboolean JNICALL 392Java_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 420JNIEXPORT jboolean JNICALL 421Java_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 437JNIEXPORT jlong JNICALL 438Java_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 468static 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 486void register_java_io_UnixFileSystem(JNIEnv* env) { 487 jniRegisterNativeMethods(env, "java/io/UnixFileSystem", gMethods, NELEM(gMethods)); 488} 489