1/*
2 * Copyright (c) 1998, 2016, 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            rv = 1000 * (jlong)sb.st_mtime;
233        }
234    } END_PLATFORM_STRING(env, path);
235    return rv;
236}
237
238
239JNIEXPORT jlong JNICALL
240Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
241                                      jobject file)
242{
243    jlong rv = 0;
244
245    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
246        struct stat64 sb;
247        if (stat64(path, &sb) == 0) {
248            rv = sb.st_size;
249        }
250    } END_PLATFORM_STRING(env, path);
251    return rv;
252}
253
254
255/* -- File operations -- */
256
257
258JNIEXPORT jboolean JNICALL
259Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
260                                                  jstring pathname)
261{
262    jboolean rv = JNI_FALSE;
263
264    WITH_PLATFORM_STRING(env, pathname, path) {
265        FD fd;
266        /* The root directory always exists */
267        if (strcmp (path, "/")) {
268            fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
269            if (fd < 0) {
270                if (errno != EEXIST)
271                    JNU_ThrowIOExceptionWithLastError(env, path);
272            } else {
273                if (close(fd) == -1)
274                    JNU_ThrowIOExceptionWithLastError(env, path);
275                rv = JNI_TRUE;
276            }
277        }
278    } END_PLATFORM_STRING(env, path);
279    return rv;
280}
281
282
283JNIEXPORT jboolean JNICALL
284Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
285                                    jobject file)
286{
287    jboolean rv = JNI_FALSE;
288
289    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
290        if (remove(path) == 0) {
291            rv = JNI_TRUE;
292        }
293    } END_PLATFORM_STRING(env, path);
294    return rv;
295}
296
297
298JNIEXPORT jobjectArray JNICALL
299Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
300                                 jobject file)
301{
302    DIR *dir = NULL;
303    struct dirent64 *ptr;
304    struct dirent64 *result;
305    int len, maxlen;
306    jobjectArray rv, old;
307    jclass str_class;
308
309    str_class = JNU_ClassString(env);
310    CHECK_NULL_RETURN(str_class, NULL);
311
312    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
313        dir = opendir(path);
314    } END_PLATFORM_STRING(env, path);
315    if (dir == NULL) return NULL;
316
317    ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
318    if (ptr == NULL) {
319        JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
320        closedir(dir);
321        return NULL;
322    }
323
324    /* Allocate an initial String array */
325    len = 0;
326    maxlen = 16;
327    rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
328    if (rv == NULL) goto error;
329
330    /* Scan the directory */
331    while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
332        jstring name;
333        if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
334            continue;
335        if (len == maxlen) {
336            old = rv;
337            rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
338            if (rv == NULL) goto error;
339            if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
340            (*env)->DeleteLocalRef(env, old);
341        }
342#ifdef MACOSX
343        name = newStringPlatform(env, ptr->d_name);
344#else
345        name = JNU_NewStringPlatform(env, ptr->d_name);
346#endif
347        if (name == NULL) goto error;
348        (*env)->SetObjectArrayElement(env, rv, len++, name);
349        (*env)->DeleteLocalRef(env, name);
350    }
351    closedir(dir);
352    free(ptr);
353
354    /* Copy the final results into an appropriately-sized array */
355    old = rv;
356    rv = (*env)->NewObjectArray(env, len, str_class, NULL);
357    if (rv == NULL) {
358        return NULL;
359    }
360    if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
361        return NULL;
362    }
363    return rv;
364
365 error:
366    closedir(dir);
367    free(ptr);
368    return NULL;
369}
370
371
372JNIEXPORT jboolean JNICALL
373Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
374                                            jobject file)
375{
376    jboolean rv = JNI_FALSE;
377
378    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
379        if (mkdir(path, 0777) == 0) {
380            rv = JNI_TRUE;
381        }
382    } END_PLATFORM_STRING(env, path);
383    return rv;
384}
385
386
387JNIEXPORT jboolean JNICALL
388Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
389                                    jobject from, jobject to)
390{
391    jboolean rv = JNI_FALSE;
392
393    WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
394        WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
395            if (rename(fromPath, toPath) == 0) {
396                rv = JNI_TRUE;
397            }
398        } END_PLATFORM_STRING(env, toPath);
399    } END_PLATFORM_STRING(env, fromPath);
400    return rv;
401}
402
403JNIEXPORT jboolean JNICALL
404Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
405                                                jobject file, jlong time)
406{
407    jboolean rv = JNI_FALSE;
408
409    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
410        struct stat64 sb;
411
412        if (stat64(path, &sb) == 0) {
413            struct timeval tv[2];
414
415            /* Preserve access time */
416            tv[0].tv_sec = sb.st_atime;
417            tv[0].tv_usec = 0;
418
419            /* Change last-modified time */
420            tv[1].tv_sec = time / 1000;
421            tv[1].tv_usec = (time % 1000) * 1000;
422
423            if (utimes(path, tv) == 0)
424                rv = JNI_TRUE;
425        }
426    } END_PLATFORM_STRING(env, path);
427
428    return rv;
429}
430
431
432JNIEXPORT jboolean JNICALL
433Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
434                                        jobject file)
435{
436    jboolean rv = JNI_FALSE;
437
438    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
439        int mode;
440        if (statMode(path, &mode)) {
441            if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
442                rv = JNI_TRUE;
443            }
444        }
445    } END_PLATFORM_STRING(env, path);
446    return rv;
447}
448
449JNIEXPORT jlong JNICALL
450Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
451                                     jobject file, jint t)
452{
453    jlong rv = 0L;
454
455    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
456#ifdef MACOSX
457        struct statfs fsstat;
458#else
459        struct statvfs64 fsstat;
460#endif
461        memset(&fsstat, 0, sizeof(fsstat));
462#ifdef MACOSX
463        if (statfs(path, &fsstat) == 0) {
464            switch(t) {
465                case java_io_FileSystem_SPACE_TOTAL:
466                    rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
467                                   long_to_jlong(fsstat.f_blocks));
468                    break;
469                case java_io_FileSystem_SPACE_FREE:
470                    rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
471                                   long_to_jlong(fsstat.f_bfree));
472                    break;
473                case java_io_FileSystem_SPACE_USABLE:
474                    rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
475                                   long_to_jlong(fsstat.f_bavail));
476                    break;
477                default:
478                    assert(0);
479            }
480        }
481#else
482        if (statvfs64(path, &fsstat) == 0) {
483            switch(t) {
484            case java_io_FileSystem_SPACE_TOTAL:
485                rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
486                               long_to_jlong(fsstat.f_blocks));
487                break;
488            case java_io_FileSystem_SPACE_FREE:
489                rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
490                               long_to_jlong(fsstat.f_bfree));
491                break;
492            case java_io_FileSystem_SPACE_USABLE:
493                rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
494                               long_to_jlong(fsstat.f_bavail));
495                break;
496            default:
497                assert(0);
498            }
499        }
500#endif
501    } END_PLATFORM_STRING(env, path);
502    return rv;
503}
504
505JNIEXPORT jlong JNICALL
506Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
507                                        jstring pathname)
508{
509    jlong length = -1;
510    WITH_PLATFORM_STRING(env, pathname, path) {
511        length = (jlong)pathconf(path, _PC_NAME_MAX);
512    } END_PLATFORM_STRING(env, path);
513    return length != -1 ? length : (jlong)NAME_MAX;
514}
515