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