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 "readlink.h" 26 #include "toStringArray.h" 27 28 #include <string> 29 #include <vector> 30 31 #include <dirent.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 #include <time.h> 39 #include <unistd.h> 40 #include <utime.h> 41 42 static jstring File_readlink(JNIEnv* env, jclass, jstring javaPath) { 43 ScopedUtfChars path(env, javaPath); 44 if (path.c_str() == NULL) { 45 return NULL; 46 } 47 48 std::string result; 49 if (!readlink(path.c_str(), result)) { 50 jniThrowIOException(env, errno); 51 return NULL; 52 } 53 return env->NewStringUTF(result.c_str()); 54 } 55 56 static jstring File_realpath(JNIEnv* env, jclass, jstring javaPath) { 57 ScopedUtfChars path(env, javaPath); 58 if (path.c_str() == NULL) { 59 return NULL; 60 } 61 62 extern bool realpath(const char* path, std::string& resolved); 63 std::string result; 64 if (!realpath(path.c_str(), result)) { 65 jniThrowIOException(env, errno); 66 return NULL; 67 } 68 return env->NewStringUTF(result.c_str()); 69 } 70 71 static jboolean File_setLastModifiedImpl(JNIEnv* env, jclass, jstring javaPath, jlong ms) { 72 ScopedUtfChars path(env, javaPath); 73 if (path.c_str() == NULL) { 74 return JNI_FALSE; 75 } 76 77 // We want to preserve the access time. 78 struct stat sb; 79 if (stat(path.c_str(), &sb) == -1) { 80 return JNI_FALSE; 81 } 82 83 // TODO: we could get microsecond resolution with utimes(3), "legacy" though it is. 84 utimbuf times; 85 times.actime = sb.st_atime; 86 times.modtime = static_cast<time_t>(ms / 1000); 87 return (utime(path.c_str(), ×) == 0); 88 } 89 90 // Iterates over the filenames in the given directory. 91 class ScopedReaddir { 92 public: 93 ScopedReaddir(const char* path) { 94 mDirStream = opendir(path); 95 mIsBad = (mDirStream == NULL); 96 } 97 98 ~ScopedReaddir() { 99 if (mDirStream != NULL) { 100 closedir(mDirStream); 101 } 102 } 103 104 // Returns the next filename, or NULL. 105 const char* next() { 106 if (mIsBad) { 107 return NULL; 108 } 109 errno = 0; 110 dirent* result = readdir(mDirStream); 111 if (result != NULL) { 112 return result->d_name; 113 } 114 if (errno != 0) { 115 mIsBad = true; 116 } 117 return NULL; 118 } 119 120 // Has an error occurred on this stream? 121 bool isBad() const { 122 return mIsBad; 123 } 124 125 private: 126 DIR* mDirStream; 127 bool mIsBad; 128 129 // Disallow copy and assignment. 130 ScopedReaddir(const ScopedReaddir&); 131 void operator=(const ScopedReaddir&); 132 }; 133 134 typedef std::vector<std::string> DirEntries; 135 136 // Reads the directory referred to by 'pathBytes', adding each directory entry 137 // to 'entries'. 138 static bool readDirectory(JNIEnv* env, jstring javaPath, DirEntries& entries) { 139 ScopedUtfChars path(env, javaPath); 140 if (path.c_str() == NULL) { 141 return false; 142 } 143 144 ScopedReaddir dir(path.c_str()); 145 const char* filename; 146 while ((filename = dir.next()) != NULL) { 147 if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0) { 148 // TODO: this hides allocation failures from us. Push directory iteration up into Java? 149 entries.push_back(filename); 150 } 151 } 152 return !dir.isBad(); 153 } 154 155 static jobjectArray File_listImpl(JNIEnv* env, jclass, jstring javaPath) { 156 // Read the directory entries into an intermediate form. 157 DirEntries entries; 158 if (!readDirectory(env, javaPath, entries)) { 159 return NULL; 160 } 161 // Translate the intermediate form into a Java String[]. 162 return toStringArray(env, entries); 163 } 164 165 static JNINativeMethod gMethods[] = { 166 NATIVE_METHOD(File, listImpl, "(Ljava/lang/String;)[Ljava/lang/String;"), 167 NATIVE_METHOD(File, readlink, "(Ljava/lang/String;)Ljava/lang/String;"), 168 NATIVE_METHOD(File, realpath, "(Ljava/lang/String;)Ljava/lang/String;"), 169 NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"), 170 }; 171 void register_java_io_File(JNIEnv* env) { 172 jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods)); 173 } 174