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