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 }