• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1998, 2018, 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 <unistd.h>
27 #include <assert.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 #ifdef MACOSX
32 #include <sys/param.h>
33 #include <sys/mount.h>
34 #else
35 #include <sys/statvfs.h>
36 #endif
37 #include <string.h>
38 #include <stdlib.h>
39 #include <dlfcn.h>
40 #include <limits.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <dirent.h>
44 
45 #include "jni.h"
46 #include "jni_util.h"
47 #include "jlong.h"
48 #include "io_util.h"
49 #include "io_util_md.h"
50 #include "java_io_FileSystem.h"
51 #include "java_io_UnixFileSystem.h"
52 
53 #include <nativehelper/JNIHelp.h>
54 
55 #if defined(_AIX)
56   #if !defined(NAME_MAX)
57     #define NAME_MAX MAXNAMLEN
58   #endif
59   #define DIR DIR64
60   #define opendir opendir64
61   #define closedir closedir64
62 #endif
63 
64 #if defined(__solaris__) && !defined(NAME_MAX)
65   #define NAME_MAX MAXNAMLEN
66 #endif
67 
68 // Android-changed: Fuchsia: Alias *64 on Fuchsia builds. http://b/119496969
69 // #if defined(_ALLBSD_SOURCE)
70 #if defined(_ALLBSD_SOURCE) || defined(__Fuchsia__)
71   #define dirent64 dirent
72   #define readdir64 readdir
73   #define stat64 stat
74   #ifndef MACOSX
75     #define statvfs64 statvfs
76   #endif
77 #endif
78 
79 /* -- Field IDs -- */
80 
81 static struct {
82     jfieldID path;
83 } ids;
84 
85 
86 #define NATIVE_METHOD(className, functionName, signature) \
87 { #functionName, signature, (void*)(Java_java_io_ ## className ## _ ## functionName) }
88 
89 JNIEXPORT void JNICALL
Java_java_io_UnixFileSystem_initIDs(JNIEnv * env,jclass cls)90 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
91 {
92     jclass fileClass = (*env)->FindClass(env, "java/io/File");
93     if (!fileClass) return;
94     ids.path = (*env)->GetFieldID(env, fileClass,
95                                   "path", "Ljava/lang/String;");
96 }
97 
98 /* -- Path operations -- */
99 
100 // Android-changed: hidden to avoid conflict with libm (b/135018555)
101 __attribute__((visibility("hidden")))
102 extern int canonicalize(char *path, const char *out, int len);
103 
104 JNIEXPORT jstring JNICALL
Java_java_io_UnixFileSystem_canonicalize0(JNIEnv * env,jobject this,jstring pathname)105 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
106                                           jstring pathname)
107 {
108     jstring rv = NULL;
109 
110     WITH_PLATFORM_STRING(env, pathname, path) {
111         char canonicalPath[PATH_MAX];
112         if (canonicalize((char *)path,
113                          canonicalPath, PATH_MAX) < 0) {
114             JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
115         } else {
116 #ifdef MACOSX
117             rv = newStringPlatform(env, canonicalPath);
118 #else
119             rv = JNU_NewStringPlatform(env, canonicalPath);
120 #endif
121         }
122     } END_PLATFORM_STRING(env, path);
123     return rv;
124 }
125 
126 
127 /* -- Attribute accessors -- */
128 
129 
130 static jboolean
statMode(const char * path,int * mode)131 statMode(const char *path, int *mode)
132 {
133     struct stat64 sb;
134     if (stat64(path, &sb) == 0) {
135         *mode = sb.st_mode;
136         return JNI_TRUE;
137     }
138     return JNI_FALSE;
139 }
140 
141 
142 JNIEXPORT jint JNICALL
Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv * env,jobject this,jstring abspath)143 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
144                                                   jstring abspath)
145 {
146     jint rv = 0;
147 
148     /* ----- BEGIN android -----
149     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {*/
150     WITH_PLATFORM_STRING(env, abspath, path) {
151     // ----- END android -----
152         int mode;
153         if (statMode(path, &mode)) {
154             int fmt = mode & S_IFMT;
155             rv = (jint) (java_io_FileSystem_BA_EXISTS
156                   | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
157                   | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
158         }
159     } END_PLATFORM_STRING(env, path);
160     return rv;
161 }
162 
163 // BEGIN Android-removed: Access files through common interface.
164 /*
165 JNIEXPORT jboolean JNICALL
166 Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
167                                         jobject file, jint a)
168 {
169     jboolean rv = JNI_FALSE;
170     int mode = 0;
171     switch (a) {
172     case java_io_FileSystem_ACCESS_READ:
173         mode = R_OK;
174         break;
175     case java_io_FileSystem_ACCESS_WRITE:
176         mode = W_OK;
177         break;
178     case java_io_FileSystem_ACCESS_EXECUTE:
179         mode = X_OK;
180         break;
181     default: assert(0);
182     }
183     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
184         if (access(path, mode) == 0) {
185             rv = JNI_TRUE;
186         }
187     } END_PLATFORM_STRING(env, path);
188     return rv;
189 }
190 */
191 // END Android-removed: Access files through common interface.
192 
193 // Android-changed: Name changed because of added thread policy check
194 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setPermission0(JNIEnv * env,jobject this,jobject file,jint access,jboolean enable,jboolean owneronly)195 Java_java_io_UnixFileSystem_setPermission0(JNIEnv *env, jobject this,
196                                            jobject file,
197                                            jint access,
198                                            jboolean enable,
199                                            jboolean owneronly)
200 {
201     jboolean rv = JNI_FALSE;
202 
203     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
204         int amode = 0;
205         int mode;
206         switch (access) {
207         case java_io_FileSystem_ACCESS_READ:
208             if (owneronly)
209                 amode = S_IRUSR;
210             else
211                 amode = S_IRUSR | S_IRGRP | S_IROTH;
212             break;
213         case java_io_FileSystem_ACCESS_WRITE:
214             if (owneronly)
215                 amode = S_IWUSR;
216             else
217                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
218             break;
219         case java_io_FileSystem_ACCESS_EXECUTE:
220             if (owneronly)
221                 amode = S_IXUSR;
222             else
223                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
224             break;
225         default:
226             assert(0);
227         }
228         if (statMode(path, &mode)) {
229             if (enable)
230                 mode |= amode;
231             else
232                 mode &= ~amode;
233             if (chmod(path, mode) >= 0) {
234                 rv = JNI_TRUE;
235             }
236         }
237     } END_PLATFORM_STRING(env, path);
238     return rv;
239 }
240 
241 // Android-changed: Name changed because of added thread policy check
242 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv * env,jobject this,jobject file)243 Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv *env, jobject this,
244                                                  jobject file)
245 {
246     jlong rv = 0;
247 
248     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
249         struct stat64 sb;
250         if (stat64(path, &sb) == 0) {
251 #if defined(_AIX)
252             rv =  (jlong)sb.st_mtime * 1000;
253             rv += (jlong)sb.st_mtime_n / 1000000;
254 #elif defined(MACOSX)
255             rv  = (jlong)sb.st_mtimespec.tv_sec * 1000;
256             rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000;
257 #else
258             rv  = (jlong)sb.st_mtim.tv_sec * 1000;
259             rv += (jlong)sb.st_mtim.tv_nsec / 1000000;
260 #endif
261         }
262     } END_PLATFORM_STRING(env, path);
263     return rv;
264 }
265 
266 // BEGIN Android-removed: Access files through common interface.
267 /*
268 JNIEXPORT jlong JNICALL
269 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
270                                       jobject file)
271 {
272     jlong rv = 0;
273 
274     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
275         struct stat64 sb;
276         if (stat64(path, &sb) == 0) {
277             rv = sb.st_size;
278         }
279     } END_PLATFORM_STRING(env, path);
280     return rv;
281 }
282 */
283 // END Android-removed: Access files through common interface.
284 
285 
286 /* -- File operations -- */
287 
288 // Android-changed: Name changed because of added thread policy check
289 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv * env,jclass cls,jstring pathname)290 Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls,
291                                                    jstring pathname)
292 {
293     jboolean rv = JNI_FALSE;
294 
295     WITH_PLATFORM_STRING(env, pathname, path) {
296         FD fd;
297         /* The root directory always exists */
298         if (strcmp (path, "/")) {
299             fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
300             if (fd < 0) {
301                 if (errno != EEXIST)
302                     JNU_ThrowIOExceptionWithLastError(env, path);
303             } else {
304                 if (close(fd) == -1)
305                     JNU_ThrowIOExceptionWithLastError(env, path);
306                 rv = JNI_TRUE;
307             }
308         }
309     } END_PLATFORM_STRING(env, path);
310     return rv;
311 }
312 
313 
314 // BEGIN Android-removed: Access files through common interface.
315 /*
316 JNIEXPORT jboolean JNICALL
317 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
318                                     jobject file)
319 {
320     jboolean rv = JNI_FALSE;
321 
322     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
323         if (remove(path) == 0) {
324             rv = JNI_TRUE;
325         }
326     } END_PLATFORM_STRING(env, path);
327     return rv;
328 }
329 */
330 // END Android-removed: Access files through common interface.
331 
332 // Android-changed: Name changed because of added thread policy check
333 JNIEXPORT jobjectArray JNICALL
Java_java_io_UnixFileSystem_list0(JNIEnv * env,jobject this,jobject file)334 Java_java_io_UnixFileSystem_list0(JNIEnv *env, jobject this,
335                                   jobject file)
336 {
337     DIR *dir = NULL;
338     struct dirent64 *ptr;
339     int len, maxlen;
340     jobjectArray rv, old;
341     jclass str_class;
342 
343     str_class = JNU_ClassString(env);
344     CHECK_NULL_RETURN(str_class, NULL);
345 
346 
347     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
348         dir = opendir(path);
349     } END_PLATFORM_STRING(env, path);
350     if (dir == NULL) return NULL;
351 
352     /* Allocate an initial String array */
353     len = 0;
354     maxlen = 16;
355     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
356     if (rv == NULL) goto error;
357 
358     /* Scan the directory */
359     while ((ptr = readdir64(dir)) != NULL) {
360         jstring name;
361         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
362             continue;
363         if (len == maxlen) {
364             old = rv;
365             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
366             if (rv == NULL) goto error;
367             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
368             (*env)->DeleteLocalRef(env, old);
369         }
370 #ifdef MACOSX
371         name = newStringPlatform(env, ptr->d_name);
372 #else
373         name = JNU_NewStringPlatform(env, ptr->d_name);
374 #endif
375         if (name == NULL) goto error;
376         (*env)->SetObjectArrayElement(env, rv, len++, name);
377         (*env)->DeleteLocalRef(env, name);
378     }
379     closedir(dir);
380 
381     /* Copy the final results into an appropriately-sized array */
382     old = rv;
383     rv = (*env)->NewObjectArray(env, len, str_class, NULL);
384     if (rv == NULL) {
385         return NULL;
386     }
387     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
388         return NULL;
389     }
390     return rv;
391 
392  error:
393     closedir(dir);
394     return NULL;
395 }
396 
397 // Android-changed: Name changed because of added thread policy check
398 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createDirectory0(JNIEnv * env,jobject this,jobject file)399 Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this,
400                                              jobject file)
401 {
402     jboolean rv = JNI_FALSE;
403 
404     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
405         if (mkdir(path, 0777) == 0) {
406             rv = JNI_TRUE;
407         }
408     } END_PLATFORM_STRING(env, path);
409     return rv;
410 }
411 
412 
413 // BEGIN Android-removed: Access files through common interface.
414 /*
415 JNIEXPORT jboolean JNICALL
416 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
417                                     jobject from, jobject to)
418 {
419     jboolean rv = JNI_FALSE;
420 
421     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
422         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
423             if (rename(fromPath, toPath) == 0) {
424                 rv = JNI_TRUE;
425             }
426         } END_PLATFORM_STRING(env, toPath);
427     } END_PLATFORM_STRING(env, fromPath);
428     return rv;
429 }
430 */
431 // END Android-removed: Access files through common interface.
432 
433 // Android-changed: Name changed because of added thread policy check
434 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv * env,jobject this,jobject file,jlong time)435 Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this,
436                                                  jobject file, jlong time)
437 {
438     jboolean rv = JNI_FALSE;
439 
440     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
441         struct stat64 sb;
442 
443         if (stat64(path, &sb) == 0) {
444             struct timeval tv[2];
445 
446             /* Preserve access time */
447 #if defined(_AIX)
448             tv[0].tv_sec = sb.st_atime;
449             tv[0].tv_usec = sb.st_atime_n / 1000;
450 #elif defined(MACOSX)
451             tv[0].tv_sec = sb.st_atimespec.tv_sec;
452             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
453 #else
454             tv[0].tv_sec = sb.st_atim.tv_sec;
455             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
456 #endif
457             /* Change last-modified time */
458             tv[1].tv_sec = time / 1000;
459             tv[1].tv_usec = (time % 1000) * 1000;
460 
461             if (utimes(path, tv) == 0)
462                 rv = JNI_TRUE;
463         }
464     } END_PLATFORM_STRING(env, path);
465 
466     return rv;
467 }
468 
469 // Android-changed: Name changed because of added thread policy check
470 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv * env,jobject this,jobject file)471 Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this,
472                                          jobject file)
473 {
474     jboolean rv = JNI_FALSE;
475 
476     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
477         int mode;
478         if (statMode(path, &mode)) {
479             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
480                 rv = JNI_TRUE;
481             }
482         }
483     } END_PLATFORM_STRING(env, path);
484     return rv;
485 }
486 
487 // Android-changed: Name changed because of added thread policy check
488 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace0(JNIEnv * env,jobject this,jobject file,jint t)489 Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
490                                       jobject file, jint t)
491 {
492     jlong rv = 0L;
493 
494     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
495 #ifdef MACOSX
496         struct statfs fsstat;
497 #else
498         struct statvfs64 fsstat;
499 #endif
500         memset(&fsstat, 0, sizeof(fsstat));
501 #ifdef MACOSX
502         if (statfs(path, &fsstat) == 0) {
503             switch(t) {
504                 case java_io_FileSystem_SPACE_TOTAL:
505                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
506                                    long_to_jlong(fsstat.f_blocks));
507                     break;
508                 case java_io_FileSystem_SPACE_FREE:
509                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
510                                    long_to_jlong(fsstat.f_bfree));
511                     break;
512                 case java_io_FileSystem_SPACE_USABLE:
513                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
514                                    long_to_jlong(fsstat.f_bavail));
515                     break;
516                 default:
517                     assert(0);
518             }
519         }
520 #else
521         if (statvfs64(path, &fsstat) == 0) {
522             switch(t) {
523             case java_io_FileSystem_SPACE_TOTAL:
524                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
525                                long_to_jlong(fsstat.f_blocks));
526                 break;
527             case java_io_FileSystem_SPACE_FREE:
528                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
529                                long_to_jlong(fsstat.f_bfree));
530                 break;
531             case java_io_FileSystem_SPACE_USABLE:
532                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
533                                long_to_jlong(fsstat.f_bavail));
534                 break;
535             default:
536                 assert(0);
537             }
538         }
539 #endif
540     } END_PLATFORM_STRING(env, path);
541     return rv;
542 }
543 
544 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getNameMax0(JNIEnv * env,jobject this,jstring pathname)545 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
546                                         jstring pathname)
547 {
548     jlong length = -1;
549     WITH_PLATFORM_STRING(env, pathname, path) {
550         length = (jlong)pathconf(path, _PC_NAME_MAX);
551     } END_PLATFORM_STRING(env, path);
552     return length != -1 ? length : (jlong)NAME_MAX;
553 }
554 
555 static JNINativeMethod gMethods[] = {
556     NATIVE_METHOD(UnixFileSystem, initIDs, "()V"),
557     NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"),
558     NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"),
559     NATIVE_METHOD(UnixFileSystem, getNameMax0, "(Ljava/lang/String;)J"),
560     NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"),
561     NATIVE_METHOD(UnixFileSystem, getLastModifiedTime0, "(Ljava/io/File;)J"),
562     NATIVE_METHOD(UnixFileSystem, createFileExclusively0, "(Ljava/lang/String;)Z"),
563     NATIVE_METHOD(UnixFileSystem, list0, "(Ljava/io/File;)[Ljava/lang/String;"),
564     NATIVE_METHOD(UnixFileSystem, createDirectory0, "(Ljava/io/File;)Z"),
565     NATIVE_METHOD(UnixFileSystem, setLastModifiedTime0, "(Ljava/io/File;J)Z"),
566     NATIVE_METHOD(UnixFileSystem, setReadOnly0, "(Ljava/io/File;)Z"),
567     NATIVE_METHOD(UnixFileSystem, getSpace0, "(Ljava/io/File;I)J"),
568 };
569 
register_java_io_UnixFileSystem(JNIEnv * env)570 void register_java_io_UnixFileSystem(JNIEnv* env) {
571     jniRegisterNativeMethods(env, "java/io/UnixFileSystem", gMethods, NELEM(gMethods));
572 }
573