• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <assert.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/stat.h>
30 #include <sys/statvfs.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <dlfcn.h>
34 #include <limits.h>
35 
36 #include "jni.h"
37 #include "jni_util.h"
38 #include "jlong.h"
39 #include "jvm.h"
40 #include "io_util.h"
41 #include "io_util_md.h"
42 #include "java_io_FileSystem.h"
43 #include "java_io_UnixFileSystem.h"
44 
45 #include <nativehelper/JNIHelp.h>
46 
47 #if defined(_ALLBSD_SOURCE)
48 #define dirent64 dirent
49 #define readdir64_r readdir_r
50 #define stat64 stat
51 #define statvfs64 statvfs
52 #endif
53 
54 /* -- Field IDs -- */
55 
56 static struct {
57     jfieldID path;
58 } ids;
59 
60 
61 #define NATIVE_METHOD(className, functionName, signature) \
62 { #functionName, signature, (void*)(Java_java_io_ ## className ## _ ## functionName) }
63 
64 JNIEXPORT void JNICALL
Java_java_io_UnixFileSystem_initIDs(JNIEnv * env,jclass cls)65 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
66 {
67     jclass fileClass = (*env)->FindClass(env, "java/io/File");
68     if (!fileClass) return;
69     ids.path = (*env)->GetFieldID(env, fileClass,
70                                   "path", "Ljava/lang/String;");
71 }
72 
73 /* -- Path operations -- */
74 
75 extern int canonicalize(char *path, const char *out, int len);
76 
77 JNIEXPORT jstring JNICALL
Java_java_io_UnixFileSystem_canonicalize0(JNIEnv * env,jobject this,jstring pathname)78 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
79                                           jstring pathname)
80 {
81     jstring rv = NULL;
82 
83     WITH_PLATFORM_STRING(env, pathname, path) {
84         char canonicalPath[JVM_MAXPATHLEN];
85         if (canonicalize((char *)path,
86                          canonicalPath, JVM_MAXPATHLEN) < 0) {
87             JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
88         } else {
89 #ifdef MACOSX
90             rv = newStringPlatform(env, canonicalPath);
91 #else
92             rv = JNU_NewStringPlatform(env, canonicalPath);
93 #endif
94         }
95     } END_PLATFORM_STRING(env, path);
96     return rv;
97 }
98 
99 
100 /* -- Attribute accessors -- */
101 
102 
103 static jboolean
statMode(const char * path,int * mode)104 statMode(const char *path, int *mode)
105 {
106     struct stat64 sb;
107     if (stat64(path, &sb) == 0) {
108         *mode = sb.st_mode;
109         return JNI_TRUE;
110     }
111     return JNI_FALSE;
112 }
113 
114 
115 JNIEXPORT jint JNICALL
Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv * env,jobject this,jstring abspath)116 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
117                                                   jstring abspath)
118 {
119     jint rv = 0;
120 
121     /* ----- BEGIN android -----
122     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {*/
123     WITH_PLATFORM_STRING(env, abspath, path) {
124     // ----- END android -----
125         int mode;
126         if (statMode(path, &mode)) {
127             int fmt = mode & S_IFMT;
128             rv = (jint) (java_io_FileSystem_BA_EXISTS
129                   | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
130                   | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
131         }
132     } END_PLATFORM_STRING(env, path);
133     return rv;
134 }
135 
136 // Android-changed: Name changed because of added thread policy check
137 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_checkAccess0(JNIEnv * env,jobject this,jobject file,jint a)138 Java_java_io_UnixFileSystem_checkAccess0(JNIEnv *env, jobject this,
139                                          jobject file, jint a)
140 {
141     jboolean rv = JNI_FALSE;
142     int mode = 0;
143     switch (a) {
144     // Android-changed: Added ACCESS_OK case
145     case java_io_FileSystem_ACCESS_OK:
146         mode = F_OK;
147         break;
148     case java_io_FileSystem_ACCESS_READ:
149         mode = R_OK;
150         break;
151     case java_io_FileSystem_ACCESS_WRITE:
152         mode = W_OK;
153         break;
154     case java_io_FileSystem_ACCESS_EXECUTE:
155         mode = X_OK;
156         break;
157     default: assert(0);
158     }
159     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
160         if (access(path, mode) == 0) {
161             rv = JNI_TRUE;
162         }
163     } END_PLATFORM_STRING(env, path);
164     return rv;
165 }
166 
167 // Android-changed: Name changed because of added thread policy check
168 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setPermission0(JNIEnv * env,jobject this,jobject file,jint access,jboolean enable,jboolean owneronly)169 Java_java_io_UnixFileSystem_setPermission0(JNIEnv *env, jobject this,
170                                            jobject file,
171                                            jint access,
172                                            jboolean enable,
173                                            jboolean owneronly)
174 {
175     jboolean rv = JNI_FALSE;
176 
177     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
178         int amode = 0;
179         int mode;
180         switch (access) {
181         case java_io_FileSystem_ACCESS_READ:
182             if (owneronly)
183                 amode = S_IRUSR;
184             else
185                 amode = S_IRUSR | S_IRGRP | S_IROTH;
186             break;
187         case java_io_FileSystem_ACCESS_WRITE:
188             if (owneronly)
189                 amode = S_IWUSR;
190             else
191                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
192             break;
193         case java_io_FileSystem_ACCESS_EXECUTE:
194             if (owneronly)
195                 amode = S_IXUSR;
196             else
197                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
198             break;
199         default:
200             assert(0);
201         }
202         if (statMode(path, &mode)) {
203             if (enable)
204                 mode |= amode;
205             else
206                 mode &= ~amode;
207             if (chmod(path, mode) >= 0) {
208                 rv = JNI_TRUE;
209             }
210         }
211     } END_PLATFORM_STRING(env, path);
212     return rv;
213 }
214 
215 // Android-changed: Name changed because of added thread policy check
216 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv * env,jobject this,jobject file)217 Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv *env, jobject this,
218                                                  jobject file)
219 {
220     jlong rv = 0;
221 
222     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
223         struct stat64 sb;
224         if (stat64(path, &sb) == 0) {
225             rv = 1000 * (jlong)sb.st_mtime;
226         }
227     } END_PLATFORM_STRING(env, path);
228     return rv;
229 }
230 
231 // Android-changed: Name changed because of added thread policy check
232 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getLength0(JNIEnv * env,jobject this,jobject file)233 Java_java_io_UnixFileSystem_getLength0(JNIEnv *env, jobject this,
234                                        jobject file)
235 {
236     jlong rv = 0;
237 
238     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
239         struct stat64 sb;
240         if (stat64(path, &sb) == 0) {
241             rv = sb.st_size;
242         }
243     } END_PLATFORM_STRING(env, path);
244     return rv;
245 }
246 
247 
248 /* -- File operations -- */
249 
250 // Android-changed: Name changed because of added thread policy check
251 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv * env,jclass cls,jstring pathname)252 Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls,
253                                                    jstring pathname)
254 {
255     jboolean rv = JNI_FALSE;
256 
257     WITH_PLATFORM_STRING(env, pathname, path) {
258         FD fd;
259         /* The root directory always exists */
260         if (strcmp (path, "/")) {
261             fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
262             if (fd < 0) {
263                 if (errno != EEXIST)
264                     JNU_ThrowIOExceptionWithLastError(env, path);
265             } else {
266                 if (close(fd) == -1)
267                     JNU_ThrowIOExceptionWithLastError(env, path);
268                 rv = JNI_TRUE;
269             }
270         }
271     } END_PLATFORM_STRING(env, path);
272     return rv;
273 }
274 
275 
276 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_delete0(JNIEnv * env,jobject this,jobject file)277 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
278                                     jobject file)
279 {
280     jboolean rv = JNI_FALSE;
281 
282     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
283         if (remove(path) == 0) {
284             rv = JNI_TRUE;
285         }
286     } END_PLATFORM_STRING(env, path);
287     return rv;
288 }
289 
290 // Android-changed: Name changed because of added thread policy check
291 JNIEXPORT jobjectArray JNICALL
Java_java_io_UnixFileSystem_list0(JNIEnv * env,jobject this,jobject file)292 Java_java_io_UnixFileSystem_list0(JNIEnv *env, jobject this,
293                                   jobject file)
294 {
295     DIR *dir = NULL;
296     struct dirent64 *ptr;
297     struct dirent64 *result;
298     int len, maxlen;
299     jobjectArray rv, old;
300     jclass str_class;
301 
302     str_class = JNU_ClassString(env);
303     CHECK_NULL_RETURN(str_class, NULL);
304 
305 
306     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
307         dir = opendir(path);
308     } END_PLATFORM_STRING(env, path);
309     if (dir == NULL) return NULL;
310 
311     ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
312     if (ptr == NULL) {
313         JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
314         closedir(dir);
315         return NULL;
316     }
317 
318     /* Allocate an initial String array */
319     len = 0;
320     maxlen = 16;
321     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
322     if (rv == NULL) goto error;
323 
324     /* Scan the directory */
325     while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
326         jstring name;
327         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
328             continue;
329         if (len == maxlen) {
330             old = rv;
331             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
332             if (rv == NULL) goto error;
333             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
334             (*env)->DeleteLocalRef(env, old);
335         }
336 #ifdef MACOSX
337         name = newStringPlatform(env, ptr->d_name);
338 #else
339         name = JNU_NewStringPlatform(env, ptr->d_name);
340 #endif
341         if (name == NULL) goto error;
342         (*env)->SetObjectArrayElement(env, rv, len++, name);
343         (*env)->DeleteLocalRef(env, name);
344     }
345     closedir(dir);
346     free(ptr);
347 
348     /* Copy the final results into an appropriately-sized array */
349     old = rv;
350     rv = (*env)->NewObjectArray(env, len, str_class, NULL);
351     if (rv == NULL) {
352         return NULL;
353     }
354     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
355         return NULL;
356     }
357     return rv;
358 
359  error:
360     closedir(dir);
361     free(ptr);
362     return NULL;
363 }
364 
365 // Android-changed: Name changed because of added thread policy check
366 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createDirectory0(JNIEnv * env,jobject this,jobject file)367 Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this,
368                                              jobject file)
369 {
370     jboolean rv = JNI_FALSE;
371 
372     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
373         if (mkdir(path, 0777) == 0) {
374             rv = JNI_TRUE;
375         }
376     } END_PLATFORM_STRING(env, path);
377     return rv;
378 }
379 
380 
381 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_rename0(JNIEnv * env,jobject this,jobject from,jobject to)382 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
383                                     jobject from, jobject to)
384 {
385     jboolean rv = JNI_FALSE;
386 
387     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
388         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
389             if (rename(fromPath, toPath) == 0) {
390                 rv = JNI_TRUE;
391             }
392         } END_PLATFORM_STRING(env, toPath);
393     } END_PLATFORM_STRING(env, fromPath);
394     return rv;
395 }
396 
397 // Android-changed: Name changed because of added thread policy check
398 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv * env,jobject this,jobject file,jlong time)399 Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this,
400                                                  jobject file, jlong time)
401 {
402     jboolean rv = JNI_FALSE;
403 
404     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
405         struct stat64 sb;
406 
407         if (stat64(path, &sb) == 0) {
408             struct timeval tv[2];
409 
410             /* Preserve access time */
411             tv[0].tv_sec = sb.st_atime;
412             tv[0].tv_usec = 0;
413 
414             /* Change last-modified time */
415             tv[1].tv_sec = time / 1000;
416             tv[1].tv_usec = (time % 1000) * 1000;
417 
418             if (utimes(path, tv) == 0)
419                 rv = JNI_TRUE;
420         }
421     } END_PLATFORM_STRING(env, path);
422 
423     return rv;
424 }
425 
426 // Android-changed: Name changed because of added thread policy check
427 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv * env,jobject this,jobject file)428 Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this,
429                                          jobject file)
430 {
431     jboolean rv = JNI_FALSE;
432 
433     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
434         int mode;
435         if (statMode(path, &mode)) {
436             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
437                 rv = JNI_TRUE;
438             }
439         }
440     } END_PLATFORM_STRING(env, path);
441     return rv;
442 }
443 
444 // Android-changed: Name changed because of added thread policy check
445 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace0(JNIEnv * env,jobject this,jobject file,jint t)446 Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
447                                       jobject file, jint t)
448 {
449     jlong rv = 0L;
450 
451     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
452         struct statvfs64 fsstat;
453         memset(&fsstat, 0, sizeof(fsstat));
454         if (statvfs64(path, &fsstat) == 0) {
455             switch(t) {
456             case java_io_FileSystem_SPACE_TOTAL:
457                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
458                                long_to_jlong(fsstat.f_blocks));
459                 break;
460             case java_io_FileSystem_SPACE_FREE:
461                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
462                                long_to_jlong(fsstat.f_bfree));
463                 break;
464             case java_io_FileSystem_SPACE_USABLE:
465                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
466                                long_to_jlong(fsstat.f_bavail));
467                 break;
468             default:
469                 assert(0);
470             }
471         }
472     } END_PLATFORM_STRING(env, path);
473     return rv;
474 }
475 
476 static JNINativeMethod gMethods[] = {
477     NATIVE_METHOD(UnixFileSystem, initIDs, "()V"),
478     NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"),
479     NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"),
480     NATIVE_METHOD(UnixFileSystem, checkAccess0, "(Ljava/io/File;I)Z"),
481     NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"),
482     NATIVE_METHOD(UnixFileSystem, getLastModifiedTime0, "(Ljava/io/File;)J"),
483     NATIVE_METHOD(UnixFileSystem, getLength0, "(Ljava/io/File;)J"),
484     NATIVE_METHOD(UnixFileSystem, createFileExclusively0, "(Ljava/lang/String;)Z"),
485     NATIVE_METHOD(UnixFileSystem, delete0, "(Ljava/io/File;)Z"),
486     NATIVE_METHOD(UnixFileSystem, list0, "(Ljava/io/File;)[Ljava/lang/String;"),
487     NATIVE_METHOD(UnixFileSystem, createDirectory0, "(Ljava/io/File;)Z"),
488     NATIVE_METHOD(UnixFileSystem, rename0, "(Ljava/io/File;Ljava/io/File;)Z"),
489     NATIVE_METHOD(UnixFileSystem, setLastModifiedTime0, "(Ljava/io/File;J)Z"),
490     NATIVE_METHOD(UnixFileSystem, setReadOnly0, "(Ljava/io/File;)Z"),
491     NATIVE_METHOD(UnixFileSystem, getSpace0, "(Ljava/io/File;I)J"),
492 };
493 
register_java_io_UnixFileSystem(JNIEnv * env)494 void register_java_io_UnixFileSystem(JNIEnv* env) {
495     jniRegisterNativeMethods(env, "java/io/UnixFileSystem", gMethods, NELEM(gMethods));
496 }
497