blob: 300393a003aa9af94608a346fe85e4610ca71bd0 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 "java_io_FileSystem.h"
42#include "java_io_UnixFileSystem.h"
43
44
45/* -- Field IDs -- */
46
47static struct {
48 jfieldID path;
49} ids;
50
51
52JNIEXPORT void JNICALL
53Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
54{
55 jclass fileClass = (*env)->FindClass(env, "java/io/File");
56 if (!fileClass) return;
57 ids.path = (*env)->GetFieldID(env, fileClass,
58 "path", "Ljava/lang/String;");
59}
60
61
62/* -- Large-file support -- */
63
64/* LINUX_FIXME: ifdef __solaris__ here is wrong. We need to move the
65 * definition of stat64 into a solaris_largefile.h and create a
66 * linux_largefile.h with a good stat64 structure to compile on
67 * glibc2.0 based systems.
68 */
69#if defined(__solaris__) && !defined(_LFS_LARGEFILE) || !_LFS_LARGEFILE
70
71/* The stat64 structure must be provided for systems without large-file support
72 (e.g., Solaris 2.5.1). These definitions are copied from the Solaris 2.6
73 <sys/stat.h> and <sys/types.h> files.
74 */
75
76typedef longlong_t off64_t; /* offsets within files */
77typedef u_longlong_t ino64_t; /* expanded inode type */
78typedef longlong_t blkcnt64_t; /* count of file blocks */
79
80struct stat64 {
81 dev_t st_dev;
82 long st_pad1[3];
83 ino64_t st_ino;
84 mode_t st_mode;
85 nlink_t st_nlink;
86 uid_t st_uid;
87 gid_t st_gid;
88 dev_t st_rdev;
89 long st_pad2[2];
90 off64_t st_size;
91 timestruc_t st_atim;
92 timestruc_t st_mtim;
93 timestruc_t st_ctim;
94 long st_blksize;
95 blkcnt64_t st_blocks;
96 char st_fstype[_ST_FSTYPSZ];
97 long st_pad4[8];
98};
99
100#endif /* !_LFS_LARGEFILE */
101
102typedef int (*STAT64)(const char *, struct stat64 *);
103
104#if defined(__linux__) && defined(_LARGEFILE64_SOURCE)
105static STAT64 stat64_ptr = &stat64;
106#else
107static STAT64 stat64_ptr = NULL;
108#endif
109
110#ifndef __linux__
111#ifdef __GNUC__
112static void init64IO(void) __attribute__((constructor));
113#else
114#pragma init(init64IO)
115#endif
116#endif
117
118static void init64IO(void) {
119 void *handle = dlopen(0, RTLD_LAZY);
120 stat64_ptr = (STAT64) dlsym(handle, "_stat64");
121 dlclose(handle);
122}
123
124
125/* -- Path operations -- */
126
127extern int canonicalize(char *path, const char *out, int len);
128
129JNIEXPORT jstring JNICALL
130Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
131 jstring pathname)
132{
133 jstring rv = NULL;
134
135 WITH_PLATFORM_STRING(env, pathname, path) {
136 char canonicalPath[JVM_MAXPATHLEN];
137 if (canonicalize(JVM_NativePath((char *)path),
138 canonicalPath, JVM_MAXPATHLEN) < 0) {
139 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
140 } else {
141 rv = JNU_NewStringPlatform(env, canonicalPath);
142 }
143 } END_PLATFORM_STRING(env, path);
144 return rv;
145}
146
147
148/* -- Attribute accessors -- */
149
150
151static jboolean
152statMode(const char *path, int *mode)
153{
154 if (stat64_ptr) {
155 struct stat64 sb;
156 if (((*stat64_ptr)(path, &sb)) == 0) {
157 *mode = sb.st_mode;
158 return JNI_TRUE;
159 }
160 } else {
161 struct stat sb;
162 if (stat(path, &sb) == 0) {
163 *mode = sb.st_mode;
164 return JNI_TRUE;
165 }
166 }
167 return JNI_FALSE;
168}
169
170
171JNIEXPORT jint JNICALL
172Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
173 jobject file)
174{
175 jint rv = 0;
176
177 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
178 int mode;
179 if (statMode(path, &mode)) {
180 int fmt = mode & S_IFMT;
181 rv = (jint) (java_io_FileSystem_BA_EXISTS
182 | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
183 | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
184 }
185 } END_PLATFORM_STRING(env, path);
186 return rv;
187}
188
189JNIEXPORT jboolean JNICALL
190Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
191 jobject file, jint a)
192{
193 jboolean rv = JNI_FALSE;
194 int mode;
195 switch (a) {
196 case java_io_FileSystem_ACCESS_READ:
197 mode = R_OK;
198 break;
199 case java_io_FileSystem_ACCESS_WRITE:
200 mode = W_OK;
201 break;
202 case java_io_FileSystem_ACCESS_EXECUTE:
203 mode = X_OK;
204 break;
205 default: assert(0);
206 }
207 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
208 if (access(path, mode) == 0) {
209 rv = JNI_TRUE;
210 }
211 } END_PLATFORM_STRING(env, path);
212 return rv;
213}
214
215
216JNIEXPORT jboolean JNICALL
217Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
218 jobject file,
219 jint access,
220 jboolean enable,
221 jboolean owneronly)
222{
223 jboolean rv = JNI_FALSE;
224
225 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
226 int amode, mode;
227 switch (access) {
228 case java_io_FileSystem_ACCESS_READ:
229 if (owneronly)
230 amode = S_IRUSR;
231 else
232 amode = S_IRUSR | S_IRGRP | S_IROTH;
233 break;
234 case java_io_FileSystem_ACCESS_WRITE:
235 if (owneronly)
236 amode = S_IWUSR;
237 else
238 amode = S_IWUSR | S_IWGRP | S_IWOTH;
239 break;
240 case java_io_FileSystem_ACCESS_EXECUTE:
241 if (owneronly)
242 amode = S_IXUSR;
243 else
244 amode = S_IXUSR | S_IXGRP | S_IXOTH;
245 break;
246 default:
247 assert(0);
248 }
249 if (statMode(path, &mode)) {
250 if (enable)
251 mode |= amode;
252 else
253 mode &= ~amode;
254 if (chmod(path, mode) >= 0) {
255 rv = JNI_TRUE;
256 }
257 }
258 } END_PLATFORM_STRING(env, path);
259 return rv;
260}
261
262JNIEXPORT jlong JNICALL
263Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
264 jobject file)
265{
266 jlong rv = 0;
267
268 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
269 if (stat64_ptr) {
270 struct stat64 sb;
271 if (((*stat64_ptr)(path, &sb)) == 0) {
272 rv = 1000 * (jlong)sb.st_mtime;
273 }
274 } else {
275 struct stat sb;
276 if (stat(path, &sb) == 0) {
277 rv = 1000 * (jlong)sb.st_mtime;
278 }
279 }
280 } END_PLATFORM_STRING(env, path);
281 return rv;
282}
283
284
285JNIEXPORT jlong JNICALL
286Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
287 jobject file)
288{
289 jlong rv = 0;
290
291 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
292 if (stat64_ptr) {
293 struct stat64 sb;
294 if (((*stat64_ptr)(path, &sb)) == 0) {
295 rv = sb.st_size;
296 }
297 } else {
298 struct stat sb;
299 if (stat(path, &sb) == 0) {
300 rv = sb.st_size;
301 }
302 }
303 } END_PLATFORM_STRING(env, path);
304 return rv;
305}
306
307
308/* -- File operations -- */
309
310
311JNIEXPORT jboolean JNICALL
312Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
313 jstring pathname)
314{
315 jboolean rv = JNI_FALSE;
316
317 WITH_PLATFORM_STRING(env, pathname, path) {
318 int fd;
319 if (!strcmp (path, "/")) {
320 fd = JVM_EEXIST; /* The root directory always exists */
321 } else {
322 fd = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666);
323 }
324 if (fd < 0) {
325 if (fd != JVM_EEXIST) {
326 JNU_ThrowIOExceptionWithLastError(env, path);
327 }
328 } else {
329 JVM_Close(fd);
330 rv = JNI_TRUE;
331 }
332 } END_PLATFORM_STRING(env, path);
333 return rv;
334}
335
336
337JNIEXPORT jboolean JNICALL
338Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
339 jobject file)
340{
341 jboolean rv = JNI_FALSE;
342
343 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
344 if (remove(path) == 0) {
345 rv = JNI_TRUE;
346 }
347 } END_PLATFORM_STRING(env, path);
348 return rv;
349}
350
351
352JNIEXPORT jobjectArray JNICALL
353Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
354 jobject file)
355{
356 DIR *dir = NULL;
357 struct dirent64 *ptr;
358 struct dirent64 *result;
359 int len, maxlen;
360 jobjectArray rv, old;
361
362 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
363 dir = opendir(path);
364 } END_PLATFORM_STRING(env, path);
365 if (dir == NULL) return NULL;
366
367 ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
368 if (ptr == NULL) {
369 JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
370 closedir(dir);
371 return NULL;
372 }
373
374 /* Allocate an initial String array */
375 len = 0;
376 maxlen = 16;
377 rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
378 if (rv == NULL) goto error;
379
380 /* Scan the directory */
381 while ((readdir64_r(dir, ptr, &result) == 0) && (result != NULL)) {
382 jstring name;
383 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
384 continue;
385 if (len == maxlen) {
386 old = rv;
387 rv = (*env)->NewObjectArray(env, maxlen <<= 1,
388 JNU_ClassString(env), NULL);
389 if (rv == NULL) goto error;
390 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
391 (*env)->DeleteLocalRef(env, old);
392 }
393 name = JNU_NewStringPlatform(env, ptr->d_name);
394 if (name == NULL) goto error;
395 (*env)->SetObjectArrayElement(env, rv, len++, name);
396 (*env)->DeleteLocalRef(env, name);
397 }
398 closedir(dir);
399 free(ptr);
400
401 /* Copy the final results into an appropriately-sized array */
402 old = rv;
403 rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
404 if (rv == NULL) {
405 return NULL;
406 }
407 if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
408 return NULL;
409 }
410 return rv;
411
412 error:
413 closedir(dir);
414 free(ptr);
415 return NULL;
416}
417
418
419JNIEXPORT jboolean JNICALL
420Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
421 jobject file)
422{
423 jboolean rv = JNI_FALSE;
424
425 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
426 if (mkdir(path, 0777) == 0) {
427 rv = JNI_TRUE;
428 }
429 } END_PLATFORM_STRING(env, path);
430 return rv;
431}
432
433
434JNIEXPORT jboolean JNICALL
435Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
436 jobject from, jobject to)
437{
438 jboolean rv = JNI_FALSE;
439
440 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
441 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
442 if (rename(fromPath, toPath) == 0) {
443 rv = JNI_TRUE;
444 }
445 } END_PLATFORM_STRING(env, toPath);
446 } END_PLATFORM_STRING(env, fromPath);
447 return rv;
448}
449
450
451/* Bug in solaris /usr/include/sys/time.h? */
452#ifdef __solaris__
453extern int utimes(const char *, const struct timeval *);
454#elif defined(__linux___)
455extern int utimes(const char *, struct timeval *);
456#endif
457
458
459JNIEXPORT jboolean JNICALL
460Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
461 jobject file, jlong time)
462{
463 jboolean rv = JNI_FALSE;
464
465 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
466 struct timeval tv[2];
467#ifdef __solaris__
468 timestruc_t ts;
469
470 if (stat64_ptr) {
471 struct stat64 sb;
472 if (((*stat64_ptr)(path, &sb)) == 0)
473 ts = sb.st_atim;
474 else
475 goto error;
476 } else {
477 struct stat sb;
478 if (stat(path, &sb) == 0)
479 ts = sb.st_atim;
480 else
481 goto error;
482 }
483#endif
484
485 /* Preserve access time */
486#ifdef __linux__
487 struct stat sb;
488
489 if (stat(path, &sb) == 0) {
490
491 tv[0].tv_sec = sb.st_atime;
492 tv[0].tv_usec = 0;
493 }
494#else
495 tv[0].tv_sec = ts.tv_sec;
496 tv[0].tv_usec = ts.tv_nsec / 1000;
497#endif
498
499 /* Change last-modified time */
500 tv[1].tv_sec = time / 1000;
501 tv[1].tv_usec = (time % 1000) * 1000;
502
503 if (utimes(path, tv) >= 0)
504 rv = JNI_TRUE;
505
506 error: ;
507 } END_PLATFORM_STRING(env, path);
508
509 return rv;
510}
511
512
513JNIEXPORT jboolean JNICALL
514Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
515 jobject file)
516{
517 jboolean rv = JNI_FALSE;
518
519 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
520 int mode;
521 if (statMode(path, &mode)) {
522 if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
523 rv = JNI_TRUE;
524 }
525 }
526 } END_PLATFORM_STRING(env, path);
527 return rv;
528}
529
530JNIEXPORT jlong JNICALL
531Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
532 jobject file, jint t)
533{
534 jlong rv = 0L;
535
536 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
537 struct statvfs fsstat;
538 memset(&fsstat, 0, sizeof(struct statvfs));
539 if (statvfs(path, &fsstat) == 0) {
540 switch(t) {
541 case java_io_FileSystem_SPACE_TOTAL:
542 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
543 long_to_jlong(fsstat.f_blocks));
544 break;
545 case java_io_FileSystem_SPACE_FREE:
546 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
547 long_to_jlong(fsstat.f_bfree));
548 break;
549 case java_io_FileSystem_SPACE_USABLE:
550 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
551 long_to_jlong(fsstat.f_bavail));
552 break;
553 default:
554 assert(0);
555 }
556 }
557 } END_PLATFORM_STRING(env, path);
558 return rv;
559}