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