• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
File_canonicalizePath(JNIEnv * env,jclass,jstring javaPath)41 static 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 
File_setLastModifiedImpl(JNIEnv * env,jclass,jstring javaPath,jlong ms)56 static 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(), &times) == 0);
73 }
74 
75 // Iterates over the filenames in the given directory.
76 class ScopedReaddir {
77  public:
ScopedReaddir(const char * path)78   ScopedReaddir(const char* path) {
79     mDirStream = opendir(path);
80     mIsBad = (mDirStream == NULL);
81   }
82 
~ScopedReaddir()83   ~ScopedReaddir() {
84     if (mDirStream != NULL) {
85       closedir(mDirStream);
86     }
87   }
88 
89   // Returns the next filename, or NULL.
next()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?
isBad() const106   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 
119 typedef std::vector<std::string> DirEntries;
120 
121 // Reads the directory referred to by 'pathBytes', adding each directory entry
122 // to 'entries'.
readDirectory(JNIEnv * env,jstring javaPath,DirEntries & entries)123 static 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 
File_listImpl(JNIEnv * env,jclass,jstring javaPath)140 static 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 
150 static 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 };
register_java_io_File(JNIEnv * env)155 void register_java_io_File(JNIEnv* env) {
156   jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
157 }
158