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 "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(JVM_NativePath((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 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_checkAccess0(JNIEnv * env,jobject this,jobject file,jint a)137 Java_java_io_UnixFileSystem_checkAccess0(JNIEnv *env, jobject this,
138                                          jobject file, jint a)
139 {
140     jboolean rv = JNI_FALSE;
141     int mode = 0;
142     switch (a) {
143     case java_io_FileSystem_ACCESS_OK:
144         mode = F_OK;
145         break;
146     case java_io_FileSystem_ACCESS_READ:
147         mode = R_OK;
148         break;
149     case java_io_FileSystem_ACCESS_WRITE:
150         mode = W_OK;
151         break;
152     case java_io_FileSystem_ACCESS_EXECUTE:
153         mode = X_OK;
154         break;
155     default: assert(0);
156     }
157     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
158         if (access(path, mode) == 0) {
159             rv = JNI_TRUE;
160         }
161     } END_PLATFORM_STRING(env, path);
162     return rv;
163 }
164 
165 
166 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setPermission0(JNIEnv * env,jobject this,jobject file,jint access,jboolean enable,jboolean owneronly)167 Java_java_io_UnixFileSystem_setPermission0(JNIEnv *env, jobject this,
168                                            jobject file,
169                                            jint access,
170                                            jboolean enable,
171                                            jboolean owneronly)
172 {
173     jboolean rv = JNI_FALSE;
174 
175     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
176         int amode = 0;
177         int mode;
178         switch (access) {
179         case java_io_FileSystem_ACCESS_READ:
180             if (owneronly)
181                 amode = S_IRUSR;
182             else
183                 amode = S_IRUSR | S_IRGRP | S_IROTH;
184             break;
185         case java_io_FileSystem_ACCESS_WRITE:
186             if (owneronly)
187                 amode = S_IWUSR;
188             else
189                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
190             break;
191         case java_io_FileSystem_ACCESS_EXECUTE:
192             if (owneronly)
193                 amode = S_IXUSR;
194             else
195                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
196             break;
197         default:
198             assert(0);
199         }
200         if (statMode(path, &mode)) {
201             if (enable)
202                 mode |= amode;
203             else
204                 mode &= ~amode;
205             if (chmod(path, mode) >= 0) {
206                 rv = JNI_TRUE;
207             }
208         }
209     } END_PLATFORM_STRING(env, path);
210     return rv;
211 }
212 
213 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv * env,jobject this,jobject file)214 Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv *env, jobject this,
215                                                  jobject file)
216 {
217     jlong rv = 0;
218 
219     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
220         struct stat64 sb;
221         if (stat64(path, &sb) == 0) {
222             rv = 1000 * (jlong)sb.st_mtime;
223         }
224     } END_PLATFORM_STRING(env, path);
225     return rv;
226 }
227 
228 
229 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getLength0(JNIEnv * env,jobject this,jobject file)230 Java_java_io_UnixFileSystem_getLength0(JNIEnv *env, jobject this,
231                                        jobject file)
232 {
233     jlong rv = 0;
234 
235     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
236         struct stat64 sb;
237         if (stat64(path, &sb) == 0) {
238             rv = sb.st_size;
239         }
240     } END_PLATFORM_STRING(env, path);
241     return rv;
242 }
243 
244 
245 /* -- File operations -- */
246 
247 
248 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv * env,jclass cls,jstring pathname)249 Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls,
250                                                    jstring pathname)
251 {
252     jboolean rv = JNI_FALSE;
253 
254     WITH_PLATFORM_STRING(env, pathname, path) {
255         int fd;
256         if (!strcmp (path, "/")) {
257             fd = JVM_EEXIST;    /* The root directory always exists */
258         } else {
259             fd = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666);
260         }
261         if (fd < 0) {
262             if (fd != JVM_EEXIST) {
263                 JNU_ThrowIOExceptionWithLastError(env, path);
264             }
265         } else {
266             JVM_Close(fd);
267             rv = JNI_TRUE;
268         }
269     } END_PLATFORM_STRING(env, path);
270     return rv;
271 }
272 
273 
274 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_delete0(JNIEnv * env,jobject this,jobject file)275 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
276                                     jobject file)
277 {
278     jboolean rv = JNI_FALSE;
279 
280     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
281         if (remove(path) == 0) {
282             rv = JNI_TRUE;
283         }
284     } END_PLATFORM_STRING(env, path);
285     return rv;
286 }
287 
288 
289 JNIEXPORT jobjectArray JNICALL
Java_java_io_UnixFileSystem_list0(JNIEnv * env,jobject this,jobject file)290 Java_java_io_UnixFileSystem_list0(JNIEnv *env, jobject this,
291                                   jobject file)
292 {
293     DIR *dir = NULL;
294     struct dirent64 *ptr;
295     struct dirent64 *result;
296     int len, maxlen;
297     jobjectArray rv, old;
298 
299     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
300         dir = opendir(path);
301     } END_PLATFORM_STRING(env, path);
302     if (dir == NULL) return NULL;
303 
304     ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
305     if (ptr == NULL) {
306         JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
307         closedir(dir);
308         return NULL;
309     }
310 
311     /* Allocate an initial String array */
312     len = 0;
313     maxlen = 16;
314     rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
315     if (rv == NULL) goto error;
316 
317     /* Scan the directory */
318     while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
319         jstring name;
320         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
321             continue;
322         if (len == maxlen) {
323             old = rv;
324             rv = (*env)->NewObjectArray(env, maxlen <<= 1,
325                                         JNU_ClassString(env), NULL);
326             if (rv == NULL) goto error;
327             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
328             (*env)->DeleteLocalRef(env, old);
329         }
330 #ifdef MACOSX
331         name = newStringPlatform(env, ptr->d_name);
332 #else
333         name = JNU_NewStringPlatform(env, ptr->d_name);
334 #endif
335         if (name == NULL) goto error;
336         (*env)->SetObjectArrayElement(env, rv, len++, name);
337         (*env)->DeleteLocalRef(env, name);
338     }
339     closedir(dir);
340     free(ptr);
341 
342     /* Copy the final results into an appropriately-sized array */
343     old = rv;
344     rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
345     if (rv == NULL) {
346         return NULL;
347     }
348     if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
349         return NULL;
350     }
351     return rv;
352 
353  error:
354     closedir(dir);
355     free(ptr);
356     return NULL;
357 }
358 
359 
360 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createDirectory0(JNIEnv * env,jobject this,jobject file)361 Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this,
362                                              jobject file)
363 {
364     jboolean rv = JNI_FALSE;
365 
366     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
367         if (mkdir(path, 0777) == 0) {
368             rv = JNI_TRUE;
369         }
370     } END_PLATFORM_STRING(env, path);
371     return rv;
372 }
373 
374 
375 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_rename0(JNIEnv * env,jobject this,jobject from,jobject to)376 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
377                                     jobject from, jobject to)
378 {
379     jboolean rv = JNI_FALSE;
380 
381     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
382         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
383             if (rename(fromPath, toPath) == 0) {
384                 rv = JNI_TRUE;
385             }
386         } END_PLATFORM_STRING(env, toPath);
387     } END_PLATFORM_STRING(env, fromPath);
388     return rv;
389 }
390 
391 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv * env,jobject this,jobject file,jlong time)392 Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this,
393                                                  jobject file, jlong time)
394 {
395     jboolean rv = JNI_FALSE;
396 
397     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
398         struct stat64 sb;
399 
400         if (stat64(path, &sb) == 0) {
401             struct timeval tv[2];
402 
403             /* Preserve access time */
404             tv[0].tv_sec = sb.st_atime;
405             tv[0].tv_usec = 0;
406 
407             /* Change last-modified time */
408             tv[1].tv_sec = time / 1000;
409             tv[1].tv_usec = (time % 1000) * 1000;
410 
411             if (utimes(path, tv) == 0)
412                 rv = JNI_TRUE;
413         }
414     } END_PLATFORM_STRING(env, path);
415 
416     return rv;
417 }
418 
419 
420 JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv * env,jobject this,jobject file)421 Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this,
422                                          jobject file)
423 {
424     jboolean rv = JNI_FALSE;
425 
426     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
427         int mode;
428         if (statMode(path, &mode)) {
429             if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
430                 rv = JNI_TRUE;
431             }
432         }
433     } END_PLATFORM_STRING(env, path);
434     return rv;
435 }
436 
437 JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace0(JNIEnv * env,jobject this,jobject file,jint t)438 Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
439                                       jobject file, jint t)
440 {
441     jlong rv = 0L;
442 
443     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
444         struct statvfs64 fsstat;
445         memset(&fsstat, 0, sizeof(fsstat));
446         if (statvfs64(path, &fsstat) == 0) {
447             switch(t) {
448             case java_io_FileSystem_SPACE_TOTAL:
449                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
450                                long_to_jlong(fsstat.f_blocks));
451                 break;
452             case java_io_FileSystem_SPACE_FREE:
453                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
454                                long_to_jlong(fsstat.f_bfree));
455                 break;
456             case java_io_FileSystem_SPACE_USABLE:
457                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
458                                long_to_jlong(fsstat.f_bavail));
459                 break;
460             default:
461                 assert(0);
462             }
463         }
464     } END_PLATFORM_STRING(env, path);
465     return rv;
466 }
467 
468 static JNINativeMethod gMethods[] = {
469     NATIVE_METHOD(UnixFileSystem, initIDs, "()V"),
470     NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"),
471     NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"),
472     NATIVE_METHOD(UnixFileSystem, checkAccess0, "(Ljava/io/File;I)Z"),
473     NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"),
474     NATIVE_METHOD(UnixFileSystem, getLastModifiedTime0, "(Ljava/io/File;)J"),
475     NATIVE_METHOD(UnixFileSystem, getLength0, "(Ljava/io/File;)J"),
476     NATIVE_METHOD(UnixFileSystem, createFileExclusively0, "(Ljava/lang/String;)Z"),
477     NATIVE_METHOD(UnixFileSystem, delete0, "(Ljava/io/File;)Z"),
478     NATIVE_METHOD(UnixFileSystem, list0, "(Ljava/io/File;)[Ljava/lang/String;"),
479     NATIVE_METHOD(UnixFileSystem, createDirectory0, "(Ljava/io/File;)Z"),
480     NATIVE_METHOD(UnixFileSystem, rename0, "(Ljava/io/File;Ljava/io/File;)Z"),
481     NATIVE_METHOD(UnixFileSystem, setLastModifiedTime0, "(Ljava/io/File;J)Z"),
482     NATIVE_METHOD(UnixFileSystem, setReadOnly0, "(Ljava/io/File;)Z"),
483     NATIVE_METHOD(UnixFileSystem, getSpace0, "(Ljava/io/File;I)J"),
484 };
485 
register_java_io_UnixFileSystem(JNIEnv * env)486 void register_java_io_UnixFileSystem(JNIEnv* env) {
487     jniRegisterNativeMethods(env, "java/io/UnixFileSystem", gMethods, NELEM(gMethods));
488 }
489