summaryrefslogtreecommitdiffstats
path: root/luni/src/main/native/java_io_File.c
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src/main/native/java_io_File.c')
-rw-r--r--luni/src/main/native/java_io_File.c704
1 files changed, 704 insertions, 0 deletions
diff --git a/luni/src/main/native/java_io_File.c b/luni/src/main/native/java_io_File.c
new file mode 100644
index 0000000..bf89681
--- /dev/null
+++ b/luni/src/main/native/java_io_File.c
@@ -0,0 +1,704 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define MaxPath 1024
+
+#include "JNIHelp.h"
+
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <utime.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <assert.h>
+
+
+/* these were copied from java.io.File */
+enum {
+ STAT_TYPE_EXISTS = 0x0001,
+ STAT_TYPE_DIR = 0x0002,
+ STAT_TYPE_FILE = 0x0004
+};
+
+static void convertToPlatform(char *path) {
+ char *pathIndex;
+
+ pathIndex = path;
+ while(*pathIndex != '\0') {
+ if(*pathIndex == '\\') {
+ *pathIndex = '/';
+ }
+ pathIndex++;
+ }
+}
+
+/*
+ * private static native byte[][] rootsImpl()
+ *
+ * Returns the linux root in an array of byte arrays
+ */
+static jobject java_io_File_rootsImpl(JNIEnv* env, jclass clazz) {
+ char rootStrings[3];
+ jarray answer;
+
+ rootStrings[0] = '/';
+ rootStrings[1] = '\0';
+ rootStrings[2] = '\0';
+
+ jclass arrayClass = (*env)->FindClass(env, "[B");
+ if (arrayClass == NULL)
+ return NULL;
+
+ answer = (*env)->NewObjectArray(env, 1, arrayClass, NULL);
+ if (!answer)
+ return NULL;
+
+ jbyteArray rootname;
+
+ rootname = (*env)->NewByteArray(env, 3);
+ (*env)->SetByteArrayRegion(env, rootname, 0, 3, (jbyte *) rootStrings);
+ (*env)->SetObjectArrayElement(env, answer, 0, rootname);
+ //(*env)->DeleteLocalRef(env, rootname);
+
+ return answer;
+}
+
+static jbyteArray java_io_File_getCanonImpl(JNIEnv * env, jobject recv,
+ jbyteArray path) {
+ /* This needs work. Currently it does no more or less than VAJ-20 ST
+ * implementationbut really should figure out '..', '.', and really
+ * resolve references.
+ */
+ jbyteArray answer;
+ size_t answerlen;
+ char *pathIndex;
+ char pathCopy[MaxPath];
+ jsize length = (jsize) (*env)->GetArrayLength(env, path);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy);
+ pathCopy[length] = '\0';
+
+ convertToPlatform(pathCopy);
+
+ answerlen = strlen(pathCopy);
+ answer = (*env)->NewByteArray(env, answerlen);
+ (*env)->SetByteArrayRegion(env, answer, 0, answerlen, (jbyte *) pathCopy);
+
+ return answer;
+}
+
+/*
+ * native private boolean deleteFileImpl()
+ *
+ * Returns "true" if the file exists and was successfully deleted.
+ */
+static jboolean java_io_File_deleteFileImpl(JNIEnv* env, jobject obj,
+ jbyteArray path) {
+
+ int cc;
+
+ if(path == NULL) {
+ return JNI_FALSE; /* exception thrown */
+ }
+
+ char pathCopy[MaxPath];
+ jsize length = (jsize) (*env)->GetArrayLength(env, path);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy);
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ cc = unlink(pathCopy);
+ if(cc < 0) {
+ int err = errno;
+
+ /*
+ * According to the man pages, Linux uses EISDIR and Mac OS X
+ * uses EPERM to indicate a non-super-user attempt to unlink
+ * a directory. Mac OS does have EISDIR in the header file.
+ *
+ * We should get EACCES if the problem is directory permissions.
+ */
+ if(err == EISDIR || err == EPERM) {
+ cc = rmdir(pathCopy);
+ if(cc < 0) {
+ /* probably ENOTEMPTY */
+ LOGD("unable to rmdir '%s': %s (errno=%d)\n",
+ pathCopy, strerror(err), err);
+ }
+ } else {
+ LOGD("unable to unlink '%s': %s (errno=%d)\n",
+ pathCopy, strerror(err), err);
+ }
+ }
+
+ return (cc == 0);
+}
+
+/*
+ * harmony implements this method practically identical to the deleteFileImpl,
+ * except that it uses a diffrent helper method from hyport. Dalvik seems to
+ * just need this one method to delete both
+ */
+static jboolean java_io_File_deleteDirImpl(JNIEnv * env, jobject recv,
+ jbyteArray path) {
+ return java_io_File_deleteFileImpl( env, recv, path);
+}
+
+/*
+ * native public long lengthImpl()
+ *
+ * Returns the file length, or 0 if the file does not exist. The result for
+ * a directory is not defined.
+ */
+static jlong java_io_File_lengthImpl(JNIEnv* env, jobject obj,
+ jbyteArray path) {
+ struct stat sb;
+ jlong result = 0;
+ int cc;
+
+ char pathCopy[MaxPath];
+ jsize length = (jsize) (*env)->GetArrayLength(env, path);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy);
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ cc = stat(pathCopy, &sb);
+ if(cc == 0) {
+ // BEGIN android-added
+ /*
+ * This explicitly treats non-regular files (e.g., sockets and
+ * block-special devices) as having size zero. Some synthetic
+ * "regular" files may report an arbitrary non-zero size, but
+ * in these cases they generally report a block count of zero.
+ * So, use a zero block count to trump any other concept of
+ * size.
+ */
+ if (S_ISREG(sb.st_mode) && (sb.st_blocks != 0)) {
+ result = sb.st_size;
+ } else {
+ result = 0;
+ }
+ // END android-added
+ // BEGIN android-deleted
+ //result = sb.st_size;
+ // END android-deleted
+ }
+
+ return result;
+}
+
+/*
+ * native public long lastModified()
+ *
+ * Get the last modified date of the file. Measured in milliseconds
+ * from epoch (00:00:00 GMT, January 1, 1970). Returns 0 if the file does
+ * not exist
+ */
+static jlong java_io_File_lastModifiedImpl(JNIEnv* env, jobject obj,
+ jbyteArray path) {
+ struct stat sb;
+ jlong result = 0;
+ int cc;
+
+ char pathCopy[MaxPath];
+ jsize length = (jsize) (*env)->GetArrayLength(env, path);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy);
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ cc = stat(pathCopy, &sb);
+ if(cc == 0) {
+ // sb.st_mtime is a time_t which is in seconds since epoch.
+ result = sb.st_mtime;
+ result *= 1000L;
+ }
+
+ return result;
+}
+
+/*
+ * private static native int stattype(String path)
+ */
+static jint java_io_File_stattype(JNIEnv* env, jobject recv,
+ jbyteArray pathStr) {
+
+ char pathCopy[MaxPath];
+ jsize length = (jsize) (*env)->GetArrayLength(env, pathStr);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)pathCopy);
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ struct stat sb;
+ int cc, type;
+
+ type = 0;
+ cc = stat(pathCopy, &sb);
+
+ if(cc == 0) {
+ type |= STAT_TYPE_EXISTS;
+ if(S_ISDIR(sb.st_mode)) {
+ type |= STAT_TYPE_DIR;
+ } else if(S_ISREG(sb.st_mode)) {
+ type |= STAT_TYPE_FILE;
+ }
+ }
+
+ return type;
+}
+
+static jboolean java_io_File_isDirectoryImpl(JNIEnv* env, jobject recv,
+ jbyteArray pathStr) {
+ return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_DIR)
+ == STAT_TYPE_DIR);
+}
+
+static jboolean java_io_File_existsImpl(JNIEnv* env, jobject recv,
+ jbyteArray pathStr) {
+ return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_EXISTS)
+ == STAT_TYPE_EXISTS);
+}
+
+static jboolean java_io_File_isFileImpl(JNIEnv* env, jobject recv,
+ jbyteArray pathStr) {
+ return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_FILE)
+ == STAT_TYPE_FILE);
+}
+
+static jboolean java_io_File_isHiddenImpl(JNIEnv * env, jobject recv,
+ jbyteArray path) {
+
+ char pathCopy[MaxPath];
+ jsize index;
+ jsize length = (*env)->GetArrayLength(env, path);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+
+ if(length == 0) {
+ return 0;
+ }
+
+ ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy));
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ if(!java_io_File_existsImpl(env, recv, path)) {
+ return 0;
+ }
+
+ for(index = length; index >= 0; index--) {
+ if(pathCopy[index] == '.'
+ && (index > 0 && pathCopy[index - 1] == '/')) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static jboolean java_io_File_readable(JNIEnv* env, jobject recv,
+ jbyteArray pathStr) {
+ char path[MaxPath];
+ struct stat sb;
+ int cc, type;
+
+ if(pathStr == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return -1;
+ }
+
+ jsize length = (jsize) (*env)->GetArrayLength(env, pathStr);
+
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)path);
+ path[length] = '\0';
+ convertToPlatform(path);
+
+ cc = access(path, R_OK);
+
+ return cc == 0;
+}
+
+static jboolean java_io_File_writable(JNIEnv* env, jobject recv,
+ jbyteArray pathStr) {
+ char path[MaxPath];
+ struct stat sb;
+ int cc, type;
+
+ if(pathStr == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return -1;
+ }
+
+ jsize length = (jsize) (*env)->GetArrayLength(env, pathStr);
+
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)path);
+ path[length] = '\0';
+ convertToPlatform(path);
+
+ cc = access(path, W_OK);
+
+ return cc == 0;
+}
+
+// BEGIN android-deleted
+#if 0
+static jboolean java_io_File_isReadOnlyImpl(JNIEnv* env, jobject recv,
+ jbyteArray path) {
+
+ return (java_io_File_readable(env, recv, path)
+ && !java_io_File_writable(env, recv, path));
+}
+
+static jboolean java_io_File_isWriteOnlyImpl(JNIEnv* env, jobject recv,
+ jbyteArray path) {
+
+ return (!java_io_File_readable(env, recv, path)
+ && java_io_File_writable(env, recv, path));
+}
+#endif
+// END android-deleted
+
+static jbyteArray java_io_File_getLinkImpl(JNIEnv* env, jobject recv,
+ jbyteArray path) {
+ jbyteArray answer;
+ jsize answerlen;
+ char pathCopy[MaxPath];
+
+ jsize length = (jsize) (*env)->GetArrayLength(env, path);
+
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy);
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ jboolean test = -1;
+
+ char *link = pathCopy;
+
+ int size = readlink(link, link, MaxPath);
+ if(size <= 0) {
+ test = 0;
+ } else {
+ if(size >= MaxPath) {
+ link[MaxPath - 1] = 0;
+ } else {
+ link[size] = 0;
+ }
+ }
+
+ if(test) {
+ answerlen = strlen(pathCopy);
+ answer = (*env)->NewByteArray(env, answerlen);
+ (*env)->SetByteArrayRegion(env, answer, 0, answerlen,
+ (jbyte *) pathCopy);
+ } else {
+ answer = path;
+ }
+
+ return answer;
+}
+
+static jboolean java_io_File_setLastModifiedImpl(JNIEnv* env, jobject recv,
+ jbyteArray path, jlong time) {
+ jboolean result;
+
+ jsize length = (*env)->GetArrayLength(env, path);
+ char pathCopy[MaxPath];
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy));
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ struct stat statbuf;
+ struct utimbuf timebuf;
+ if(stat(pathCopy, &statbuf)) {
+ result = 0;
+ } else {
+ timebuf.actime = statbuf.st_atime;
+ timebuf.modtime = (time_t) (time / 1000);
+ result = utime(pathCopy, &timebuf) == 0;
+ }
+
+ return result;
+}
+
+static jboolean java_io_File_setReadOnlyImpl(JNIEnv* env, jobject recv,
+ jbyteArray path) {
+ jsize length = (*env)->GetArrayLength(env, path);
+ char pathCopy[MaxPath];
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+
+ ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy));
+
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ struct stat buffer;
+ mode_t mode;
+ if(stat(pathCopy, &buffer)) {
+ return 0;
+ }
+ mode = buffer.st_mode;
+ mode = mode & 07555;
+
+ return chmod(pathCopy, mode) == 0;
+}
+
+static jobject java_io_File_listImpl(JNIEnv* env, jclass clazz,
+ jbyteArray path) {
+
+ struct dirEntry {
+ char pathEntry[MaxPath];
+ struct dirEntry *next;
+ } *dirList, *currentEntry;
+
+ jsize length = (*env)->GetArrayLength(env, path);
+ char pathCopy[MaxPath];
+ char filename[MaxPath];
+ jint result = 0, index;
+ jint numEntries = 0;
+ jarray answer = NULL;
+ jclass javaClass = NULL;
+
+ dirList = NULL;
+ currentEntry = NULL;
+
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy));
+ if(length >= 1 && pathCopy[length - 1] != '\\'
+ && pathCopy[length - 1] != '/') {
+ pathCopy[length] = '/';
+ length++;
+ }
+ pathCopy[length] = '\0';
+
+ convertToPlatform(pathCopy);
+
+ DIR *dirp = NULL;
+ struct dirent *entry;
+
+ dirp = opendir(pathCopy);
+
+ if(dirp == NULL) {
+ return NULL;
+ }
+
+ entry = readdir(dirp);
+
+ if(entry == NULL) {
+ closedir(dirp);
+ return NULL;
+ }
+ strcpy(filename, entry->d_name);
+
+ while(result > -1) {
+ if(strcmp(".", filename) != 0 && (strcmp("..", filename) != 0)) {
+ if(numEntries > 0) {
+ currentEntry->next =
+ (struct dirEntry *) malloc(sizeof(struct dirEntry));
+ currentEntry = currentEntry->next;
+ } else {
+ dirList = (struct dirEntry *) malloc(sizeof(struct dirEntry));
+ currentEntry = dirList;
+ }
+ if(currentEntry == NULL) {
+ closedir(dirp);
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ goto cleanup;
+ }
+ strcpy(currentEntry->pathEntry, filename);
+ numEntries++;
+ }
+
+ entry = readdir(dirp);
+
+ if(entry == NULL) {
+ result = -1;
+ } else {
+ strcpy(filename, entry->d_name);
+ }
+ }
+ closedir(dirp);
+
+ if(numEntries == 0) {
+ return NULL;
+ }
+
+ javaClass = (*env)->FindClass(env, "[B");
+ if(javaClass == NULL) {
+ return NULL;
+ }
+ answer = (*env)->NewObjectArray(env, numEntries, javaClass, NULL);
+
+cleanup:
+ for(index = 0; index < numEntries; index++)
+ {
+ jbyteArray entrypath;
+ jsize entrylen = strlen(dirList->pathEntry);
+ currentEntry = dirList;
+ if(answer)
+ {
+ entrypath = (*env)->NewByteArray(env, entrylen);
+ (*env)->SetByteArrayRegion(env, entrypath, 0, entrylen,
+ (jbyte *) dirList->pathEntry);
+ (*env)->SetObjectArrayElement(env, answer, index, entrypath);
+ (*env)->DeleteLocalRef(env, entrypath);
+ }
+ dirList = dirList->next;
+ free((void *)currentEntry);
+ }
+
+ return answer;
+}
+
+static jboolean java_io_File_mkdirImpl(JNIEnv* env, jobject recv,
+ jbyteArray path) {
+ jint result;
+ char pathCopy[MaxPath];
+ jsize length = (*env)->GetArrayLength(env, path);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy));
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+// BEGIN android-changed
+// don't want default permissions to allow global access.
+ result = mkdir(pathCopy, S_IRWXU);
+// END android-changed
+
+ if(-1 != result)
+ {
+ result = 0;
+ }
+
+ return result == 0;
+}
+
+static jint java_io_File_newFileImpl(JNIEnv* env, jobject recv,
+ jbyteArray path) {
+
+ if(path == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return -1;
+ }
+
+ jint result;
+ jsize length = (*env)->GetArrayLength(env, path);
+ char pathCopy[MaxPath];
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy);
+ pathCopy[length] = '\0';
+ convertToPlatform(pathCopy);
+
+ /* First check to see if file already exists */
+ if(java_io_File_existsImpl(env, recv, path))
+ {
+ return 1;
+ }
+
+ /* Now create the file and close it */
+// BEGIN android-changed
+// don't want default permissions to allow global access.
+ int fd = open(pathCopy, O_EXCL | O_CREAT, 0600);
+// END android-changed
+ if(fd == -1)
+ {
+ if(errno == EEXIST) {
+ return 1;
+ }
+ return -1;
+ }
+ close(fd);
+
+ return 0;
+}
+
+static jboolean java_io_File_renameToImpl(JNIEnv* env, jobject recv,
+ jbyteArray pathExist, jbyteArray pathNew) {
+ jint result;
+ jsize length;
+ char pathExistCopy[MaxPath], pathNewCopy[MaxPath];
+
+ length = (*env)->GetArrayLength(env, pathExist);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ ((*env)->GetByteArrayRegion(env, pathExist, 0, length,
+ (jbyte *)pathExistCopy));
+ pathExistCopy[length] = '\0';
+
+ length = (*env)->GetArrayLength(env, pathNew);
+ length = length < MaxPath - 1 ? length : MaxPath - 1;
+ ((*env)->GetByteArrayRegion(env, pathNew, 0, length,
+ (jbyte *)pathNewCopy));
+ pathNewCopy[length] = '\0';
+
+ convertToPlatform(pathExistCopy);
+ convertToPlatform(pathNewCopy);
+
+ result = rename(pathExistCopy, pathNewCopy);
+
+ return result == 0;
+}
+
+static void java_io_File_oneTimeInitialization(JNIEnv * env, jclass clazz)
+{
+ // dummy to stay compatible to harmony
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "rootsImpl", "()[[B", (void*) java_io_File_rootsImpl },
+ { "deleteDirImpl", "([B)Z", (void*) java_io_File_deleteDirImpl },
+ { "deleteFileImpl", "([B)Z", (void*) java_io_File_deleteFileImpl },
+ { "existsImpl", "([B)Z", (void*) java_io_File_existsImpl },
+ { "getCanonImpl", "([B)[B", (void*) java_io_File_getCanonImpl },
+ { "isDirectoryImpl", "([B)Z", (void*) java_io_File_isDirectoryImpl },
+ { "isFileImpl", "([B)Z", (void*) java_io_File_isFileImpl },
+ { "isHiddenImpl", "([B)Z", (void*) java_io_File_isHiddenImpl },
+ // BEGIN android-changed
+ { "isReadableImpl", "([B)Z", (void*) java_io_File_readable },
+ { "isWriteableImpl", "([B)Z", (void*) java_io_File_writable },
+ // END android-changed
+ { "getLinkImpl", "([B)[B", (void*) java_io_File_getLinkImpl },
+ { "lastModifiedImpl", "([B)J", (void*) java_io_File_lastModifiedImpl },
+ { "setReadOnlyImpl", "([B)Z", (void*) java_io_File_setReadOnlyImpl },
+ { "lengthImpl", "([B)J", (void*) java_io_File_lengthImpl },
+ { "listImpl", "([B)[[B",(void*) java_io_File_listImpl },
+ { "mkdirImpl", "([B)Z", (void*) java_io_File_mkdirImpl },
+ { "newFileImpl", "([B)I", (void*) java_io_File_newFileImpl },
+ { "renameToImpl", "([B[B)Z",(void*) java_io_File_renameToImpl },
+ { "setLastModifiedImpl","([BJ)Z",
+ (void*) java_io_File_setLastModifiedImpl },
+ { "oneTimeInitialization","()V",
+ (void*) java_io_File_oneTimeInitialization }
+};
+int register_java_io_File(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, "java/io/File",
+ gMethods, NELEM(gMethods));
+}