1 /* java_io_VMFile.c - Native methods for java.io.File class
2 Copyright (C) 1998, 2004, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 /* do not move; needed here because of some macro definitions */
45 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
46 #include <sys/types.h>
56 #include "java_io_VMFile.h"
58 /*************************************************************************/
61 * Method to create an empty file.
63 * Class: java_io_VMFile
65 * Signature: (Ljava/lang/String;)Z
68 JNIEXPORT jboolean JNICALL
69 Java_java_io_VMFile_create (JNIEnv * env,
70 jclass clazz __attribute__ ((__unused__)),
73 #ifndef WITHOUT_FILESYSTEM
78 filename = JCL_jstring_to_cstring (env, name);
84 result = cpio_openFile (filename, &fd, CPFILE_FLAG_CREATE|CPFILE_FLAG_WRITE, CPFILE_PERMISSION_NORMAL);
85 if (result != CPNATIVE_OK)
88 JCL_ThrowException (env,
89 "java/io/IOException",
90 cpnative_getErrorString (result));
91 JCL_free_cstring (env, name, filename);
96 JCL_free_cstring (env, name, filename);
98 #else /* not WITHOUT_FILESYSTEM */
100 #endif /* not WITHOUT_FILESYSTEM */
103 /*************************************************************************/
106 * This method checks to see if we have read permission on a file.
108 * Class: java_io_VMFile
110 * Signature: (Ljava/lang/String;)Z
113 JNIEXPORT jboolean JNICALL
114 Java_java_io_VMFile_canRead (JNIEnv * env,
115 jobject obj __attribute__ ((__unused__)),
118 #ifndef WITHOUT_FILESYSTEM
119 const char *filename;
123 /* Don't use the JCL convert function because it throws an exception
125 filename = (*env)->GetStringUTFChars (env, name, 0);
126 if (filename == NULL)
131 /* The lazy man's way out. We actually do open the file for reading
132 briefly to verify it can be done */
133 result = cpio_openFile (filename, &fd, CPFILE_FLAG_READ, 0);
134 (*env)->ReleaseStringUTFChars (env, name, filename);
135 if (result != CPNATIVE_OK)
140 #else /* not WITHOUT_FILESYSTEM */
142 #endif /* not WITHOUT_FILESYSTEM */
145 /*************************************************************************/
148 * This method checks to see if we have write permission on a file.
150 * Class: java_io_VMFile
152 * Signature: (Ljava/lang/String;)Z
155 JNIEXPORT jboolean JNICALL
156 Java_java_io_VMFile_canWrite (JNIEnv * env,
157 jobject obj __attribute__ ((__unused__)),
160 #ifndef WITHOUT_FILESYSTEM
161 const char *filename;
165 /* Don't use the JCL convert function because it throws an exception
167 filename = (*env)->GetStringUTFChars (env, name, 0);
168 if (filename == NULL)
173 /* The lazy man's way out. We actually do open the file for writing
174 briefly to verify it can be done */
175 result = cpio_openFile (filename, &fd, CPFILE_FLAG_READWRITE, 0);
176 (*env)->ReleaseStringUTFChars (env, name, filename);
177 if (result != CPNATIVE_OK)
184 #else /* not WITHOUT_FILESYSTEM */
186 #endif /* not WITHOUT_FILESYSTEM */
189 /*************************************************************************/
192 * This method makes a file read only.
194 * Class: java_io_VMFile
195 * Method: setReadOnly
196 * Signature: (Ljava/lang/String;)Z
199 JNIEXPORT jboolean JNICALL
200 Java_java_io_VMFile_setReadOnly (JNIEnv * env,
201 jobject obj __attribute__ ((__unused__)),
204 #ifndef WITHOUT_FILESYSTEM
205 const char *filename;
208 /* Don't use the JCL convert function because it throws an exception
210 filename = (*env)->GetStringUTFChars (env, name, 0);
211 if (filename == NULL)
216 result = cpio_setFileReadonly (filename);
217 (*env)->ReleaseStringUTFChars (env, name, filename);
219 return result == CPNATIVE_OK ? 1 : 0;
220 #else /* not WITHOUT_FILESYSTEM */
222 #endif /* not WITHOUT_FILESYSTEM */
225 /*************************************************************************/
228 * This method checks to see if a file exists.
230 * Class: java_io_VMFile
232 * Signature: (Ljava/lang/String;)Z
235 JNIEXPORT jboolean JNICALL
236 Java_java_io_VMFile_exists (JNIEnv * env,
237 jobject obj __attribute__ ((__unused__)),
240 #ifndef WITHOUT_FILESYSTEM
241 const char *filename;
244 /* Don't use the JCL convert function because it throws an exception
246 filename = (*env)->GetStringUTFChars (env, name, 0);
247 if (filename == NULL)
252 result = cpio_isFileExists (filename);
253 (*env)->ReleaseStringUTFChars (env, name, filename);
255 return result == CPNATIVE_OK ? 1 : 0;
256 #else /* not WITHOUT_FILESYSTEM */
258 #endif /* not WITHOUT_FILESYSTEM */
261 /*************************************************************************/
264 * This method checks to see if a file is a "plain" file; that is, not
265 * a directory, pipe, etc.
267 * Class: java_io_VMFile
269 * Signature: (Ljava/lang/String;)Z
272 JNIEXPORT jboolean JNICALL
273 Java_java_io_VMFile_isFile (JNIEnv * env,
274 jobject obj __attribute__ ((__unused__)),
277 #ifndef WITHOUT_FILESYSTEM
278 const char *filename;
282 /* Don't use the JCL convert function because it throws an exception
284 filename = (*env)->GetStringUTFChars (env, name, 0);
285 if (filename == NULL)
290 result = cpio_checkType (filename, &entryType);
291 (*env)->ReleaseStringUTFChars (env, name, filename);
293 return result == CPNATIVE_OK && entryType == CPFILE_FILE ? 1 : 0;
294 #else /* not WITHOUT_FILESYSTEM */
296 #endif /* not WITHOUT_FILESYSTEM */
299 /*************************************************************************/
302 * This method checks to see if a file is a directory or not.
304 * Class: java_io_VMFile
305 * Method: isDirectory
306 * Signature: (Ljava/lang/String;)Z
309 JNIEXPORT jboolean JNICALL
310 Java_java_io_VMFile_isDirectory (JNIEnv * env,
311 jobject obj __attribute__ ((__unused__)),
314 #ifndef WITHOUT_FILESYSTEM
315 const char *filename;
319 /* Don't use the JCL convert function because it throws an exception
321 filename = (*env)->GetStringUTFChars (env, name, 0);
322 if (filename == NULL)
327 result = cpio_checkType (filename, &entryType);
328 (*env)->ReleaseStringUTFChars (env, name, filename);
330 return result == CPNATIVE_OK && entryType == CPFILE_DIRECTORY ? 1 : 0;
331 #else /* not WITHOUT_FILESYSTEM */
333 #endif /* not WITHOUT_FILESYSTEM */
336 /*************************************************************************/
339 * This method returns the length of the file.
341 * Class: java_io_VMFile
343 * Signature: (Ljava/lang/String;)J
346 JNIEXPORT jlong JNICALL
347 Java_java_io_VMFile_length (JNIEnv * env,
348 jobject obj __attribute__ ((__unused__)),
351 #ifndef WITHOUT_FILESYSTEM
352 const char *filename;
357 /* Don't use the JCL convert function because it throws an exception
359 filename = (*env)->GetStringUTFChars (env, name, 0);
360 if (filename == NULL)
363 /* open file for reading, get size and close file */
364 result = cpio_openFile (filename, &tmpfd, CPFILE_FLAG_READ, 0);
365 if (result != CPNATIVE_OK)
368 result = cpio_getFileSize (tmpfd, &length);
369 if (result != CPNATIVE_OK)
371 cpio_closeFile (tmpfd);
375 result = cpio_closeFile (tmpfd);
376 (*env)->ReleaseStringUTFChars (env, name, filename);
378 return result == CPNATIVE_OK ? length : 0;
379 #else /* not WITHOUT_FILESYSTEM */
381 #endif /* not WITHOUT_FILESYSTEM */
384 /*************************************************************************/
387 * This method returns the modification date of the file.
389 * Class: java_io_VMFile
390 * Method: lastModified
391 * Signature: (Ljava/lang/String;)J
394 JNIEXPORT jlong JNICALL
395 Java_java_io_VMFile_lastModified (JNIEnv * env,
396 jobject obj __attribute__ ((__unused__)),
399 #ifndef WITHOUT_FILESYSTEM
400 const char *filename;
404 /* Don't use the JCL convert function because it throws an exception
406 filename = (*env)->GetStringUTFChars (env, name, 0);
407 if (filename == NULL)
412 result = cpio_getModificationTime (filename, &mtime);
413 (*env)->ReleaseStringUTFChars (env, name, filename);
415 return result == CPNATIVE_OK ? mtime : 0;
416 #else /* not WITHOUT_FILESYSTEM */
418 #endif /* not WITHOUT_FILESYSTEM */
421 /*************************************************************************/
424 * This method sets the modification date of the file.
426 * Class: java_io_VMFile
427 * Method: setLastModified
428 * Signature: (Ljava/lang/String;J)Z
431 JNIEXPORT jboolean JNICALL
432 Java_java_io_VMFile_setLastModified (JNIEnv * env,
433 jobject obj __attribute__ ((__unused__)),
434 jstring name, jlong newtime)
436 #ifndef WITHOUT_FILESYSTEM
437 const char *filename;
440 /* Don't use the JCL convert function because it throws an exception
442 filename = (*env)->GetStringUTFChars (env, name, 0);
443 if (filename == NULL)
448 result = cpio_setModificationTime (filename, newtime);
449 (*env)->ReleaseStringUTFChars (env, name, filename);
451 return result == CPNATIVE_OK ? 1 : 0;
452 #else /* not WITHOUT_FILESYSTEM */
454 #endif /* not WITHOUT_FILESYSTEM */
457 /*************************************************************************/
460 * This method deletes a file (actually a name for a file - additional
461 * links could exist).
463 * Class: java_io_VMFile
465 * Signature: (Ljava/lang/String;)Z
468 JNIEXPORT jboolean JNICALL
469 Java_java_io_VMFile_delete (JNIEnv * env,
470 jobject obj __attribute__ ((__unused__)),
473 #ifndef WITHOUT_FILESYSTEM
474 const char *filename;
477 /* Don't use the JCL convert function because it throws an exception
479 filename = (*env)->GetStringUTFChars (env, name, 0);
480 if (filename == NULL)
485 result = cpio_removeFile (filename);
486 (*env)->ReleaseStringUTFChars (env, name, filename);
488 return result == CPNATIVE_OK ? 1 : 0;
489 #else /* not WITHOUT_FILESYSTEM */
491 #endif /* not WITHOUT_FILESYSTEM */
494 /*************************************************************************/
497 * This method creates a directory.
499 * Class: java_io_VMFile
501 * Signature: (Ljava/lang/String;)Z
504 JNIEXPORT jboolean JNICALL
505 Java_java_io_VMFile_mkdir (JNIEnv * env,
506 jobject obj __attribute__ ((__unused__)),
509 #ifndef WITHOUT_FILESYSTEM
510 const char *pathname;
513 /* Don't use the JCL convert function because it throws an exception
515 pathname = (*env)->GetStringUTFChars (env, name, 0);
516 if (pathname == NULL)
521 result = cpio_mkdir (pathname);
522 (*env)->ReleaseStringUTFChars (env, name, pathname);
524 return (result == CPNATIVE_OK) ? 1 : 0;
525 #else /* not WITHOUT_FILESYSTEM */
527 #endif /* not WITHOUT_FILESYSTEM */
530 /*************************************************************************/
533 * This method renames a (link to a) file.
535 * Class: java_io_VMFile
537 * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
540 JNIEXPORT jboolean JNICALL
541 Java_java_io_VMFile_renameTo (JNIEnv * env,
542 jobject obj __attribute__ ((__unused__)),
543 jstring t, jstring d)
545 #ifndef WITHOUT_FILESYSTEM
546 const char *old_filename, *new_filename;
549 /* Don't use the JCL convert function because it throws an exception
551 old_filename = (*env)->GetStringUTFChars (env, t, 0);
552 if (old_filename == NULL)
557 new_filename = (*env)->GetStringUTFChars (env, d, 0);
558 if (new_filename == NULL)
560 (*env)->ReleaseStringUTFChars (env, t, old_filename);
564 result = cpio_rename (old_filename, new_filename);
565 (*env)->ReleaseStringUTFChars (env, d, new_filename);
566 (*env)->ReleaseStringUTFChars (env, t, old_filename);
568 return (result == CPNATIVE_OK) ? 1 : 0;
569 #else /* not WITHOUT_FILESYSTEM */
571 #endif /* not WITHOUT_FILESYSTEM */
574 /*************************************************************************/
577 * This method returns an array of String representing all the files
578 * in a directory except "." and "..".
580 * Class: java_io_VMFile
582 * Signature: (Ljava/lang/String;)[Ljava/lang/String;
585 JNIEXPORT jobjectArray JNICALL
586 Java_java_io_VMFile_list (JNIEnv * env, jobject obj
587 __attribute__ ((__unused__)), jstring name)
589 #ifndef WITHOUT_FILESYSTEM
590 const int REALLOC_SIZE = 10;
596 char *filename = (char *) JCL_malloc (env, FILENAME_MAX);
597 unsigned long int filelist_count, max_filelist_count;
600 jobjectArray filearray;
604 /* Don't use the JCL convert function because it throws an exception
606 dirname = (*env)->GetStringUTFChars (env, name, 0);
612 /* open directory for reading */
613 result = cpio_openDir (dirname, &handle);
615 (*env)->ReleaseStringUTFChars (env, name, dirname);
617 if (result != CPNATIVE_OK)
622 /* allocate filelist */
623 filelist = (char **) JCL_malloc (env, sizeof (char *) * REALLOC_SIZE);
624 if (filelist == NULL)
626 result = cpio_closeDir (handle);
630 max_filelist_count = REALLOC_SIZE;
632 /* read the files from the directory */
633 result = cpio_readDir (handle, filename);
634 while (result == CPNATIVE_OK)
636 if ((strcmp (filename, ".") != 0) && (strcmp (filename, "..") != 0))
638 /* allocate more memory if necessary */
639 if (filelist_count >= max_filelist_count)
641 tmp_filelist = (char **) JCL_realloc (env,
643 (max_filelist_count +
646 if (tmp_filelist == NULL)
648 for (i = 0; i < filelist_count; i++)
650 JCL_free (env, filelist[i]);
652 JCL_free (env, filelist);
653 result = cpio_closeDir (handle);
656 filelist = tmp_filelist;
657 max_filelist_count += REALLOC_SIZE;
660 /* save entry in list (avoid strdup, because it is not ANSI C, thus difficult to port) */
661 filelist[filelist_count] =
662 (char *) JCL_malloc (env, strlen (filename) + 1);
663 assert (filelist[filelist_count] != NULL);
664 strcpy (filelist[filelist_count], filename);
668 /* read next directory entry */
669 result = cpio_readDir (handle, filename);
672 JCL_free (env, filename);
674 /* close directory */
675 result = cpio_closeDir (handle);
677 /* put the list of files into a Java String array and return it */
678 str_clazz = (*env)->FindClass (env, "java/lang/String");
679 if (str_clazz == NULL)
681 for (i = 0; i < filelist_count; i++)
683 JCL_free (env, filelist[i]);
685 JCL_free (env, filelist);
688 filearray = (*env)->NewObjectArray (env, filelist_count, str_clazz, 0);
689 if (filearray == NULL)
691 for (i = 0; i < filelist_count; i++)
693 JCL_free (env, filelist[i]);
695 JCL_free (env, filelist);
699 (*env)->DeleteLocalRef (env, str_clazz);
701 for (i = 0; i < filelist_count; i++)
703 /* create new string */
704 str = (*env)->NewStringUTF (env, filelist[i]);
707 /* We don't clean up everything here, but if this failed,
708 something serious happened anyway */
709 for (i = 0; i < filelist_count; i++)
711 JCL_free (env, filelist[i]);
713 JCL_free (env, filelist);
717 /* save into array */
718 (*env)->SetObjectArrayElement (env, filearray, i, str);
720 /* delete local reference */
721 (*env)->DeleteLocalRef (env, str);
725 for (i = 0; i < filelist_count; i++)
727 JCL_free (env, filelist[i]);
729 JCL_free (env, filelist);
732 #else /* not WITHOUT_FILESYSTEM */
734 #endif /* not WITHOUT_FILESYSTEM */
737 /*************************************************************************/
740 * These two methods are used to maintain dynamically allocated
741 * buffers for getCanonicalPath without the overhead of calling
742 * realloc every time a buffer is modified. Buffers are sized
743 * at the smallest multiple of CHUNKSIZ that is greater than or
744 * equal to the desired length. The default CHUNKSIZ is 256,
745 * longer than most paths, so in most cases a getCanonicalPath
746 * will require only one malloc per buffer.
750 #define CHUNKSIZ (1 << CHUNKLOG)
753 nextChunkSize (int size)
755 return ((size >> CHUNKLOG) + ((size & (CHUNKSIZ - 1)) ? 1 : 0)) << CHUNKLOG;
759 maybeGrowBuf (JNIEnv *env, char *buf, int *size, int required)
761 if (required > *size)
763 *size = nextChunkSize (required);
764 buf = JCL_realloc (env, buf, *size);
769 /*************************************************************************/
772 * This method converts a path to canonical form on GNU/Posix systems.
773 * This involves the removal of redundant separators, references to
774 * "." and "..", and symbolic links.
776 * The conversion proceeds on a component-by-component basis: symbolic
777 * links and references to ".." are resolved as and when they occur.
778 * This means that if "/foo/bar" is a symbolic link to "/baz" then the
779 * canonical form of "/foo/bar/.." is "/" and not "/foo".
781 * In order to mimic the behaviour of proprietary JVMs, non-existant
782 * path components are allowed (a departure from the normal GNU system
783 * convention). This means that if "/foo/bar" is a symbolic link to
784 * "/baz", the canonical form of "/non-existant-directory/../foo/bar"
787 * Class: java_io_VMFile
788 * Method: toCanonicalForm
789 * Signature: (Ljava/lang/String)Ljava/lang/String
792 JNIEXPORT jstring JNICALL
793 Java_java_io_VMFile_toCanonicalForm (JNIEnv *env,
794 jclass class __attribute__ ((__unused__)),
797 #ifndef WITHOUT_FILESYSTEM
804 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
806 #endif /* HAVE_LSTAT && HAVE_READLINK */
808 path = JCL_jstring_to_cstring (env, jpath);
812 /* It is the caller's responsibility to ensure the path is absolute. */
813 if (path[0] == 0 || path[0] != '/')
815 JCL_free_cstring (env, jpath, path);
816 JCL_ThrowException (env, "java/lang/RuntimeException", "Not absolute");
821 srcl = nextChunkSize (len + 1);
822 src = JCL_malloc (env, srcl);
825 JCL_free_cstring (env, jpath, path);
829 JCL_free_cstring (env, jpath, path);
832 dstl = nextChunkSize (2);
833 dst = JCL_malloc (env, dstl);
844 while (src[srci] != '\0')
849 while (src[srci] == '/')
852 /* Find next slash. */
853 while (src[srci] != '/' && src[srci] != '\0')
856 /* We hit the end. */
860 /* Handle "." and "..". */
861 if (len == 1 && src[tmpi] == '.')
863 if (len == 2 && src[tmpi] == '.' && src[tmpi + 1] == '.')
865 while (dsti > 1 && dst[dsti - 1] != '/')
869 /* Reenable filesystem checking if disabled, as we might
870 * have reversed over whatever caused the problem before.
871 * At least one proprietary JVM has inconsistencies because
872 * it does not do this.
878 /* Handle real path components. */
879 dst = maybeGrowBuf (env,
880 dst, &dstl, dsti + (dsti > 1 ? 1 : 0) + len + 1);
889 strncpy (&dst[dsti], &src[tmpi], len);
891 if (fschecks == JNI_FALSE)
894 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
896 if (lstat (dst, &sb) == 0)
898 if (S_ISLNK (sb.st_mode))
901 char *tmp = JCL_malloc (env, tmpl);
911 tmpi = readlink (dst, tmp, tmpl);
917 JCL_ThrowException (env, "java/io/IOException",
924 tmp = JCL_realloc (env, tmp, tmpl);
927 /* Prepend the link's path to src. */
928 tmp = maybeGrowBuf (env,
929 tmp, &tmpl, tmpi + strlen (&src[srci]) + 1);
937 strcpy (&tmp[tmpi], &src[srci]);
943 /* Either replace or append dst depending on whether the
944 * link is relative or absolute.
946 dsti = src[0] == '/' ? 1 : dsti_save;
951 /* Something doesn't exist, or we don't have permission to
952 * read it, or a previous path component is a directory, or
953 * a symlink is looped. Whatever, we can't check the
954 * filesystem any more.
956 fschecks = JNI_FALSE;
958 #endif /* HAVE_LSTAT && HAVE_READLINK */
962 jpath = (*env)->NewStringUTF (env, dst);
966 #else /* not WITHOUT_FILESYSTEM */
968 #endif /* not WITHOUT_FILESYSTEM */