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