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 #include "AndroidSystemNatives.h" 19 #include "JNIHelp.h" 20 #include "LocalArray.h" 21 #include "ScopedByteArray.h" 22 #include "ScopedFd.h" 23 24 #include <string.h> 25 #include <fcntl.h> 26 #include <time.h> 27 #include <utime.h> 28 #include <unistd.h> 29 #include <stdlib.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <dirent.h> 33 #include <errno.h> 34 35 // BEGIN android-note: this file has been extensively rewritten to 36 // remove fixed-length buffers, buffer overruns, duplication, and 37 // poor choices of where to divide the work between Java and native 38 // code. 39 40 static jbyteArray java_io_File_getCanonImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 41 ScopedByteArray path(env, pathBytes); 42 // The only thing this native code currently does is truncate the byte[] at 43 // the first NUL. 44 // TODO: this is completely pointless. we should do this in Java, or do all of getCanonicalPath in native code. (realpath(2)?) 45 size_t length = strlen(&path[0]); 46 jbyteArray result = env->NewByteArray(length); 47 env->SetByteArrayRegion(result, 0, length, path.bytes()); 48 return result; 49 } 50 51 static jboolean java_io_File_deleteImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 52 ScopedByteArray path(env, pathBytes); 53 return (remove(&path[0]) == 0); 54 } 55 56 static bool doStat(JNIEnv* env, jbyteArray pathBytes, struct stat& sb) { 57 ScopedByteArray path(env, pathBytes); 58 return (stat(&path[0], &sb) == 0); 59 } 60 61 static jlong java_io_File_lengthImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 62 struct stat sb; 63 if (!doStat(env, pathBytes, sb)) { 64 // We must return 0 for files that don't exist. 65 // TODO: shouldn't we throw an IOException for ELOOP or EACCES? 66 return 0; 67 } 68 69 /* 70 * This android-changed code explicitly treats non-regular files (e.g., 71 * sockets and block-special devices) as having size zero. Some synthetic 72 * "regular" files may report an arbitrary non-zero size, but 73 * in these cases they generally report a block count of zero. 74 * So, use a zero block count to trump any other concept of 75 * size. 76 * 77 * TODO: why do we do this? 78 */ 79 if (!S_ISREG(sb.st_mode) || sb.st_blocks == 0) { 80 return 0; 81 } 82 return sb.st_size; 83 } 84 85 static jlong java_io_File_lastModifiedImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 86 struct stat sb; 87 if (!doStat(env, pathBytes, sb)) { 88 return 0; 89 } 90 return static_cast<jlong>(sb.st_mtime) * 1000L; 91 } 92 93 static jboolean java_io_File_isDirectoryImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 94 struct stat sb; 95 return (doStat(env, pathBytes, sb) && S_ISDIR(sb.st_mode)); 96 } 97 98 static jboolean java_io_File_isFileImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 99 struct stat sb; 100 return (doStat(env, pathBytes, sb) && S_ISREG(sb.st_mode)); 101 } 102 103 static jboolean java_io_File_existsImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 104 ScopedByteArray path(env, pathBytes); 105 return (access(&path[0], F_OK) == 0); 106 } 107 108 static jboolean java_io_File_isReadableImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 109 ScopedByteArray path(env, pathBytes); 110 return (access(&path[0], R_OK) == 0); 111 } 112 113 static jboolean java_io_File_isWritableImpl(JNIEnv* env, jobject recv, jbyteArray pathBytes) { 114 ScopedByteArray path(env, pathBytes); 115 return (access(&path[0], W_OK) == 0); 116 } 117 118 static jbyteArray java_io_File_getLinkImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 119 ScopedByteArray path(env, pathBytes); 120 121 // We can't know how big a buffer readlink(2) will need, so we need to 122 // loop until it says "that fit". 123 size_t bufSize = 512; 124 while (true) { 125 LocalArray<512> buf(bufSize); 126 ssize_t len = readlink(&path[0], &buf[0], buf.size() - 1); 127 if (len == -1) { 128 // An error occurred. 129 return pathBytes; 130 } 131 if (len < buf.size() - 1) { 132 // The buffer was big enough. 133 // TODO: why do we bother with the NUL termination? (if you change this, remove the "- 1"s above.) 134 buf[len] = '\0'; // readlink(2) doesn't NUL-terminate. 135 jbyteArray result = env->NewByteArray(len); 136 const jbyte* src = reinterpret_cast<const jbyte*>(&buf[0]); 137 env->SetByteArrayRegion(result, 0, len, src); 138 return result; 139 } 140 // Try again with a bigger buffer. 141 bufSize *= 2; 142 } 143 } 144 145 static jboolean java_io_File_setLastModifiedImpl(JNIEnv* env, jobject, jbyteArray pathBytes, jlong ms) { 146 ScopedByteArray path(env, pathBytes); 147 148 // We want to preserve the access time. 149 struct stat sb; 150 if (stat(&path[0], &sb) == -1) { 151 return JNI_FALSE; 152 } 153 154 // TODO: we could get microsecond resolution with utimes(3), "legacy" though it is. 155 utimbuf times; 156 times.actime = sb.st_atime; 157 times.modtime = static_cast<time_t>(ms / 1000); 158 return (utime(&path[0], ×) == 0); 159 } 160 161 static jboolean java_io_File_setReadOnlyImpl(JNIEnv* env, jobject recv, jbyteArray pathBytes) { 162 ScopedByteArray path(env, pathBytes); 163 164 struct stat sb; 165 if (stat(&path[0], &sb) == -1) { 166 return JNI_FALSE; 167 } 168 169 // Strictly, this is set-not-writable (i.e. we leave the execute 170 // bits untouched), but that's deliberate. 171 return (chmod(&path[0], sb.st_mode & ~0222) == 0); 172 } 173 174 // Iterates over the filenames in the given directory. 175 class ScopedReaddir { 176 public: 177 ScopedReaddir(const char* path) { 178 mDirStream = opendir(path); 179 mIsBad = (mDirStream == NULL); 180 } 181 182 ~ScopedReaddir() { 183 if (mDirStream != NULL) { 184 closedir(mDirStream); 185 } 186 } 187 188 // Returns the next filename, or NULL. 189 const char* next() { 190 dirent* result = NULL; 191 int rc = readdir_r(mDirStream, &mEntry, &result); 192 if (rc != 0) { 193 mIsBad = true; 194 return NULL; 195 } 196 return (result != NULL) ? result->d_name : NULL; 197 } 198 199 // Has an error occurred on this stream? 200 bool isBad() const { 201 return mIsBad; 202 } 203 204 private: 205 DIR* mDirStream; 206 dirent mEntry; 207 bool mIsBad; 208 }; 209 210 // DirEntry and DirEntries is a minimal equivalent of std::forward_list 211 // for the filenames. 212 struct DirEntry { 213 DirEntry(const char* filename) : name(strlen(filename)) { 214 strcpy(&name[0], filename); 215 next = NULL; 216 } 217 // On Linux, the ext family all limit the length of a directory entry to 218 // less than 256 characters. 219 LocalArray<256> name; 220 DirEntry* next; 221 }; 222 223 class DirEntries { 224 public: 225 DirEntries() : mSize(0), mHead(NULL) { 226 } 227 228 ~DirEntries() { 229 while (mHead) { 230 pop_front(); 231 } 232 } 233 234 bool push_front(const char* name) { 235 DirEntry* oldHead = mHead; 236 mHead = new DirEntry(name); 237 if (mHead == NULL) { 238 return false; 239 } 240 mHead->next = oldHead; 241 ++mSize; 242 return true; 243 } 244 245 const char* front() const { 246 return &mHead->name[0]; 247 } 248 249 void pop_front() { 250 DirEntry* popped = mHead; 251 if (popped != NULL) { 252 mHead = popped->next; 253 --mSize; 254 delete popped; 255 } 256 } 257 258 size_t size() const { 259 return mSize; 260 } 261 262 private: 263 size_t mSize; 264 DirEntry* mHead; 265 }; 266 267 // Reads the directory referred to by 'pathBytes', adding each directory entry 268 // to 'entries'. 269 static bool readDirectory(JNIEnv* env, jbyteArray pathBytes, DirEntries& entries) { 270 ScopedByteArray path(env, pathBytes); 271 ScopedReaddir dir(&path[0]); 272 if (dir.isBad()) { 273 return false; 274 } 275 const char* filename; 276 while ((filename = dir.next()) != NULL) { 277 if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0) { 278 if (!entries.push_front(filename)) { 279 jniThrowException(env, "java/lang/OutOfMemoryError", NULL); 280 return false; 281 } 282 } 283 } 284 return true; 285 } 286 287 static jobjectArray java_io_File_listImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 288 // Read the directory entries into an intermediate form. 289 DirEntries files; 290 if (!readDirectory(env, pathBytes, files)) { 291 return NULL; 292 } 293 // Translate the intermediate form into a Java String[]. 294 jclass stringClass = env->FindClass("java/lang/String"); 295 if (stringClass == NULL) { 296 return NULL; 297 } 298 jobjectArray result = env->NewObjectArray(files.size(), stringClass, NULL); 299 for (int i = 0; files.size() != 0; files.pop_front(), ++i) { 300 jstring javaFilename = env->NewStringUTF(files.front()); 301 if (env->ExceptionCheck()) { 302 return NULL; 303 } 304 env->SetObjectArrayElement(result, i, javaFilename); 305 if (env->ExceptionCheck()) { 306 return NULL; 307 } 308 env->DeleteLocalRef(javaFilename); 309 } 310 return result; 311 } 312 313 static jboolean java_io_File_mkdirImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 314 ScopedByteArray path(env, pathBytes); 315 // On Android, we don't want default permissions to allow global access. 316 return (mkdir(&path[0], S_IRWXU) == 0); 317 } 318 319 static jboolean java_io_File_createNewFileImpl(JNIEnv* env, jobject, jbyteArray pathBytes) { 320 ScopedByteArray path(env, pathBytes); 321 // On Android, we don't want default permissions to allow global access. 322 ScopedFd fd(open(&path[0], O_CREAT | O_EXCL, 0600)); 323 if (fd.get() != -1) { 324 // We created a new file. Success! 325 return JNI_TRUE; 326 } 327 if (errno == EEXIST) { 328 // The file already exists. 329 return JNI_FALSE; 330 } 331 jniThrowIOException(env, errno); 332 return JNI_FALSE; // Ignored by Java; keeps the C++ compiler happy. 333 } 334 335 static jboolean java_io_File_renameToImpl(JNIEnv* env, jobject, jbyteArray oldPathBytes, jbyteArray newPathBytes) { 336 ScopedByteArray oldPath(env, oldPathBytes); 337 ScopedByteArray newPath(env, newPathBytes); 338 return (rename(&oldPath[0], &newPath[0]) == 0); 339 } 340 341 static JNINativeMethod gMethods[] = { 342 /* name, signature, funcPtr */ 343 { "createNewFileImpl", "([B)Z", (void*) java_io_File_createNewFileImpl }, 344 { "deleteImpl", "([B)Z", (void*) java_io_File_deleteImpl }, 345 { "existsImpl", "([B)Z", (void*) java_io_File_existsImpl }, 346 { "getCanonImpl", "([B)[B", (void*) java_io_File_getCanonImpl }, 347 { "getLinkImpl", "([B)[B", (void*) java_io_File_getLinkImpl }, 348 { "isDirectoryImpl", "([B)Z", (void*) java_io_File_isDirectoryImpl }, 349 { "isFileImpl", "([B)Z", (void*) java_io_File_isFileImpl }, 350 { "isReadableImpl", "([B)Z", (void*) java_io_File_isReadableImpl }, 351 { "isWritableImpl", "([B)Z", (void*) java_io_File_isWritableImpl }, 352 { "lastModifiedImpl", "([B)J", (void*) java_io_File_lastModifiedImpl }, 353 { "lengthImpl", "([B)J", (void*) java_io_File_lengthImpl }, 354 { "listImpl", "([B)[Ljava/lang/String;", (void*) java_io_File_listImpl }, 355 { "mkdirImpl", "([B)Z", (void*) java_io_File_mkdirImpl }, 356 { "renameToImpl", "([B[B)Z",(void*) java_io_File_renameToImpl }, 357 { "setLastModifiedImpl","([BJ)Z", (void*) java_io_File_setLastModifiedImpl }, 358 { "setReadOnlyImpl", "([B)Z", (void*) java_io_File_setReadOnlyImpl }, 359 }; 360 int register_java_io_File(JNIEnv* env) { 361 return jniRegisterNativeMethods(env, "java/io/File", 362 gMethods, NELEM(gMethods)); 363 } 364