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