diff options
Diffstat (limited to 'luni/src/main/native/java_io_File.c')
-rw-r--r-- | luni/src/main/native/java_io_File.c | 704 |
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)); +} |