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_checkAccess0(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_setPermission0(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_getLastModifiedTime0(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_getLength0(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_createFileExclusively0(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_list0(JNIEnv *env, jobject this, jobject file)

311 {
312     DIR *dir = NULL;
313     struct dirent *ptr;
314     int len, maxlen;
315     jobjectArray rv, old;
316     jclass str_class;
317 
318     str_class = JNU_ClassString(env);
319     CHECK_NULL_RETURN(str_class, NULL);
320 
321     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
322         dir = opendir(path);
323     } END_PLATFORM_STRING(env, path);
324     if (dir == NULL) return NULL;
325 
326     /* Allocate an initial String array */
327     len = 0;
328     maxlen = 16;
329     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
330     if (rv == NULL) goto error;
331 
332     /* Scan the directory */
333     while ((ptr = readdir(dir)) != NULL) {
334         jstring name;
335         if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
336             continue;
337         if (len == maxlen) {
338             old = rv;
339             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
340             if (rv == NULL) goto error;
341             if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
342             (*env)->DeleteLocalRef(env, old);
343         }
344 #ifdef MACOSX
345         name = newStringPlatform(env, ptr->d_name);
346 #else
347         name = JNU_NewStringPlatform(env, ptr->d_name);
348 #endif
349         if (name == NULL) goto error;
350         (*env)->SetObjectArrayElement(env, rv, len++, name);
351         (*env)->DeleteLocalRef(env, name);
352     }
353     closedir(dir);
354 
355     /* Copy the final results into an appropriately-sized array */
356     if (len < maxlen) {
357         old = rv;
358         rv = (*env)->NewObjectArray(env, len, str_class, NULL);
359         if (rv == NULL) {
360             return NULL;
361         }
362         if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
363             return NULL;
364         }
365     }
366     return rv;
367 
368  error:
369     closedir(dir);
370     return NULL;
371 }
372 
373 
374 JNIEXPORT jboolean JNICALL
375 Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this,
376                                              jobject file)
377 {
378     jboolean rv = JNI_FALSE;
379 
380     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
381         if (mkdir(path, 0777) == 0) {
382             rv = JNI_TRUE;
383         }
384     } END_PLATFORM_STRING(env, path);
385     return rv;
386 }
387 
388 
389 JNIEXPORT jboolean JNICALL
390 Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
391                                     jobject from, jobject to)
392 {
393     jboolean rv = JNI_FALSE;
394 
395     WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
396         WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
397             if (rename(fromPath, toPath) == 0) {
398                 rv = JNI_TRUE;
399             }
400         } END_PLATFORM_STRING(env, toPath);
401     } END_PLATFORM_STRING(env, fromPath);
402     return rv;
403 }
404 
405 JNIEXPORT jboolean JNICALL
406 Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this,
407                                                  jobject file, jlong time)
408 {
409     jboolean rv = JNI_FALSE;
410 
411     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
412         struct stat64 sb;
413 
414         if (stat64(path, &sb) == 0) {
415             struct timeval tv[2];
416 
417             /* Preserve access time */
418 #if defined(_AIX)
419             tv[0].tv_sec = sb.st_atime;
420             tv[0].tv_usec = sb.st_atime_n / 1000;
421 #elif defined(MACOSX)
422             tv[0].tv_sec = sb.st_atimespec.tv_sec;
423             tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
424 #else
425             tv[0].tv_sec = sb.st_atim.tv_sec;
426             tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
427 #endif
428             /* Change last-modified time */
429             tv[1].tv_sec = time / 1000;
430             tv[1].tv_usec = (time % 1000) * 1000;
431 
432             if (utimes(path, tv) == 0)
433                 rv = JNI_TRUE;
434         }
435     } END_PLATFORM_STRING(env, path);
436 
437     return rv;
438 }
439 
440 
441 JNIEXPORT jboolean JNICALL
442 Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this,
443                                          jobject file)
444 {
445     jboolean rv = JNI_FALSE;
446 
447     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
448         int mode;
449         int res;
450         if (statMode(path, &mode)) {
451             RESTARTABLE(chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)), res);
452             if (res == 0) {
453                 rv = JNI_TRUE;
454             }
455         }
456     } END_PLATFORM_STRING(env, path);
457     return rv;
458 }
459 
460 JNIEXPORT jlong JNICALL
461 Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
462                                       jobject file, jint t)
463 {
464     jlong rv = 0L;
465 
466     WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
467 #ifdef MACOSX
468         struct statfs fsstat;
469 #else
470         struct statvfs64 fsstat;
471         int res;
472 #endif
473         memset(&fsstat, 0, sizeof(fsstat));
474 #ifdef MACOSX
475         if (statfs(path, &fsstat) == 0) {
476             switch(t) {
477                 case java_io_FileSystem_SPACE_TOTAL:
478                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
479                                    long_to_jlong(fsstat.f_blocks));
480                     break;
481                 case java_io_FileSystem_SPACE_FREE:
482                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
483                                    long_to_jlong(fsstat.f_bfree));
484                     break;
485                 case java_io_FileSystem_SPACE_USABLE:
486                     rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
487                                    long_to_jlong(fsstat.f_bavail));
488                     break;
489                 default:
490                     assert(0);
491             }
492         }
493 #else
494         RESTARTABLE(statvfs64(path, &fsstat), res);
495         if (res == 0) {
496             switch(t) {
497             case java_io_FileSystem_SPACE_TOTAL:
498                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
499                                long_to_jlong(fsstat.f_blocks));
500                 break;
501             case java_io_FileSystem_SPACE_FREE:
502                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
503                                long_to_jlong(fsstat.f_bfree));
504                 break;
505             case java_io_FileSystem_SPACE_USABLE:
506                 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
507                                long_to_jlong(fsstat.f_bavail));
508                 break;
509             default:
510                 assert(0);
511             }
512         }
513 #endif
514     } END_PLATFORM_STRING(env, path);
515     return rv;
516 }
517 
518 JNIEXPORT jlong JNICALL
519 Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
520                                         jstring pathname)
521 {
522     jlong length = -1;
523     WITH_PLATFORM_STRING(env, pathname, path) {
524         length = (jlong)pathconf(path, _PC_NAME_MAX);
525     } END_PLATFORM_STRING(env, path);
526     return length != -1 ? length : (jlong)NAME_MAX;
527 }
--- EOF ---