| /* |
| * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| #include <assert.h> |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/stat.h> |
| #include <sys/statvfs.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <dlfcn.h> |
| #include <limits.h> |
| |
| #include "jni.h" |
| #include "jni_util.h" |
| #include "jlong.h" |
| #include "jvm.h" |
| #include "io_util.h" |
| #include "java_io_FileSystem.h" |
| #include "java_io_UnixFileSystem.h" |
| |
| |
| /* -- Field IDs -- */ |
| |
| static struct { |
| jfieldID path; |
| } ids; |
| |
| |
| JNIEXPORT void JNICALL |
| Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls) |
| { |
| jclass fileClass = (*env)->FindClass(env, "java/io/File"); |
| if (!fileClass) return; |
| ids.path = (*env)->GetFieldID(env, fileClass, |
| "path", "Ljava/lang/String;"); |
| } |
| |
| |
| /* -- Large-file support -- */ |
| |
| /* LINUX_FIXME: ifdef __solaris__ here is wrong. We need to move the |
| * definition of stat64 into a solaris_largefile.h and create a |
| * linux_largefile.h with a good stat64 structure to compile on |
| * glibc2.0 based systems. |
| */ |
| #if defined(__solaris__) && !defined(_LFS_LARGEFILE) || !_LFS_LARGEFILE |
| |
| /* The stat64 structure must be provided for systems without large-file support |
| (e.g., Solaris 2.5.1). These definitions are copied from the Solaris 2.6 |
| <sys/stat.h> and <sys/types.h> files. |
| */ |
| |
| typedef longlong_t off64_t; /* offsets within files */ |
| typedef u_longlong_t ino64_t; /* expanded inode type */ |
| typedef longlong_t blkcnt64_t; /* count of file blocks */ |
| |
| struct stat64 { |
| dev_t st_dev; |
| long st_pad1[3]; |
| ino64_t st_ino; |
| mode_t st_mode; |
| nlink_t st_nlink; |
| uid_t st_uid; |
| gid_t st_gid; |
| dev_t st_rdev; |
| long st_pad2[2]; |
| off64_t st_size; |
| timestruc_t st_atim; |
| timestruc_t st_mtim; |
| timestruc_t st_ctim; |
| long st_blksize; |
| blkcnt64_t st_blocks; |
| char st_fstype[_ST_FSTYPSZ]; |
| long st_pad4[8]; |
| }; |
| |
| #endif /* !_LFS_LARGEFILE */ |
| |
| typedef int (*STAT64)(const char *, struct stat64 *); |
| |
| #if defined(__linux__) && defined(_LARGEFILE64_SOURCE) |
| static STAT64 stat64_ptr = &stat64; |
| #else |
| static STAT64 stat64_ptr = NULL; |
| #endif |
| |
| #ifndef __linux__ |
| #ifdef __GNUC__ |
| static void init64IO(void) __attribute__((constructor)); |
| #else |
| #pragma init(init64IO) |
| #endif |
| #endif |
| |
| static void init64IO(void) { |
| void *handle = dlopen(0, RTLD_LAZY); |
| stat64_ptr = (STAT64) dlsym(handle, "_stat64"); |
| dlclose(handle); |
| } |
| |
| |
| /* -- Path operations -- */ |
| |
| extern int canonicalize(char *path, const char *out, int len); |
| |
| JNIEXPORT jstring JNICALL |
| Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this, |
| jstring pathname) |
| { |
| jstring rv = NULL; |
| |
| WITH_PLATFORM_STRING(env, pathname, path) { |
| char canonicalPath[JVM_MAXPATHLEN]; |
| if (canonicalize(JVM_NativePath((char *)path), |
| canonicalPath, JVM_MAXPATHLEN) < 0) { |
| JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); |
| } else { |
| rv = JNU_NewStringPlatform(env, canonicalPath); |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| |
| /* -- Attribute accessors -- */ |
| |
| |
| static jboolean |
| statMode(const char *path, int *mode) |
| { |
| if (stat64_ptr) { |
| struct stat64 sb; |
| if (((*stat64_ptr)(path, &sb)) == 0) { |
| *mode = sb.st_mode; |
| return JNI_TRUE; |
| } |
| } else { |
| struct stat sb; |
| if (stat(path, &sb) == 0) { |
| *mode = sb.st_mode; |
| return JNI_TRUE; |
| } |
| } |
| return JNI_FALSE; |
| } |
| |
| |
| JNIEXPORT jint JNICALL |
| Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this, |
| jobject file) |
| { |
| jint rv = 0; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| int mode; |
| if (statMode(path, &mode)) { |
| int fmt = mode & S_IFMT; |
| rv = (jint) (java_io_FileSystem_BA_EXISTS |
| | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0) |
| | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0)); |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this, |
| jobject file, jint a) |
| { |
| jboolean rv = JNI_FALSE; |
| int mode; |
| switch (a) { |
| case java_io_FileSystem_ACCESS_READ: |
| mode = R_OK; |
| break; |
| case java_io_FileSystem_ACCESS_WRITE: |
| mode = W_OK; |
| break; |
| case java_io_FileSystem_ACCESS_EXECUTE: |
| mode = X_OK; |
| break; |
| default: assert(0); |
| } |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| if (access(path, mode) == 0) { |
| rv = JNI_TRUE; |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this, |
| jobject file, |
| jint access, |
| jboolean enable, |
| jboolean owneronly) |
| { |
| jboolean rv = JNI_FALSE; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| int amode, mode; |
| switch (access) { |
| case java_io_FileSystem_ACCESS_READ: |
| if (owneronly) |
| amode = S_IRUSR; |
| else |
| amode = S_IRUSR | S_IRGRP | S_IROTH; |
| break; |
| case java_io_FileSystem_ACCESS_WRITE: |
| if (owneronly) |
| amode = S_IWUSR; |
| else |
| amode = S_IWUSR | S_IWGRP | S_IWOTH; |
| break; |
| case java_io_FileSystem_ACCESS_EXECUTE: |
| if (owneronly) |
| amode = S_IXUSR; |
| else |
| amode = S_IXUSR | S_IXGRP | S_IXOTH; |
| break; |
| default: |
| assert(0); |
| } |
| if (statMode(path, &mode)) { |
| if (enable) |
| mode |= amode; |
| else |
| mode &= ~amode; |
| if (chmod(path, mode) >= 0) { |
| rv = JNI_TRUE; |
| } |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| JNIEXPORT jlong JNICALL |
| Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this, |
| jobject file) |
| { |
| jlong rv = 0; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| if (stat64_ptr) { |
| struct stat64 sb; |
| if (((*stat64_ptr)(path, &sb)) == 0) { |
| rv = 1000 * (jlong)sb.st_mtime; |
| } |
| } else { |
| struct stat sb; |
| if (stat(path, &sb) == 0) { |
| rv = 1000 * (jlong)sb.st_mtime; |
| } |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| |
| JNIEXPORT jlong JNICALL |
| Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this, |
| jobject file) |
| { |
| jlong rv = 0; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| if (stat64_ptr) { |
| struct stat64 sb; |
| if (((*stat64_ptr)(path, &sb)) == 0) { |
| rv = sb.st_size; |
| } |
| } else { |
| struct stat sb; |
| if (stat(path, &sb) == 0) { |
| rv = sb.st_size; |
| } |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| |
| /* -- File operations -- */ |
| |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls, |
| jstring pathname) |
| { |
| jboolean rv = JNI_FALSE; |
| |
| WITH_PLATFORM_STRING(env, pathname, path) { |
| int fd; |
| if (!strcmp (path, "/")) { |
| fd = JVM_EEXIST; /* The root directory always exists */ |
| } else { |
| fd = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666); |
| } |
| if (fd < 0) { |
| if (fd != JVM_EEXIST) { |
| JNU_ThrowIOExceptionWithLastError(env, path); |
| } |
| } else { |
| JVM_Close(fd); |
| rv = JNI_TRUE; |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this, |
| jobject file) |
| { |
| jboolean rv = JNI_FALSE; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| if (remove(path) == 0) { |
| rv = JNI_TRUE; |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| |
| JNIEXPORT jobjectArray JNICALL |
| Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this, |
| jobject file) |
| { |
| DIR *dir = NULL; |
| struct dirent64 *ptr; |
| struct dirent64 *result; |
| int len, maxlen; |
| jobjectArray rv, old; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| dir = opendir(path); |
| } END_PLATFORM_STRING(env, path); |
| if (dir == NULL) return NULL; |
| |
| ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1)); |
| if (ptr == NULL) { |
| JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); |
| closedir(dir); |
| return NULL; |
| } |
| |
| /* Allocate an initial String array */ |
| len = 0; |
| maxlen = 16; |
| rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL); |
| if (rv == NULL) goto error; |
| |
| /* Scan the directory */ |
| while ((readdir64_r(dir, ptr, &result) == 0) && (result != NULL)) { |
| jstring name; |
| if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, "..")) |
| continue; |
| if (len == maxlen) { |
| old = rv; |
| rv = (*env)->NewObjectArray(env, maxlen <<= 1, |
| JNU_ClassString(env), NULL); |
| if (rv == NULL) goto error; |
| if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; |
| (*env)->DeleteLocalRef(env, old); |
| } |
| name = JNU_NewStringPlatform(env, ptr->d_name); |
| if (name == NULL) goto error; |
| (*env)->SetObjectArrayElement(env, rv, len++, name); |
| (*env)->DeleteLocalRef(env, name); |
| } |
| closedir(dir); |
| free(ptr); |
| |
| /* Copy the final results into an appropriately-sized array */ |
| old = rv; |
| rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL); |
| if (rv == NULL) { |
| return NULL; |
| } |
| if (JNU_CopyObjectArray(env, rv, old, len) < 0) { |
| return NULL; |
| } |
| return rv; |
| |
| error: |
| closedir(dir); |
| free(ptr); |
| return NULL; |
| } |
| |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this, |
| jobject file) |
| { |
| jboolean rv = JNI_FALSE; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| if (mkdir(path, 0777) == 0) { |
| rv = JNI_TRUE; |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this, |
| jobject from, jobject to) |
| { |
| jboolean rv = JNI_FALSE; |
| |
| WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) { |
| WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) { |
| if (rename(fromPath, toPath) == 0) { |
| rv = JNI_TRUE; |
| } |
| } END_PLATFORM_STRING(env, toPath); |
| } END_PLATFORM_STRING(env, fromPath); |
| return rv; |
| } |
| |
| |
| /* Bug in solaris /usr/include/sys/time.h? */ |
| #ifdef __solaris__ |
| extern int utimes(const char *, const struct timeval *); |
| #elif defined(__linux___) |
| extern int utimes(const char *, struct timeval *); |
| #endif |
| |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, |
| jobject file, jlong time) |
| { |
| jboolean rv = JNI_FALSE; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| struct timeval tv[2]; |
| #ifdef __solaris__ |
| timestruc_t ts; |
| |
| if (stat64_ptr) { |
| struct stat64 sb; |
| if (((*stat64_ptr)(path, &sb)) == 0) |
| ts = sb.st_atim; |
| else |
| goto error; |
| } else { |
| struct stat sb; |
| if (stat(path, &sb) == 0) |
| ts = sb.st_atim; |
| else |
| goto error; |
| } |
| #endif |
| |
| /* Preserve access time */ |
| #ifdef __linux__ |
| struct stat sb; |
| |
| if (stat(path, &sb) == 0) { |
| |
| tv[0].tv_sec = sb.st_atime; |
| tv[0].tv_usec = 0; |
| } |
| #else |
| tv[0].tv_sec = ts.tv_sec; |
| tv[0].tv_usec = ts.tv_nsec / 1000; |
| #endif |
| |
| /* Change last-modified time */ |
| tv[1].tv_sec = time / 1000; |
| tv[1].tv_usec = (time % 1000) * 1000; |
| |
| if (utimes(path, tv) >= 0) |
| rv = JNI_TRUE; |
| |
| error: ; |
| } END_PLATFORM_STRING(env, path); |
| |
| return rv; |
| } |
| |
| |
| JNIEXPORT jboolean JNICALL |
| Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this, |
| jobject file) |
| { |
| jboolean rv = JNI_FALSE; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| int mode; |
| if (statMode(path, &mode)) { |
| if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) { |
| rv = JNI_TRUE; |
| } |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |
| |
| JNIEXPORT jlong JNICALL |
| Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this, |
| jobject file, jint t) |
| { |
| jlong rv = 0L; |
| |
| WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) { |
| struct statvfs fsstat; |
| memset(&fsstat, 0, sizeof(struct statvfs)); |
| if (statvfs(path, &fsstat) == 0) { |
| switch(t) { |
| case java_io_FileSystem_SPACE_TOTAL: |
| rv = jlong_mul(long_to_jlong(fsstat.f_frsize), |
| long_to_jlong(fsstat.f_blocks)); |
| break; |
| case java_io_FileSystem_SPACE_FREE: |
| rv = jlong_mul(long_to_jlong(fsstat.f_frsize), |
| long_to_jlong(fsstat.f_bfree)); |
| break; |
| case java_io_FileSystem_SPACE_USABLE: |
| rv = jlong_mul(long_to_jlong(fsstat.f_frsize), |
| long_to_jlong(fsstat.f_bavail)); |
| break; |
| default: |
| assert(0); |
| } |
| } |
| } END_PLATFORM_STRING(env, path); |
| return rv; |
| } |