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