1 /* java_io_VMFile.c - Native methods for java.io.File class
2    Copyright (C) 1998, 2004, 2006 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
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)
9 any later version.
10 
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.
15 
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
19 02110-1301 USA.
20 
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
24 combination.
25 
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. */
37 
38 /* do not move; needed here because of some macro definitions */
39 #include <config.h>
40 
41 #include <assert.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 
45 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #endif
50 
51 #include <jni.h>
52 #include <jcl.h>
53 #include "cpio.h"
54 #include "cpnative.h"
55 
56 #include "java_io_VMFile.h"
57 
58 /* ***** PRIVATE FUNCTIONS DELCARATION ***** */
59 
60 /**
61  * Enables of disables the passed permission bit of a file.
62  */
63 static jboolean set_file_permissions (JNIEnv *env, jstring name,
64                                       jboolean enable,
65                                       jboolean ownerOnly,
66                                       int permissions);
67 
68 /* ***** END: PRIVATE FUNCTIONS DELCARATION ***** */
69 
70 /*************************************************************************/
71 
72 /*
73  * Method to create an empty file.
74  *
75  * Class:     java_io_VMFile
76  * Method:    create
77  * Signature: (Ljava/lang/String;)Z
78  */
79 
80 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_create(JNIEnv * env,jclass clazz,jstring name)81 Java_java_io_VMFile_create (JNIEnv * env,
82 			    jclass clazz __attribute__ ((__unused__)),
83 			    jstring name)
84 {
85 #ifndef WITHOUT_FILESYSTEM
86   const char *filename;
87   int fd;
88   int result;
89 
90   filename = JCL_jstring_to_cstring (env, name);
91   if (filename == NULL)
92     {
93       return 0;
94     }
95 
96   result = cpio_openFile (filename, &fd, CPFILE_FLAG_CREATE|CPFILE_FLAG_WRITE, CPFILE_PERMISSION_NORMAL);
97   if (result != CPNATIVE_OK)
98     {
99       if (result != EEXIST)
100         JCL_ThrowException (env,
101 			    "java/io/IOException",
102 			    cpnative_getErrorString (result));
103       JCL_free_cstring (env, name, filename);
104       return 0;
105     }
106   cpio_closeFile (fd);
107 
108   JCL_free_cstring (env, name, filename);
109   return 1;
110 #else /* not WITHOUT_FILESYSTEM */
111   return 0;
112 #endif /* not WITHOUT_FILESYSTEM */
113 }
114 
115 /*************************************************************************/
116 
117 /*
118  * This method checks to see if we have read permission on a file.
119  *
120  * Class:     java_io_VMFile
121  * Method:    canRead
122  * Signature: (Ljava/lang/String;)Z
123  */
124 
125 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_canRead(JNIEnv * env,jclass clazz,jstring name)126 Java_java_io_VMFile_canRead (JNIEnv * env,
127                              jclass clazz __attribute__ ((__unused__)),
128                              jstring name)
129 {
130 #ifndef WITHOUT_FILESYSTEM
131   const char *filename;
132   int result;
133 
134   /* Don't use the JCL convert function because it throws an exception
135      on failure */
136   filename = (*env)->GetStringUTFChars (env, name, 0);
137   if (filename == NULL)
138     {
139       return JNI_FALSE;
140     }
141 
142   result = cpio_checkAccess (filename, CPFILE_FLAG_READ);
143 
144   (*env)->ReleaseStringUTFChars (env, name, filename);
145   if (result != CPNATIVE_OK)
146     return JNI_FALSE;
147 
148   return JNI_TRUE;
149 #else /* not WITHOUT_FILESYSTEM */
150   return JNI_FALSE;
151 #endif /* not WITHOUT_FILESYSTEM */
152 }
153 
154 /*************************************************************************/
155 
156 /*
157  * This method checks to see if we have write permission on a file.
158  *
159  * Class:     java_io_VMFile
160  * Method:    canWrite
161  * Signature: (Ljava/lang/String;)Z
162  */
163 
164 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_canWrite(JNIEnv * env,jclass clazz,jstring name)165 Java_java_io_VMFile_canWrite (JNIEnv * env,
166                               jclass clazz __attribute__ ((__unused__)),
167                               jstring name)
168 {
169 #ifndef WITHOUT_FILESYSTEM
170   const char *filename;
171   int result;
172 
173   /* Don't use the JCL convert function because it throws an exception
174      on failure */
175   filename = (*env)->GetStringUTFChars (env, name, 0);
176   if (filename == NULL)
177     {
178       return JNI_FALSE;
179     }
180 
181   result = cpio_checkAccess (filename, CPFILE_FLAG_WRITE);
182 
183   (*env)->ReleaseStringUTFChars (env, name, filename);
184   if (result != CPNATIVE_OK)
185     {
186       return JNI_FALSE;
187     }
188 
189   return JNI_TRUE;
190 #else /* not WITHOUT_FILESYSTEM */
191   return JNI_FALSE;
192 #endif /* not WITHOUT_FILESYSTEM */
193 }
194 
195 /*************************************************************************/
196 
197 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_canWriteDirectory(JNIEnv * env,jclass clazz,jstring path)198 Java_java_io_VMFile_canWriteDirectory (JNIEnv *env, jclass clazz, jstring path)
199 {
200   /* this is only valid on *nix systems  */
201   return Java_java_io_VMFile_canWrite(env, clazz, path);
202 }
203 
204 /*************************************************************************/
205 
206 /*
207  * This method checks to see if we have execute permission on a file.
208  *
209  * Class:     java_io_VMFile
210  * Method:    canExecute
211  * Signature: (Ljava/lang/String;)Z
212  */
213 
214 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_canExecute(JNIEnv * env,jclass clazz,jstring name)215 Java_java_io_VMFile_canExecute (JNIEnv * env,
216                                 jclass clazz __attribute__ ((__unused__)),
217                                 jstring name)
218 {
219 #ifndef WITHOUT_FILESYSTEM
220   const char *filename;
221   int result;
222 
223   /* Don't use the JCL convert function because it throws an exception
224      on failure */
225   filename = (*env)->GetStringUTFChars (env, name, 0);
226   if (filename == NULL)
227     {
228       return JNI_FALSE;
229     }
230 
231   result = cpio_checkAccess (filename, CPFILE_FLAG_EXEC);
232 
233   (*env)->ReleaseStringUTFChars (env, name, filename);
234   if (result != CPNATIVE_OK)
235     return JNI_FALSE;
236 
237   return JNI_TRUE;
238 #else /* not WITHOUT_FILESYSTEM */
239   return JNI_FALSE;
240 #endif /* not WITHOUT_FILESYSTEM */
241 }
242 
243 
244 /*************************************************************************/
245 
246 /*
247  * This method makes a file read only.
248  *
249  * Class:     java_io_VMFile
250  * Method:    setReadOnly
251  * Signature: (Ljava/lang/String;)Z
252  */
253 
254 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_setReadOnly(JNIEnv * env,jclass clazz,jstring name)255 Java_java_io_VMFile_setReadOnly (JNIEnv * env,
256                                  jclass clazz __attribute__ ((__unused__)),
257                                  jstring name)
258 {
259 #ifndef WITHOUT_FILESYSTEM
260   const char *filename;
261   int result;
262 
263   /* Don't use the JCL convert function because it throws an exception
264      on failure */
265   filename = (*env)->GetStringUTFChars (env, name, 0);
266   if (filename == NULL)
267     {
268       return 0;
269     }
270 
271   result = cpio_setFileReadonly (filename);
272   (*env)->ReleaseStringUTFChars (env, name, filename);
273 
274   return result == CPNATIVE_OK ? 1 : 0;
275 #else /* not WITHOUT_FILESYSTEM */
276   return 0;
277 #endif /* not WITHOUT_FILESYSTEM */
278 }
279 
280 /*************************************************************************/
281 
282 /*
283  * This method changes the read permission bit of a file.
284  *
285  * Class:     java_io_VMFile
286  * Method:    setReadable
287  * Signature: (Ljava/lang/String;ZZ)Z
288  */
289 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_setReadable(JNIEnv * env,jclass clazz,jstring name,jboolean readable,jboolean ownerOnly)290 Java_java_io_VMFile_setReadable (JNIEnv *env,
291                                  jclass clazz __attribute__ ((__unused__)),
292                                  jstring name,
293                                  jboolean readable,
294                                  jboolean ownerOnly)
295 {
296   return set_file_permissions (env, name, readable, ownerOnly,
297                                CPFILE_FLAG_READ);
298 }
299 
300 
301 /*************************************************************************/
302 
303 /*
304  * This method changes the write permission bit of a file.
305  *
306  * Class:     java_io_VMFile
307  * Method:    setWritable
308  * Signature: (Ljava/lang/String;ZZ)Z
309  */
310 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_setWritable(JNIEnv * env,jclass clazz,jstring name,jboolean writable,jboolean ownerOnly)311 Java_java_io_VMFile_setWritable (JNIEnv *env,
312                                  jclass clazz __attribute__ ((__unused__)),
313                                  jstring name,
314                                  jboolean writable,
315                                  jboolean ownerOnly)
316 {
317   return set_file_permissions (env, name, writable, ownerOnly,
318                                CPFILE_FLAG_WRITE);
319 }
320 
321 /*************************************************************************/
322 
323 /*
324  * This method changes the execute permission bit of a file.
325  *
326  * Class:     java_io_VMFile
327  * Method:    setExecutable
328  * Signature: (Ljava/lang/String;ZZ)Z
329  */
330 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_setExecutable(JNIEnv * env,jclass clazz,jstring name,jboolean executable,jboolean ownerOnly)331 Java_java_io_VMFile_setExecutable (JNIEnv *env,
332                                    jclass clazz __attribute__ ((__unused__)),
333                                    jstring name,
334                                    jboolean executable,
335                                    jboolean ownerOnly)
336 {
337   return set_file_permissions (env, name, executable, ownerOnly,
338                                CPFILE_FLAG_EXEC);
339 }
340 
341 /*************************************************************************/
342 
343 JNIEXPORT jlong JNICALL
Java_java_io_VMFile_getTotalSpace(JNIEnv * env,jclass clazz,jstring path)344 Java_java_io_VMFile_getTotalSpace (JNIEnv *env,
345                                    jclass clazz __attribute__ ((__unused__)),
346                                    jstring path)
347 {
348 #ifndef WITHOUT_FILESYSTEM
349 
350   jlong result;
351   const char *_path = NULL;
352 
353   _path = (*env)->GetStringUTFChars (env, path, 0);
354   if (_path == NULL)
355     {
356       return 0L;
357     }
358 
359   result = cpio_df (_path, TOTAL);
360 
361   (*env)->ReleaseStringUTFChars (env, path, _path);
362 
363   return result;
364 
365 #else /* not WITHOUT_FILESYSTEM */
366   return 0L;
367 #endif /* not WITHOUT_FILESYSTEM */
368 }
369 
370 /*************************************************************************/
371 
372 JNIEXPORT jlong JNICALL
Java_java_io_VMFile_getFreeSpace(JNIEnv * env,jclass clazz,jstring path)373 Java_java_io_VMFile_getFreeSpace (JNIEnv *env,
374                                   jclass clazz __attribute__ ((__unused__)),
375                                   jstring path)
376 {
377 #ifndef WITHOUT_FILESYSTEM
378 
379   jlong result;
380   const char *_path = NULL;
381 
382   _path = (*env)->GetStringUTFChars (env, path, 0);
383   if (_path == NULL)
384     {
385       return 0L;
386     }
387 
388   result = cpio_df (_path, FREE);
389 
390   (*env)->ReleaseStringUTFChars (env, path, _path);
391 
392   return result;
393 
394 #else /* not WITHOUT_FILESYSTEM */
395   return 0L;
396 #endif /* not WITHOUT_FILESYSTEM */
397 }
398 
399 /*************************************************************************/
400 
401 JNIEXPORT jlong JNICALL
Java_java_io_VMFile_getUsableSpace(JNIEnv * env,jclass clazz,jstring path)402 Java_java_io_VMFile_getUsableSpace (JNIEnv *env,
403                                     jclass clazz __attribute__ ((__unused__)),
404                                     jstring path)
405 {
406 #ifndef WITHOUT_FILESYSTEM
407 
408   jlong result;
409   const char *_path = NULL;
410 
411   _path = (*env)->GetStringUTFChars (env, path, 0);
412   if (_path == NULL)
413     {
414       return 0L;
415     }
416 
417   result = cpio_df (_path, USABLE);
418 
419   (*env)->ReleaseStringUTFChars (env, path, _path);
420 
421   return result;
422 
423 #else /* not WITHOUT_FILESYSTEM */
424   return 0L;
425 #endif /* not WITHOUT_FILESYSTEM */
426 }
427 
428 /*************************************************************************/
429 
430 /*
431  * This method checks to see if a file exists.
432  *
433  * Class:     java_io_VMFile
434  * Method:    exists
435  * Signature: (Ljava/lang/String;)Z
436  */
437 
438 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_exists(JNIEnv * env,jclass clazz,jstring name)439 Java_java_io_VMFile_exists (JNIEnv * env,
440                             jclass clazz __attribute__ ((__unused__)),
441 			                jstring name)
442 {
443 #ifndef WITHOUT_FILESYSTEM
444   const char *filename;
445   int result;
446 
447   /* Don't use the JCL convert function because it throws an exception
448      on failure */
449   filename = (*env)->GetStringUTFChars (env, name, 0);
450   if (filename == NULL)
451     {
452       return 0;
453     }
454 
455   result = cpio_isFileExists (filename);
456   (*env)->ReleaseStringUTFChars (env, name, filename);
457 
458   return result == CPNATIVE_OK ? 1 : 0;
459 #else /* not WITHOUT_FILESYSTEM */
460   return 0;
461 #endif /* not WITHOUT_FILESYSTEM */
462 }
463 
464 /*************************************************************************/
465 
466 /*
467  * This method checks to see if a file is a "plain" file; that is, not
468  * a directory, pipe, etc.
469  *
470  * Class:     java_io_VMFile
471  * Method:    isFile
472  * Signature: (Ljava/lang/String;)Z
473  */
474 
475 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_isFile(JNIEnv * env,jclass clazz,jstring name)476 Java_java_io_VMFile_isFile (JNIEnv * env,
477                             jclass clazz __attribute__ ((__unused__)),
478                             jstring name)
479 {
480 #ifndef WITHOUT_FILESYSTEM
481   const char *filename;
482   int result;
483   jint entryType;
484 
485   /* Don't use the JCL convert function because it throws an exception
486      on failure */
487   filename = (*env)->GetStringUTFChars (env, name, 0);
488   if (filename == NULL)
489     {
490       return 0;
491     }
492 
493   result = cpio_checkType (filename, &entryType);
494   (*env)->ReleaseStringUTFChars (env, name, filename);
495 
496   return result == CPNATIVE_OK && entryType == CPFILE_FILE ? 1 : 0;
497 #else /* not WITHOUT_FILESYSTEM */
498   return 0;
499 #endif /* not WITHOUT_FILESYSTEM */
500 }
501 
502 /*************************************************************************/
503 
504 /*
505  * This method checks to see if a file is a directory or not.
506  *
507  * Class:     java_io_VMFile
508  * Method:    isDirectory
509  * Signature: (Ljava/lang/String;)Z
510  */
511 
512 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_isDirectory(JNIEnv * env,jclass clazz,jstring name)513 Java_java_io_VMFile_isDirectory (JNIEnv * env,
514                                  jclass clazz __attribute__ ((__unused__)),
515                                  jstring name)
516 {
517 #ifndef WITHOUT_FILESYSTEM
518   const char *filename;
519   int result;
520   jint entryType;
521 
522   /* Don't use the JCL convert function because it throws an exception
523      on failure */
524   filename = (*env)->GetStringUTFChars (env, name, 0);
525   if (filename == NULL)
526     {
527       return 0;
528     }
529 
530   result = cpio_checkType (filename, &entryType);
531   (*env)->ReleaseStringUTFChars (env, name, filename);
532 
533   return result == CPNATIVE_OK && entryType == CPFILE_DIRECTORY ? 1 : 0;
534 #else /* not WITHOUT_FILESYSTEM */
535   return 0;
536 #endif /* not WITHOUT_FILESYSTEM */
537 }
538 
539 /*************************************************************************/
540 
541 /*
542  * This method returns the length of the file.
543  *
544  * Class:     java_io_VMFile
545  * Method:    length
546  * Signature: (Ljava/lang/String;)J
547  */
548 
549 JNIEXPORT jlong JNICALL
Java_java_io_VMFile_length(JNIEnv * env,jclass clazz,jstring name)550 Java_java_io_VMFile_length (JNIEnv * env,
551                             jclass clazz __attribute__ ((__unused__)),
552                             jstring name)
553 {
554 #ifndef WITHOUT_FILESYSTEM
555   const char *filename;
556   int tmpfd;
557   jlong length;
558   int result;
559 
560   /* Don't use the JCL convert function because it throws an exception
561      on failure */
562   filename = (*env)->GetStringUTFChars (env, name, 0);
563   if (filename == NULL)
564     return 0;
565 
566   /* open file for reading, get size and close file */
567   result = cpio_openFile (filename, &tmpfd, CPFILE_FLAG_READ, 0);
568   if (result != CPNATIVE_OK)
569     return 0;
570 
571   result = cpio_getFileSize (tmpfd, &length);
572   if (result != CPNATIVE_OK)
573     {
574       cpio_closeFile (tmpfd);
575       return 0;
576     }
577 
578   result = cpio_closeFile (tmpfd);
579   (*env)->ReleaseStringUTFChars (env, name, filename);
580 
581   return result == CPNATIVE_OK ? length : 0;
582 #else /* not WITHOUT_FILESYSTEM */
583   return 0;
584 #endif /* not WITHOUT_FILESYSTEM */
585 }
586 
587 /*************************************************************************/
588 
589 /*
590  * This method returns the modification date of the file.
591  *
592  * Class:     java_io_VMFile
593  * Method:    lastModified
594  * Signature: (Ljava/lang/String;)J
595  */
596 
597 JNIEXPORT jlong JNICALL
Java_java_io_VMFile_lastModified(JNIEnv * env,jclass clazz,jstring name)598 Java_java_io_VMFile_lastModified (JNIEnv * env,
599                                   jclass clazz __attribute__ ((__unused__)),
600                                   jstring name)
601 {
602 #ifndef WITHOUT_FILESYSTEM
603   const char *filename;
604   jlong mtime;
605   int result;
606 
607   /* Don't use the JCL convert function because it throws an exception
608      on failure */
609   filename = (*env)->GetStringUTFChars (env, name, 0);
610   if (filename == NULL)
611     {
612       return 0;
613     }
614 
615   result = cpio_getModificationTime (filename, &mtime);
616   (*env)->ReleaseStringUTFChars (env, name, filename);
617 
618   return result == CPNATIVE_OK ? mtime : 0;
619 #else /* not WITHOUT_FILESYSTEM */
620   return 0;
621 #endif /* not WITHOUT_FILESYSTEM */
622 }
623 
624 /*************************************************************************/
625 
626 /*
627  * This method sets the modification date of the file.
628  *
629  * Class:     java_io_VMFile
630  * Method:    setLastModified
631  * Signature: (Ljava/lang/String;J)Z
632  */
633 
634 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_setLastModified(JNIEnv * env,jclass clazz,jstring name,jlong newtime)635 Java_java_io_VMFile_setLastModified (JNIEnv * env,
636                                      jclass clazz __attribute__ ((__unused__)),
637                                      jstring name, jlong newtime)
638 {
639 #ifndef WITHOUT_FILESYSTEM
640   const char *filename;
641   int result;
642 
643   /* Don't use the JCL convert function because it throws an exception
644      on failure */
645   filename = (*env)->GetStringUTFChars (env, name, 0);
646   if (filename == NULL)
647     {
648       return 0;
649     }
650 
651   result = cpio_setModificationTime (filename, newtime);
652   (*env)->ReleaseStringUTFChars (env, name, filename);
653 
654   return result == CPNATIVE_OK ? 1 : 0;
655 #else /* not WITHOUT_FILESYSTEM */
656   return 0;
657 #endif /* not WITHOUT_FILESYSTEM */
658 }
659 
660 /*************************************************************************/
661 
662 /*
663  * This method deletes a file (actually a name for a file - additional
664  * links could exist).
665  *
666  * Class:     java_io_VMFile
667  * Method:    delete
668  * Signature: (Ljava/lang/String;)Z
669  */
670 
671 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_delete(JNIEnv * env,jclass clazz,jstring name)672 Java_java_io_VMFile_delete (JNIEnv * env,
673                             jclass clazz __attribute__ ((__unused__)),
674                             jstring name)
675 {
676 #ifndef WITHOUT_FILESYSTEM
677   const char *filename;
678   int result;
679 
680   /* Don't use the JCL convert function because it throws an exception
681      on failure */
682   filename = (*env)->GetStringUTFChars (env, name, 0);
683   if (filename == NULL)
684     {
685       return 0;
686     }
687 
688   result = cpio_removeFile (filename);
689   (*env)->ReleaseStringUTFChars (env, name, filename);
690 
691   return result == CPNATIVE_OK ? 1 : 0;
692 #else /* not WITHOUT_FILESYSTEM */
693   return 0;
694 #endif /* not WITHOUT_FILESYSTEM */
695 }
696 
697 /*************************************************************************/
698 
699 /*
700  * This method creates a directory.
701  *
702  * Class:     java_io_VMFile
703  * Method:    mkdir
704  * Signature: (Ljava/lang/String;)Z
705  */
706 
707 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_mkdir(JNIEnv * env,jclass clazz,jstring name)708 Java_java_io_VMFile_mkdir (JNIEnv * env,
709                            jclass clazz __attribute__ ((__unused__)),
710                            jstring name)
711 {
712 #ifndef WITHOUT_FILESYSTEM
713   const char *pathname;
714   int result;
715 
716   /* Don't use the JCL convert function because it throws an exception
717      on failure */
718   pathname = (*env)->GetStringUTFChars (env, name, 0);
719   if (pathname == NULL)
720     {
721       return 0;
722     }
723 
724   result = cpio_mkdir (pathname);
725   (*env)->ReleaseStringUTFChars (env, name, pathname);
726 
727   return (result == CPNATIVE_OK) ? 1 : 0;
728 #else /* not WITHOUT_FILESYSTEM */
729   return 0;
730 #endif /* not WITHOUT_FILESYSTEM */
731 }
732 
733 /*************************************************************************/
734 
735 /*
736  * This method renames a (link to a) file.
737  *
738  * Class:     java_io_VMFile
739  * Method:    renameTo
740  * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
741  */
742 
743 JNIEXPORT jboolean JNICALL
Java_java_io_VMFile_renameTo(JNIEnv * env,jclass clazz,jstring t,jstring d)744 Java_java_io_VMFile_renameTo (JNIEnv * env,
745                               jclass clazz __attribute__ ((__unused__)),
746                               jstring t, jstring d)
747 {
748 #ifndef WITHOUT_FILESYSTEM
749   const char *old_filename, *new_filename;
750   int result;
751 
752   /* Don't use the JCL convert function because it throws an exception
753      on failure */
754   old_filename = (*env)->GetStringUTFChars (env, t, 0);
755   if (old_filename == NULL)
756     {
757       return 0;
758     }
759 
760   new_filename = (*env)->GetStringUTFChars (env, d, 0);
761   if (new_filename == NULL)
762     {
763       (*env)->ReleaseStringUTFChars (env, t, old_filename);
764       return 0;
765     }
766 
767   result = cpio_rename (old_filename, new_filename);
768   (*env)->ReleaseStringUTFChars (env, d, new_filename);
769   (*env)->ReleaseStringUTFChars (env, t, old_filename);
770 
771   return (result == CPNATIVE_OK) ? 1 : 0;
772 #else /* not WITHOUT_FILESYSTEM */
773   return 0;
774 #endif /* not WITHOUT_FILESYSTEM */
775 }
776 
777 /*************************************************************************/
778 
779 /*
780  * This method returns an array of String representing all the files
781  * in a directory except "." and "..".
782  *
783  * Class:     java_io_VMFile
784  * Method:    list
785  * Signature: (Ljava/lang/String;)[Ljava/lang/String;
786  */
787 
788 JNIEXPORT jobjectArray JNICALL
Java_java_io_VMFile_list(JNIEnv * env,jclass clazz,jstring name)789 Java_java_io_VMFile_list (JNIEnv * env,
790                           jclass clazz __attribute__ ((__unused__)),
791                           jstring name)
792 {
793 #ifndef WITHOUT_FILESYSTEM
794   const int REALLOC_SIZE = 10;
795 
796   const char *dirname;
797   int result;
798   char **filelist;
799   void *handle;
800   char *filename = (char *) JCL_malloc (env, FILENAME_MAX);
801   unsigned long int filelist_count, max_filelist_count;
802   char **tmp_filelist;
803   jclass str_clazz;
804   jobjectArray filearray;
805   unsigned long int i;
806   jstring str;
807 
808   /* Don't use the JCL convert function because it throws an exception
809      on failure */
810   dirname = (*env)->GetStringUTFChars (env, name, 0);
811   if (dirname == NULL)
812     {
813       return 0;
814     }
815 
816   /* open directory for reading */
817   result = cpio_openDir (dirname, &handle);
818 
819   (*env)->ReleaseStringUTFChars (env, name, dirname);
820 
821   if (result != CPNATIVE_OK)
822     {
823       return 0;
824     }
825 
826   /* allocate filelist */
827   filelist = (char **) JCL_malloc (env, sizeof (char *) * REALLOC_SIZE);
828   if (filelist == NULL)
829     {
830       result = cpio_closeDir (handle);
831       return 0;
832     }
833   filelist_count = 0;
834   max_filelist_count = REALLOC_SIZE;
835 
836   /* read the files from the directory */
837   result = cpio_readDir (handle, filename);
838   while (result == CPNATIVE_OK)
839     {
840       if ((strcmp (filename, ".") != 0) && (strcmp (filename, "..") != 0))
841 	{
842 	  /* allocate more memory if necessary */
843 	  if (filelist_count >= max_filelist_count)
844 	    {
845 	      tmp_filelist = (char **) JCL_realloc (env,
846 						    filelist,
847 						    (max_filelist_count +
848 						     REALLOC_SIZE) *
849 						    sizeof (char *));
850 	      if (tmp_filelist == NULL)
851 		{
852 		  for (i = 0; i < filelist_count; i++)
853 		    {
854 		      JCL_free (env, filelist[i]);
855 		    }
856 		  JCL_free (env, filelist);
857 		  result = cpio_closeDir (handle);
858 		  return 0;
859 		}
860 	      filelist = tmp_filelist;
861 	      max_filelist_count += REALLOC_SIZE;
862 	    }
863 
864 	  /* save entry in list (avoid strdup, because it is not ANSI C, thus difficult to port) */
865 	  filelist[filelist_count] =
866 	    (char *) JCL_malloc (env, strlen (filename) + 1);
867 	  assert (filelist[filelist_count] != NULL);
868 	  strcpy (filelist[filelist_count], filename);
869 	  filelist_count++;
870 	}
871 
872       /* read next directory entry */
873       result = cpio_readDir (handle, filename);
874     }
875 
876   JCL_free (env, filename);
877 
878   /* close directory */
879   result = cpio_closeDir (handle);
880 
881   /* put the list of files into a Java String array and return it */
882   str_clazz = (*env)->FindClass (env, "java/lang/String");
883   if (str_clazz == NULL)
884     {
885       for (i = 0; i < filelist_count; i++)
886 	{
887 	  JCL_free (env, filelist[i]);
888 	}
889       JCL_free (env, filelist);
890       return 0;
891     }
892   filearray = (*env)->NewObjectArray (env, filelist_count, str_clazz, 0);
893   if (filearray == NULL)
894     {
895       for (i = 0; i < filelist_count; i++)
896 	{
897 	  JCL_free (env, filelist[i]);
898 	}
899       JCL_free (env, filelist);
900       return 0;
901     }
902 
903   (*env)->DeleteLocalRef (env, str_clazz);
904 
905   for (i = 0; i < filelist_count; i++)
906     {
907       /* create new string */
908       str = (*env)->NewStringUTF (env, filelist[i]);
909       if (str == NULL)
910 	{
911 	  /* We don't clean up everything here, but if this failed,
912 	     something serious happened anyway */
913 	  for (i = 0; i < filelist_count; i++)
914 	    {
915 	      JCL_free (env, filelist[i]);
916 	    }
917 	  JCL_free (env, filelist);
918 	  return 0;
919 	}
920 
921       /* save into array */
922       (*env)->SetObjectArrayElement (env, filearray, i, str);
923 
924       /* delete local reference */
925       (*env)->DeleteLocalRef (env, str);
926     }
927 
928   /* free resources */
929   for (i = 0; i < filelist_count; i++)
930     {
931       JCL_free (env, filelist[i]);
932     }
933   JCL_free (env, filelist);
934 
935   return filearray;
936 #else /* not WITHOUT_FILESYSTEM */
937   return 0;
938 #endif /* not WITHOUT_FILESYSTEM */
939 }
940 
941 /*************************************************************************/
942 
943 /*
944  * These two methods are used to maintain dynamically allocated
945  * buffers for getCanonicalPath without the overhead of calling
946  * realloc every time a buffer is modified.  Buffers are sized
947  * at the smallest multiple of CHUNKSIZ that is greater than or
948  * equal to the desired length.  The default CHUNKSIZ is 256,
949  * longer than most paths, so in most cases a getCanonicalPath
950  * will require only one malloc per buffer.
951  */
952 
953 #define CHUNKLOG 8
954 #define CHUNKSIZ (1 << CHUNKLOG)
955 
956 static int
nextChunkSize(int size)957 nextChunkSize (int size)
958 {
959   return ((size >> CHUNKLOG) + ((size & (CHUNKSIZ - 1)) ? 1 : 0)) << CHUNKLOG;
960 }
961 
962 static char *
maybeGrowBuf(JNIEnv * env,char * buf,int * size,int required)963 maybeGrowBuf (JNIEnv *env, char *buf, int *size, int required)
964 {
965   if (required > *size)
966     {
967       *size = nextChunkSize (required);
968       buf = JCL_realloc (env, buf, *size);
969     }
970   return buf;
971 }
972 
973 /*************************************************************************/
974 
975 /*
976  * This method converts a path to canonical form on GNU/Posix systems.
977  * This involves the removal of redundant separators, references to
978  * "." and "..", and symbolic links.
979  *
980  * The conversion proceeds on a component-by-component basis: symbolic
981  * links and references to ".."  are resolved as and when they occur.
982  * This means that if "/foo/bar" is a symbolic link to "/baz" then the
983  * canonical form of "/foo/bar/.." is "/" and not "/foo".
984  *
985  * In order to mimic the behaviour of proprietary JVMs, non-existant
986  * path components are allowed (a departure from the normal GNU system
987  * convention).  This means that if "/foo/bar" is a symbolic link to
988  * "/baz", the canonical form of "/non-existant-directory/../foo/bar"
989  * is "/baz".
990  *
991  * Class:     java_io_VMFile
992  * Method:    toCanonicalForm
993  * Signature: (Ljava/lang/String)Ljava/lang/String
994  */
995 
996 JNIEXPORT jstring JNICALL
Java_java_io_VMFile_toCanonicalForm(JNIEnv * env,jclass class,jstring jpath)997 Java_java_io_VMFile_toCanonicalForm (JNIEnv *env,
998 				                     jclass class __attribute__ ((__unused__)),
999 				                     jstring jpath)
1000 {
1001 #ifndef WITHOUT_FILESYSTEM
1002   const char *path;
1003   char *src, *dst;
1004   int srci, dsti;
1005   int srcl, dstl;
1006   int len;
1007   int fschecks;
1008 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
1009   struct stat sb;
1010 #endif /* HAVE_LSTAT && HAVE_READLINK */
1011 
1012   path = JCL_jstring_to_cstring (env, jpath);
1013   if (path == NULL)
1014     return NULL;
1015 
1016   /* It is the caller's responsibility to ensure the path is absolute. */
1017   if (path[0] == 0 || path[0] != '/')
1018     {
1019       JCL_free_cstring (env, jpath, path);
1020       JCL_ThrowException (env, "java/lang/RuntimeException", "Not absolute");
1021       return NULL;
1022     }
1023 
1024   len = strlen (path);
1025   srcl = nextChunkSize (len + 1);
1026   src = JCL_malloc (env, srcl);
1027   if (src == NULL)
1028     {
1029       JCL_free_cstring (env, jpath, path);
1030       return NULL;
1031     }
1032   strcpy (src, path);
1033   JCL_free_cstring (env, jpath, path);
1034   srci = 1;
1035 
1036   dstl = nextChunkSize (2);
1037   dst = JCL_malloc (env, dstl);
1038   if (dst == NULL)
1039     {
1040       JCL_free (env, src);
1041       return NULL;
1042     }
1043   dst[0] = '/';
1044   dsti = 1;
1045 
1046   fschecks = JNI_TRUE;
1047 
1048   while (src[srci] != '\0')
1049     {
1050       int tmpi, dsti_save;
1051 
1052       /* Skip slashes. */
1053       while (src[srci] == '/')
1054 	srci++;
1055       tmpi = srci;
1056       /* Find next slash. */
1057       while (src[srci] != '/' && src[srci] != '\0')
1058 	srci++;
1059       if (srci == tmpi)
1060 	/* We hit the end. */
1061 	break;
1062       len = srci - tmpi;
1063 
1064       /* Handle "." and "..". */
1065       if (len == 1 && src[tmpi] == '.')
1066 	continue;
1067       if (len == 2 && src[tmpi] == '.' && src[tmpi + 1] == '.')
1068 	{
1069 	  while (dsti > 1 && dst[dsti - 1] != '/')
1070 	    dsti--;
1071 	  if (dsti != 1)
1072 	    dsti--;
1073 	  /* Reenable filesystem checking if disabled, as we might
1074 	   * have reversed over whatever caused the problem before.
1075 	   * At least one proprietary JVM has inconsistencies because
1076 	   * it does not do this.
1077 	   */
1078 	  fschecks = JNI_TRUE;
1079 	  continue;
1080 	}
1081 
1082       /* Handle real path components. */
1083       dst = maybeGrowBuf (env,
1084 			  dst, &dstl, dsti + (dsti > 1 ? 1 : 0) + len + 1);
1085       if (dst == NULL)
1086 	{
1087 	  JCL_free (env, src);
1088 	  return NULL;
1089 	}
1090       dsti_save = dsti;
1091       if (dsti > 1)
1092 	dst[dsti++] = '/';
1093       strncpy (&dst[dsti], &src[tmpi], len);
1094       dsti += len;
1095       if (fschecks == JNI_FALSE)
1096 	continue;
1097 
1098 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
1099       dst[dsti] = '\0';
1100       if (lstat (dst, &sb) == 0)
1101 	{
1102 	  if (S_ISLNK (sb.st_mode))
1103 	    {
1104 	      int tmpl = CHUNKSIZ;
1105 	      char *tmp = JCL_malloc (env, tmpl);
1106 	      if (tmp == NULL)
1107 		{
1108 		  JCL_free (env, src);
1109 		  JCL_free (env, dst);
1110 		  return NULL;
1111 		}
1112 
1113 	      while (1)
1114 		{
1115 		  tmpi = readlink (dst, tmp, tmpl);
1116 		  if (tmpi < 1)
1117 		    {
1118 		      JCL_free (env, src);
1119 		      JCL_free (env, dst);
1120 		      JCL_free (env, tmp);
1121 		      JCL_ThrowException (env, "java/io/IOException",
1122 					  "readlink failed");
1123 		      return NULL;
1124 		    }
1125 		  if (tmpi < tmpl)
1126 		    break;
1127 		  tmpl += CHUNKSIZ;
1128 		  tmp = JCL_realloc (env, tmp, tmpl);
1129 		}
1130 
1131 	      /* Prepend the link's path to src. */
1132 	      tmp = maybeGrowBuf (env,
1133 				  tmp, &tmpl, tmpi + strlen (&src[srci]) + 1);
1134 	      if (tmp == NULL)
1135 		{
1136 		  JCL_free (env, src);
1137 		  JCL_free (env, dst);
1138 		  return NULL;
1139 		}
1140 
1141 	      strcpy (&tmp[tmpi], &src[srci]);
1142 	      JCL_free (env, src);
1143 	      src = tmp;
1144 	      srcl = tmpl;
1145 	      srci = 0;
1146 
1147 	      /* Either replace or append dst depending on whether the
1148 	       * link is relative or absolute.
1149 	       */
1150 	      dsti = src[0] == '/' ? 1 : dsti_save;
1151 	    }
1152 	}
1153       else
1154 	{
1155 	  /* Something doesn't exist, or we don't have permission to
1156 	   * read it, or a previous path component is a directory, or
1157 	   * a symlink is looped.  Whatever, we can't check the
1158 	   * filesystem any more.
1159 	   */
1160 	  fschecks = JNI_FALSE;
1161 	}
1162 #endif /* HAVE_LSTAT && HAVE_READLINK */
1163     }
1164   dst[dsti] = '\0';
1165 
1166   jpath = (*env)->NewStringUTF (env, dst);
1167   JCL_free (env, src);
1168   JCL_free (env, dst);
1169   return jpath;
1170 #else /* not WITHOUT_FILESYSTEM */
1171   return NULL;
1172 #endif /* not WITHOUT_FILESYSTEM */
1173 }
1174 
1175 /*************************************************************************/
1176 
1177 /* ***** PRIVATE FUNCTIONS IMPLEMENTATION ***** */
1178 
set_file_permissions(JNIEnv * env,jstring name,jboolean enable,jboolean ownerOnly,int permissions)1179 static jboolean set_file_permissions (JNIEnv *env, jstring name,
1180                                       jboolean enable,
1181                                       jboolean ownerOnly,
1182                                       int permissions)
1183 {
1184 #ifndef WITHOUT_FILESYSTEM
1185   const char *filename;
1186   int result = JNI_FALSE;
1187 
1188   /* Don't use the JCL convert function because it throws an exception
1189      on failure */
1190   filename = (*env)->GetStringUTFChars (env, name, 0);
1191   if (filename == NULL)
1192     {
1193       return JNI_FALSE;
1194     }
1195 
1196   if (ownerOnly)
1197     {
1198       permissions |= CPFILE_FLAG_USR;
1199     }
1200 
1201   if (!enable)
1202     {
1203       permissions |= CPFILE_FLAG_OFF;
1204     }
1205 
1206   result = cpio_chmod (filename, permissions);
1207   (*env)->ReleaseStringUTFChars (env, name, filename);
1208 
1209   return result == CPNATIVE_OK ? JNI_TRUE : JNI_FALSE;
1210 
1211 #else /* not WITHOUT_FILESYSTEM */
1212   return JNI_FALSE;
1213 #endif /* not WITHOUT_FILESYSTEM */
1214 }
1215