1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#define LOG_TAG "File" 19 20#include "JNIHelp.h" 21#include "JniConstants.h" 22#include "JniException.h" 23#include "ScopedPrimitiveArray.h" 24#include "ScopedUtfChars.h" 25#include "toStringArray.h" 26 27#include <string> 28#include <vector> 29 30#include <dirent.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <stdlib.h> 34#include <string.h> 35#include <sys/stat.h> 36#include <sys/types.h> 37#include <time.h> 38#include <unistd.h> 39#include <utime.h> 40 41static jstring File_canonicalizePath(JNIEnv* env, jclass, jstring javaPath) { 42 ScopedUtfChars path(env, javaPath); 43 if (path.c_str() == NULL) { 44 return NULL; 45 } 46 47 extern bool canonicalize_path(const char* path, std::string& resolved); 48 std::string result; 49 if (!canonicalize_path(path.c_str(), result)) { 50 jniThrowIOException(env, errno); 51 return NULL; 52 } 53 return env->NewStringUTF(result.c_str()); 54} 55 56static jboolean File_setLastModifiedImpl(JNIEnv* env, jclass, jstring javaPath, jlong ms) { 57 ScopedUtfChars path(env, javaPath); 58 if (path.c_str() == NULL) { 59 return JNI_FALSE; 60 } 61 62 // We want to preserve the access time. 63 struct stat sb; 64 if (stat(path.c_str(), &sb) == -1) { 65 return JNI_FALSE; 66 } 67 68 // TODO: we could get microsecond resolution with utimes(3), "legacy" though it is. 69 utimbuf times; 70 times.actime = sb.st_atime; 71 times.modtime = static_cast<time_t>(ms / 1000); 72 return (utime(path.c_str(), ×) == 0); 73} 74 75// Iterates over the filenames in the given directory. 76class ScopedReaddir { 77 public: 78 ScopedReaddir(const char* path) { 79 mDirStream = opendir(path); 80 mIsBad = (mDirStream == NULL); 81 } 82 83 ~ScopedReaddir() { 84 if (mDirStream != NULL) { 85 closedir(mDirStream); 86 } 87 } 88 89 // Returns the next filename, or NULL. 90 const char* next() { 91 if (mIsBad) { 92 return NULL; 93 } 94 errno = 0; 95 dirent* result = readdir(mDirStream); 96 if (result != NULL) { 97 return result->d_name; 98 } 99 if (errno != 0) { 100 mIsBad = true; 101 } 102 return NULL; 103 } 104 105 // Has an error occurred on this stream? 106 bool isBad() const { 107 return mIsBad; 108 } 109 110 private: 111 DIR* mDirStream; 112 bool mIsBad; 113 114 // Disallow copy and assignment. 115 ScopedReaddir(const ScopedReaddir&); 116 void operator=(const ScopedReaddir&); 117}; 118 119typedef std::vector<std::string> DirEntries; 120 121// Reads the directory referred to by 'pathBytes', adding each directory entry 122// to 'entries'. 123static bool readDirectory(JNIEnv* env, jstring javaPath, DirEntries& entries) { 124 ScopedUtfChars path(env, javaPath); 125 if (path.c_str() == NULL) { 126 return false; 127 } 128 129 ScopedReaddir dir(path.c_str()); 130 const char* filename; 131 while ((filename = dir.next()) != NULL) { 132 if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0) { 133 // TODO: this hides allocation failures from us. Push directory iteration up into Java? 134 entries.push_back(filename); 135 } 136 } 137 return !dir.isBad(); 138} 139 140static jobjectArray File_listImpl(JNIEnv* env, jclass, jstring javaPath) { 141 // Read the directory entries into an intermediate form. 142 DirEntries entries; 143 if (!readDirectory(env, javaPath, entries)) { 144 return NULL; 145 } 146 // Translate the intermediate form into a Java String[]. 147 return toStringArray(env, entries); 148} 149 150static JNINativeMethod gMethods[] = { 151 NATIVE_METHOD(File, canonicalizePath, "(Ljava/lang/String;)Ljava/lang/String;"), 152 NATIVE_METHOD(File, listImpl, "(Ljava/lang/String;)[Ljava/lang/String;"), 153 NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"), 154}; 155void register_java_io_File(JNIEnv* env) { 156 jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods)); 157} 158