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