1 /* 2 * Copyright (c) 1998, 2013, 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((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 FD fd; 245 /* The root directory always exists */ 246 if (strcmp (path, "/")) { 247 fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666); 248 if (fd < 0) { 249 if (errno != EEXIST) 250 JNU_ThrowIOExceptionWithLastError(env, path); 251 } else { 252 if (close(fd) == -1) 253 JNU_ThrowIOExceptionWithLastError(env, path); 254 rv = JNI_TRUE; 255 } 256 } 257 } END_PLATFORM_STRING(env, path); 258 return rv; 259 } 260 261 262 JNIEXPORT jboolean JNICALL 263 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this, 264 jobject file) 265 { 266 jboolean rv = JNI_FALSE; 267 268 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 269 if (remove(path) == 0) { 270 rv = JNI_TRUE; 271 } 272 } END_PLATFORM_STRING(env, path); 273 return rv; 274 } 275 276 277 JNIEXPORT jobjectArray JNICALL 278 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this, 279 jobject file) 280 { 281 DIR *dir = NULL; 282 struct dirent64 *ptr; 283 struct dirent64 *result; 284 int len, maxlen; 285 jobjectArray rv, old; 286 287 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 288 dir = opendir(path); 289 } END_PLATFORM_STRING(env, path); 290 if (dir == NULL) return NULL; 291 292 ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1)); 293 if (ptr == NULL) { 294 JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); 295 closedir(dir); 296 return NULL; 297 } 298 299 /* Allocate an initial String array */ 300 len = 0; 301 maxlen = 16; 302 rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL); 303 if (rv == NULL) goto error; 304 305 /* Scan the directory */ 306 while ((readdir64_r(dir, ptr, &result) == 0) && (result != NULL)) { 307 jstring name; 308 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, "..")) 309 continue; 310 if (len == maxlen) { 311 old = rv; 312 rv = (*env)->NewObjectArray(env, maxlen <<= 1, 313 JNU_ClassString(env), NULL); 314 if (rv == NULL) goto error; 315 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; 316 (*env)->DeleteLocalRef(env, old); 317 } 318 #ifdef MACOSX 319 name = newStringPlatform(env, ptr->d_name); 320 #else 321 name = JNU_NewStringPlatform(env, ptr->d_name); 322 #endif 323 if (name == NULL) goto error; 324 (*env)->SetObjectArrayElement(env, rv, len++, name); 325 (*env)->DeleteLocalRef(env, name); 326 } 327 closedir(dir); 328 free(ptr); 329 330 /* Copy the final results into an appropriately-sized array */ 331 old = rv; 332 rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL); 333 if (rv == NULL) { 334 return NULL; 335 } 336 if (JNU_CopyObjectArray(env, rv, old, len) < 0) { 337 return NULL; 338 } 339 return rv; 340 341 error: 342 closedir(dir); 343 free(ptr); 344 return NULL; 345 } 346 347 348 JNIEXPORT jboolean JNICALL 349 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this, 350 jobject file) 351 { 352 jboolean rv = JNI_FALSE; 353 354 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 355 if (mkdir(path, 0777) == 0) { 356 rv = JNI_TRUE; 357 } 358 } END_PLATFORM_STRING(env, path); 359 return rv; 360 } 361 362 363 JNIEXPORT jboolean JNICALL 364 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, 365 jobject from, jobject to) 366 { 367 jboolean rv = JNI_FALSE; 368 369 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { 370 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { 371 if (rename(fromPath, toPath) == 0) { 372 rv = JNI_TRUE; 373 } 374 } END_PLATFORM_STRING(env, toPath); 375 } END_PLATFORM_STRING(env, fromPath); 376 return rv; 377 } 378 379 JNIEXPORT jboolean JNICALL 380 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, 381 jobject file, jlong time) 382 { 383 jboolean rv = JNI_FALSE; 384 385 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 386 struct stat64 sb; 387 388 if (stat64(path, &sb) == 0) { 389 struct timeval tv[2]; 390 391 /* Preserve access time */ 392 tv[0].tv_sec = sb.st_atime; 393 tv[0].tv_usec = 0; 394 395 /* Change last-modified time */ 396 tv[1].tv_sec = time / 1000; 397 tv[1].tv_usec = (time % 1000) * 1000; 398 399 if (utimes(path, tv) == 0) 400 rv = JNI_TRUE; 401 } 402 } END_PLATFORM_STRING(env, path); 403 404 return rv; 405 } 406 407 408 JNIEXPORT jboolean JNICALL 409 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this, 410 jobject file) 411 { 412 jboolean rv = JNI_FALSE; 413 414 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 415 int mode; 416 if (statMode(path, &mode)) { 417 if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) { 418 rv = JNI_TRUE; 419 } 420 } 421 } END_PLATFORM_STRING(env, path); 422 return rv; 423 } 424 425 JNIEXPORT jlong JNICALL 426 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this, 427 jobject file, jint t) 428 { 429 jlong rv = 0L; 430 431 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { 432 struct statvfs64 fsstat; 433 memset(&fsstat, 0, sizeof(fsstat)); 434 if (statvfs64(path, &fsstat) == 0) { 435 switch(t) { 436 case java_io_FileSystem_SPACE_TOTAL: 437 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 438 long_to_jlong(fsstat.f_blocks)); 439 break; 440 case java_io_FileSystem_SPACE_FREE: 441 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 442 long_to_jlong(fsstat.f_bfree)); 443 break; 444 case java_io_FileSystem_SPACE_USABLE: 445 rv = jlong_mul(long_to_jlong(fsstat.f_frsize), 446 long_to_jlong(fsstat.f_bavail)); 447 break; 448 default: 449 assert(0); 450 } 451 } 452 } END_PLATFORM_STRING(env, path); 453 return rv; 454 }