blob: bf896814895d372b4e562d59b9f49dc0ed695ad4 [file] [log] [blame]
/*
* 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));
}