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