/* * 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 #include #include #include #include #include #include #include #include #include #include /* 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)); }