1 /*
  2  * Copyright (c) 1998, 2020, 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 <unistd.h>
 27 #include <assert.h>
 28 #include <sys/types.h>
 29 #include <sys/time.h>
 30 #include <sys/stat.h>
 31 #ifdef MACOSX
 32 #include <sys/param.h>
 33 #include <sys/mount.h>
 34 #else
 35 #include <sys/statvfs.h>
 36 #endif
 37 #include <string.h>
 38 #include <stdlib.h>
 39 #include <dlfcn.h>
 40 #include <limits.h>
 41 #include <errno.h>
 42 #include <fcntl.h>
 43 #include <dirent.h>
 44 
 45 #include "jni.h"
 46 #include "jni_util.h"
 47 #include "jlong.h"
 48 #include "jdk_util.h"
 49 #include "io_util.h"
 50 #include "io_util_md.h"
 51 #include "java_io_FileSystem.h"
 52 #include "java_io_UnixFileSystem.h"
 53 
 54 #if defined(_AIX)
 55   #if !defined(NAME_MAX)
 56     #define NAME_MAX MAXNAMLEN
 57   #endif
 58   #define DIR DIR64
 59   #define dirent dirent64
 60   #define opendir opendir64
 61   #define readdir readdir64
 62   #define closedir closedir64
 63   #define stat stat64
 64 #endif
 65 
 66 #if defined(_ALLBSD_SOURCE)
 67   #ifndef MACOSX
 68     #define statvfs64 statvfs
 69     #define stat64 stat
 70   #endif
 71 #endif
 72 
 73 /* -- Field IDs -- */
 74 
 75 static struct {
 76     jfieldID path;
 77 } ids;
 78 
 79 
 80 JNIEXPORT void JNICALL
 81 Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
 82 {
 83     jclass fileClass = (*env)->FindClass(env, "java/io/File");
 84     if (!fileClass) return;
 85     ids.path = (*env)->GetFieldID(env, fileClass,
 86                                   "path", "Ljava/lang/String;");
 87 }
 88 
 89 /* -- Path operations -- */
 90 
 91 JNIEXPORT jstring JNICALL
 92 Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
 93                                           jstring pathname)
 94 {
 95     jstring rv = NULL;
 96 
 97     WITH_PLATFORM_STRING(env, pathname, path) {
 98         char canonicalPath[PATH_MAX];
 99         if (JDK_Canonicalize((char *)path,
100                          canonicalPath, PATH_MAX) < 0) {
101             JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
102         } else {
103 #ifdef MACOSX
104             rv = newStringPlatform(env, canonicalPath);
105 #else
106             rv = JNU_NewStringPlatform(env, canonicalPath);
107 #endif
108         }
109     } END_PLATFORM_STRING(env, path);
110     return rv;
111 }
112 
113 
114 /* -- Attribute accessors -- */
115 
116 
117 static jboolean
118 statMode(const char *path, int *mode)
119 {
120     struct stat64 sb;
121     if (stat64(path, &sb) == 0) {
122         *mode = sb.st_mode;
123         return JNI_TRUE;
124     }
125     return JNI_FALSE;
126 }
127 
128 
129 JNIEXPORT jint JNICALL
130 Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
131                                                   jobject file)
132 {
133     jint rv = 0;
134 
135     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
136         int mode;
137         if (statMode(path, &mode)) {
138             int fmt = mode & S_IFMT;
139             rv = (jint) (java_io_FileSystem_BA_EXISTS
140                   | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
141                   | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
142         }
143     } END_PLATFORM_STRING(env, path);
144     return rv;
145 }
146 
147 JNIEXPORT jboolean JNICALL
148 Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
149                                         jobject file, jint a)
150 {
151     jboolean rv = JNI_FALSE;
152     int mode = 0;
153     switch (a) {
154     case java_io_FileSystem_ACCESS_READ:
155         mode = R_OK;
156         break;
157     case java_io_FileSystem_ACCESS_WRITE:
158         mode = W_OK;
159         break;
160     case java_io_FileSystem_ACCESS_EXECUTE:
161         mode = X_OK;
162         break;
163     default: assert(0);
164     }
165     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
166         int res;
167         RESTARTABLE(access(path, mode), res);
168         if (res == 0) {
169             rv = JNI_TRUE;
170         }
171     } END_PLATFORM_STRING(env, path);
172     return rv;
173 }
174 
175 
176 JNIEXPORT jboolean JNICALL
177 Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
178                                           jobject file,
179                                           jint access,
180                                           jboolean enable,
181                                           jboolean owneronly)
182 {
183     jboolean rv = JNI_FALSE;
184 
185     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
186         int amode = 0;
187         int mode;
188         int res;
189         switch (access) {
190         case java_io_FileSystem_ACCESS_READ:
191             if (owneronly)
192                 amode = S_IRUSR;
193             else
194                 amode = S_IRUSR | S_IRGRP | S_IROTH;
195             break;
196         case java_io_FileSystem_ACCESS_WRITE:
197             if (owneronly)
198                 amode = S_IWUSR;
199             else
200                 amode = S_IWUSR | S_IWGRP | S_IWOTH;
201             break;
202         case java_io_FileSystem_ACCESS_EXECUTE:
203             if (owneronly)
204                 amode = S_IXUSR;
205             else
206                 amode = S_IXUSR | S_IXGRP | S_IXOTH;
207             break;
208         default:
209             assert(0);
210         }
211         if (statMode(path, &mode)) {
212             if (enable)
213                 mode |= amode;
214             else
215                 mode &= ~amode;
216             RESTARTABLE(chmod(path, mode), res);
217             if (res == 0) {
218                 rv = JNI_TRUE;
219             }
220         }
221     } END_PLATFORM_STRING(env, path);
222     return rv;
223 }
224 
225 JNIEXPORT jlong JNICALL
226 Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
227                                                 jobject file)
228 {
229     jlong rv = 0;
230 
231     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
232         struct stat64 sb;
233         if (stat64(path, &sb) == 0) {
234 #if defined(_AIX)
235             rv =  (jlong)sb.st_mtime * 1000;
236             rv += (jlong)sb.st_mtime_n / 1000000;
237 #elif defined(MACOSX)
238             rv  = (jlong)sb.st_mtimespec.tv_sec * 1000;
239             rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000;
240 #else
241             rv  = (jlong)sb.st_mtim.tv_sec * 1000;
242             rv += (jlong)sb.st_mtim.tv_nsec / 1000000;
243 #endif
244         }
245     } END_PLATFORM_STRING(env, path);
246     return rv;
247 }
248 
249 
250 JNIEXPORT jlong JNICALL
251 Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
252                                       jobject file)
253 {
254     jlong rv = 0;
255 
256     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
257         struct stat64 sb;
258         if (stat64(path, &sb) == 0) {
259             rv = sb.st_size;
260         }
261     } END_PLATFORM_STRING(env, path);
262     return rv;
263 }
264 
265 
266 /* -- File operations -- */
267 
268 
269 JNIEXPORT jboolean JNICALL
270 Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
271                                                   jstring pathname)
272 {
273     jboolean rv = JNI_FALSE;
274 
275     WITH_PLATFORM_STRING(env, pathname, path) {
276         FD fd;
277         /* The root directory always exists */
278         if (strcmp (path, "/")) {
279             fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
280             if (fd < 0) {
281                 if (errno != EEXIST)
282                     JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
283             } else {
284                 if (close(fd) == -1)
285                     JNU_ThrowIOExceptionWithLastError(env, "Could not close file");
286                 rv = JNI_TRUE;
287             }
288         }
289     } END_PLATFORM_STRING(env, path);
290     return rv;
291 }
292 
293 
294 JNIEXPORT jboolean JNICALL
295 Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
296                                     jobject file)
297 {
298     jboolean rv = JNI_FALSE;
299 
300     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
301         if (remove(path) == 0) {
302             rv = JNI_TRUE;
303         }
304     } END_PLATFORM_STRING(env, path);
305     return rv;
306 }
307 
308 
309 JNIEXPORT jobjectArray JNICALL
310 Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
311                                  jobject file)
312 {
313     DIR *dir = NULL;
314     struct dirent *ptr;
315     int len, maxlen;
316     jobjectArray rv, old;
317     jclass str_class;
318 
319     str_class = JNU_ClassString(env);
320     CHECK_NULL_RETURN(str_class, NULL);
321 
322     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
323         dir = opendir(path);
324     } END_PLATFORM_STRING(env, path);
325     if (dir == NULL) return NULL;
326 
327     /* Allocate an initial String array */
328     len = 0;
329     maxlen = 16;
330     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
331     if (rv == NULL) goto error;
332 
333     /* Scan the directory */
334     while ((ptr = readdir(dir)) != NULL) {
335         jstring name;
336         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
337             continue;
338         if (len == maxlen) {
339             old = rv;
340             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
341             if (rv == NULL) goto error;
342             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
343             (*env)->DeleteLocalRef(env, old);
344         }
345 #ifdef MACOSX
346         name = newStringPlatform(env, ptr->d_name);
347 #else
348         name = JNU_NewStringPlatform(env, ptr->d_name);
349 #endif
350         if (name == NULL) goto error;
351         (*env)->SetObjectArrayElement(env, rv, len++, name);
352         (*env)->DeleteLocalRef(env, name);
353     }
354     closedir(dir);
355 
356     /* Copy the final results into an appropriately-sized array */
357     if (len < maxlen) {
358         old = rv;
359         rv = (*env)->NewObjectArray(env, len, str_class, NULL);
360         if (rv == NULL) {
361             return NULL;
362         }
363         if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
364             return NULL;
365         }
366     }
367     return rv;
368 
369  error:
370     closedir(dir);
371     return NULL;
372 }
373 
374 
375 JNIEXPORT jboolean JNICALL
376 Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
377                                             jobject file)
378 {
379     jboolean rv = JNI_FALSE;
380 
381     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
382         if (mkdir(path, 0777) == 0) {
383             rv = JNI_TRUE;
384         }
385     } END_PLATFORM_STRING(env, path);
386     return rv;
387 }
388 
389 
390 JNIEXPORT jboolean JNICALL
391 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
392                                     jobject from, jobject to)
393 {
394     jboolean rv = JNI_FALSE;
395 
396     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
397         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
398             if (rename(fromPath, toPath) == 0) {
399                 rv = JNI_TRUE;
400             }
401         } END_PLATFORM_STRING(env, toPath);
402     } END_PLATFORM_STRING(env, fromPath);
403     return rv;
404 }
405 
406 JNIEXPORT jboolean JNICALL
407 Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
408                                                 jobject file, jlong time)
409 {
410     jboolean rv = JNI_FALSE;
411 
412     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
413         struct stat64 sb;
414 
415         if (stat64(path, &sb) == 0) {
416             struct timeval tv[2];
417 
418             /* Preserve access time */
419 #if defined(_AIX)
420             tv[0].tv_sec = sb.st_atime;
421             tv[0].tv_usec = sb.st_atime_n / 1000;
422 #elif defined(MACOSX)
423             tv[0].tv_sec = sb.st_atimespec.tv_sec;
424             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
425 #else
426             tv[0].tv_sec = sb.st_atim.tv_sec;
427             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
428 #endif
429             /* Change last-modified time */
430             tv[1].tv_sec = time / 1000;
431             tv[1].tv_usec = (time % 1000) * 1000;
432 
433             if (utimes(path, tv) == 0)
434                 rv = JNI_TRUE;
435         }
436     } END_PLATFORM_STRING(env, path);
437 
438     return rv;
439 }
440 
441 
442 JNIEXPORT jboolean JNICALL
443 Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
444                                         jobject file)
445 {
446     jboolean rv = JNI_FALSE;
447 
448     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
449         int mode;
450         int res;
451         if (statMode(path, &mode)) {
452             RESTARTABLE(chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)), res);
453             if (res == 0) {
454                 rv = JNI_TRUE;
455             }
456         }
457     } END_PLATFORM_STRING(env, path);
458     return rv;
459 }
460 
461 JNIEXPORT jlong JNICALL
462 Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
463                                      jobject file, jint t)
464 {
465     jlong rv = 0L;
466 
467     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
468 #ifdef MACOSX
469         struct statfs fsstat;
470 #else
471         struct statvfs64 fsstat;
472         int res;
473 #endif
474         memset(&fsstat, 0, sizeof(fsstat));
475 #ifdef MACOSX
476         if (statfs(path, &fsstat) == 0) {
477             switch(t) {
478                 case java_io_FileSystem_SPACE_TOTAL:
479                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
480                                    long_to_jlong(fsstat.f_blocks));
481                     break;
482                 case java_io_FileSystem_SPACE_FREE:
483                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
484                                    long_to_jlong(fsstat.f_bfree));
485                     break;
486                 case java_io_FileSystem_SPACE_USABLE:
487                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
488                                    long_to_jlong(fsstat.f_bavail));
489                     break;
490                 default:
491                     assert(0);
492             }
493         }
494 #else
495         RESTARTABLE(statvfs64(path, &fsstat), res);
496         if (res == 0) {
497             switch(t) {
498             case java_io_FileSystem_SPACE_TOTAL:
499                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
500                                long_to_jlong(fsstat.f_blocks));
501                 break;
502             case java_io_FileSystem_SPACE_FREE:
503                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
504                                long_to_jlong(fsstat.f_bfree));
505                 break;
506             case java_io_FileSystem_SPACE_USABLE:
507                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
508                                long_to_jlong(fsstat.f_bavail));
509                 break;
510             default:
511                 assert(0);
512             }
513         }
514 #endif
515     } END_PLATFORM_STRING(env, path);
516     return rv;
517 }
518 
519 JNIEXPORT jlong JNICALL
520 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
521                                         jstring pathname)
522 {
523     jlong length = -1;
524     WITH_PLATFORM_STRING(env, pathname, path) {
525         length = (jlong)pathconf(path, _PC_NAME_MAX);
526     } END_PLATFORM_STRING(env, path);
527     return length != -1 ? length : (jlong)NAME_MAX;
528 }