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 <sys/vfs.h> 39 #include <time.h> 40 #include <unistd.h> 41 #include <utime.h> 42 43 static jstring File_readlink(JNIEnv* env, jclass, jstring javaPath) { 44 ScopedUtfChars path(env, javaPath); 45 if (path.c_str() == NULL) { 46 return NULL; 47 } 48 49 std::string result; 50 if (!readlink(path.c_str(), result)) { 51 jniThrowIOException(env, errno); 52 return NULL; 53 } 54 return env->NewStringUTF(result.c_str()); 55 } 56 57 static jstring File_realpath(JNIEnv* env, jclass, jstring javaPath) { 58 ScopedUtfChars path(env, javaPath); 59 if (path.c_str() == NULL) { 60 return NULL; 61 } 62 63 extern bool realpath(const char* path, std::string& resolved); 64 std::string result; 65 if (!realpath(path.c_str(), result)) { 66 jniThrowIOException(env, errno); 67 return NULL; 68 } 69 return env->NewStringUTF(result.c_str()); 70 } 71 72 static jboolean File_setLastModifiedImpl(JNIEnv* env, jclass, jstring javaPath, jlong ms) { 73 ScopedUtfChars path(env, javaPath); 74 if (path.c_str() == NULL) { 75 return JNI_FALSE; 76 } 77 78 // We want to preserve the access time. 79 struct stat sb; 80 if (stat(path.c_str(), &sb) == -1) { 81 return JNI_FALSE; 82 } 83 84 // TODO: we could get microsecond resolution with utimes(3), "legacy" though it is. 85 utimbuf times; 86 times.actime = sb.st_atime; 87 times.modtime = static_cast<time_t>(ms / 1000); 88 return (utime(path.c_str(), ×) == 0); 89 } 90 91 // Iterates over the filenames in the given directory. 92 class ScopedReaddir { 93 public: 94 ScopedReaddir(const char* path) { 95 mDirStream = opendir(path); 96 mIsBad = (mDirStream == NULL); 97 } 98 99 ~ScopedReaddir() { 100 if (mDirStream != NULL) { 101 closedir(mDirStream); 102 } 103 } 104 105 // Returns the next filename, or NULL. 106 const char* next() { 107 dirent* result = NULL; 108 int rc = readdir_r(mDirStream, &mEntry, &result); 109 if (rc != 0) { 110 mIsBad = true; 111 return NULL; 112 } 113 return (result != NULL) ? result->d_name : NULL; 114 } 115 116 // Has an error occurred on this stream? 117 bool isBad() const { 118 return mIsBad; 119 } 120 121 private: 122 DIR* mDirStream; 123 dirent mEntry; 124 bool mIsBad; 125 126 // Disallow copy and assignment. 127 ScopedReaddir(const ScopedReaddir&); 128 void operator=(const ScopedReaddir&); 129 }; 130 131 typedef std::vector<std::string> DirEntries; 132 133 // Reads the directory referred to by 'pathBytes', adding each directory entry 134 // to 'entries'. 135 static bool readDirectory(JNIEnv* env, jstring javaPath, DirEntries& entries) { 136 ScopedUtfChars path(env, javaPath); 137 if (path.c_str() == NULL) { 138 return false; 139 } 140 141 ScopedReaddir dir(path.c_str()); 142 if (dir.isBad()) { 143 return false; 144 } 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 true; 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 int register_java_io_File(JNIEnv* env) { 172 return jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods)); 173 } 174