1/*
2 * Copyright (c) 2008, 2013, 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 <stdio.h>
27#include <stdlib.h>
28#include <limits.h>
29#include <fcntl.h>
30#include <dirent.h>
31#include <unistd.h>
32#include <pwd.h>
33#include <grp.h>
34#include <errno.h>
35#include <dlfcn.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <sys/statvfs.h>
39#include <sys/time.h>
40
41#ifdef __solaris__
42#include <strings.h>
43#endif
44
45#if defined(__linux__) || defined(_AIX)
46#include <string.h>
47#endif
48
49#ifdef _ALLBSD_SOURCE
50#include <string.h>
51
52#define stat64 stat
53#define statvfs64 statvfs
54
55#define open64 open
56#define fstat64 fstat
57#define lstat64 lstat
58#define dirent64 dirent
59#define readdir64_r readdir_r
60#endif
61
62#include "jni.h"
63#include "jni_util.h"
64#include "jlong.h"
65
66#include "sun_nio_fs_UnixNativeDispatcher.h"
67
68/**
69 * Size of password or group entry when not available via sysconf
70 */
71#define ENT_BUF_SIZE   1024
72
73#define RESTARTABLE(_cmd, _result) do { \
74  do { \
75    _result = _cmd; \
76  } while((_result == -1) && (errno == EINTR)); \
77} while(0)
78
79#define RESTARTABLE_RETURN_PTR(_cmd, _result) do { \
80  do { \
81    _result = _cmd; \
82  } while((_result == NULL) && (errno == EINTR)); \
83} while(0)
84
85static jfieldID attrs_st_mode;
86static jfieldID attrs_st_ino;
87static jfieldID attrs_st_dev;
88static jfieldID attrs_st_rdev;
89static jfieldID attrs_st_nlink;
90static jfieldID attrs_st_uid;
91static jfieldID attrs_st_gid;
92static jfieldID attrs_st_size;
93static jfieldID attrs_st_atime_sec;
94static jfieldID attrs_st_atime_nsec;
95static jfieldID attrs_st_mtime_sec;
96static jfieldID attrs_st_mtime_nsec;
97static jfieldID attrs_st_ctime_sec;
98static jfieldID attrs_st_ctime_nsec;
99
100#ifdef _DARWIN_FEATURE_64_BIT_INODE
101static jfieldID attrs_st_birthtime_sec;
102#endif
103
104static jfieldID attrs_f_frsize;
105static jfieldID attrs_f_blocks;
106static jfieldID attrs_f_bfree;
107static jfieldID attrs_f_bavail;
108
109static jfieldID entry_name;
110static jfieldID entry_dir;
111static jfieldID entry_fstype;
112static jfieldID entry_options;
113static jfieldID entry_dev;
114
115/**
116 * System calls that may not be available at run time.
117 */
118typedef int openat64_func(int, const char *, int, ...);
119typedef int fstatat64_func(int, const char *, struct stat64 *, int);
120typedef int unlinkat_func(int, const char*, int);
121typedef int renameat_func(int, const char*, int, const char*);
122typedef int futimesat_func(int, const char *, const struct timeval *);
123typedef DIR* fdopendir_func(int);
124
125static openat64_func* my_openat64_func = NULL;
126static fstatat64_func* my_fstatat64_func = NULL;
127static unlinkat_func* my_unlinkat_func = NULL;
128static renameat_func* my_renameat_func = NULL;
129static futimesat_func* my_futimesat_func = NULL;
130static fdopendir_func* my_fdopendir_func = NULL;
131
132/**
133 * fstatat missing from glibc on Linux. Temporary workaround
134 * for x86/x64.
135 */
136#if defined(__linux__) && defined(__i386)
137#define FSTATAT64_SYSCALL_AVAILABLE
138static int fstatat64_wrapper(int dfd, const char *path,
139                             struct stat64 *statbuf, int flag)
140{
141    #ifndef __NR_fstatat64
142    #define __NR_fstatat64  300
143    #endif
144    return syscall(__NR_fstatat64, dfd, path, statbuf, flag);
145}
146#endif
147
148#if defined(__linux__) && defined(__x86_64__)
149#define FSTATAT64_SYSCALL_AVAILABLE
150static int fstatat64_wrapper(int dfd, const char *path,
151                             struct stat64 *statbuf, int flag)
152{
153    #ifndef __NR_newfstatat
154    #define __NR_newfstatat  262
155    #endif
156    return syscall(__NR_newfstatat, dfd, path, statbuf, flag);
157}
158#endif
159
160/**
161 * Call this to throw an internal UnixException when a system/library
162 * call fails
163 */
164static void throwUnixException(JNIEnv* env, int errnum) {
165    jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
166        "(I)V", errnum);
167    if (x != NULL) {
168        (*env)->Throw(env, x);
169    }
170}
171
172/**
173 * Initialization
174 */
175JNIEXPORT jint JNICALL
176Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this)
177{
178    jint capabilities = 0;
179    jclass clazz;
180
181    clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileAttributes");
182    CHECK_NULL_RETURN(clazz, 0);
183    attrs_st_mode = (*env)->GetFieldID(env, clazz, "st_mode", "I");
184    CHECK_NULL_RETURN(attrs_st_mode, 0);
185    attrs_st_ino = (*env)->GetFieldID(env, clazz, "st_ino", "J");
186    CHECK_NULL_RETURN(attrs_st_ino, 0);
187    attrs_st_dev = (*env)->GetFieldID(env, clazz, "st_dev", "J");
188    CHECK_NULL_RETURN(attrs_st_dev, 0);
189    attrs_st_rdev = (*env)->GetFieldID(env, clazz, "st_rdev", "J");
190    CHECK_NULL_RETURN(attrs_st_rdev, 0);
191    attrs_st_nlink = (*env)->GetFieldID(env, clazz, "st_nlink", "I");
192    CHECK_NULL_RETURN(attrs_st_nlink, 0);
193    attrs_st_uid = (*env)->GetFieldID(env, clazz, "st_uid", "I");
194    CHECK_NULL_RETURN(attrs_st_uid, 0);
195    attrs_st_gid = (*env)->GetFieldID(env, clazz, "st_gid", "I");
196    CHECK_NULL_RETURN(attrs_st_gid, 0);
197    attrs_st_size = (*env)->GetFieldID(env, clazz, "st_size", "J");
198    CHECK_NULL_RETURN(attrs_st_size, 0);
199    attrs_st_atime_sec = (*env)->GetFieldID(env, clazz, "st_atime_sec", "J");
200    CHECK_NULL_RETURN(attrs_st_atime_sec, 0);
201    attrs_st_atime_nsec = (*env)->GetFieldID(env, clazz, "st_atime_nsec", "J");
202    CHECK_NULL_RETURN(attrs_st_atime_nsec, 0);
203    attrs_st_mtime_sec = (*env)->GetFieldID(env, clazz, "st_mtime_sec", "J");
204    CHECK_NULL_RETURN(attrs_st_mtime_sec, 0);
205    attrs_st_mtime_nsec = (*env)->GetFieldID(env, clazz, "st_mtime_nsec", "J");
206    CHECK_NULL_RETURN(attrs_st_mtime_nsec, 0);
207    attrs_st_ctime_sec = (*env)->GetFieldID(env, clazz, "st_ctime_sec", "J");
208    CHECK_NULL_RETURN(attrs_st_ctime_sec, 0);
209    attrs_st_ctime_nsec = (*env)->GetFieldID(env, clazz, "st_ctime_nsec", "J");
210    CHECK_NULL_RETURN(attrs_st_ctime_nsec, 0);
211
212#ifdef _DARWIN_FEATURE_64_BIT_INODE
213    attrs_st_birthtime_sec = (*env)->GetFieldID(env, clazz, "st_birthtime_sec", "J");
214    CHECK_NULL_RETURN(attrs_st_birthtime_sec, 0);
215#endif
216
217    clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes");
218    CHECK_NULL_RETURN(clazz, 0);
219    attrs_f_frsize = (*env)->GetFieldID(env, clazz, "f_frsize", "J");
220    CHECK_NULL_RETURN(attrs_f_frsize, 0);
221    attrs_f_blocks = (*env)->GetFieldID(env, clazz, "f_blocks", "J");
222    CHECK_NULL_RETURN(attrs_f_blocks, 0);
223    attrs_f_bfree = (*env)->GetFieldID(env, clazz, "f_bfree", "J");
224    CHECK_NULL_RETURN(attrs_f_bfree, 0);
225    attrs_f_bavail = (*env)->GetFieldID(env, clazz, "f_bavail", "J");
226    CHECK_NULL_RETURN(attrs_f_bavail, 0);
227
228    clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
229    CHECK_NULL_RETURN(clazz, 0);
230    entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
231    CHECK_NULL_RETURN(entry_name, 0);
232    entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B");
233    CHECK_NULL_RETURN(entry_dir, 0);
234    entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B");
235    CHECK_NULL_RETURN(entry_fstype, 0);
236    entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B");
237    CHECK_NULL_RETURN(entry_options, 0);
238    entry_dev = (*env)->GetFieldID(env, clazz, "dev", "J");
239    CHECK_NULL_RETURN(entry_dev, 0);
240
241    /* system calls that might not be available at run time */
242
243#if (defined(__solaris__) && defined(_LP64)) || defined(_ALLBSD_SOURCE)
244    /* Solaris 64-bit does not have openat64/fstatat64 */
245    my_openat64_func = (openat64_func*)dlsym(RTLD_DEFAULT, "openat");
246    my_fstatat64_func = (fstatat64_func*)dlsym(RTLD_DEFAULT, "fstatat");
247#else
248    my_openat64_func = (openat64_func*) dlsym(RTLD_DEFAULT, "openat64");
249    my_fstatat64_func = (fstatat64_func*) dlsym(RTLD_DEFAULT, "fstatat64");
250#endif
251    my_unlinkat_func = (unlinkat_func*) dlsym(RTLD_DEFAULT, "unlinkat");
252    my_renameat_func = (renameat_func*) dlsym(RTLD_DEFAULT, "renameat");
253    my_futimesat_func = (futimesat_func*) dlsym(RTLD_DEFAULT, "futimesat");
254    my_fdopendir_func = (fdopendir_func*) dlsym(RTLD_DEFAULT, "fdopendir");
255
256#if defined(FSTATAT64_SYSCALL_AVAILABLE)
257    /* fstatat64 missing from glibc */
258    if (my_fstatat64_func == NULL)
259        my_fstatat64_func = (fstatat64_func*)&fstatat64_wrapper;
260#endif
261
262    /* supports futimes or futimesat */
263
264#ifdef _ALLBSD_SOURCE
265    capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FUTIMES;
266#else
267    if (my_futimesat_func != NULL)
268        capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FUTIMES;
269#endif
270
271    /* supports openat, etc. */
272
273    if (my_openat64_func != NULL &&  my_fstatat64_func != NULL &&
274        my_unlinkat_func != NULL && my_renameat_func != NULL &&
275        my_futimesat_func != NULL && my_fdopendir_func != NULL)
276    {
277        capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_OPENAT;
278    }
279
280    /* supports file birthtime */
281
282#ifdef _DARWIN_FEATURE_64_BIT_INODE
283    capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_BIRTHTIME;
284#endif
285
286    return capabilities;
287}
288
289JNIEXPORT jbyteArray JNICALL
290Java_sun_nio_fs_UnixNativeDispatcher_getcwd(JNIEnv* env, jclass this) {
291    jbyteArray result = NULL;
292    char buf[PATH_MAX+1];
293
294    /* EINTR not listed as a possible error */
295    char* cwd = getcwd(buf, sizeof(buf));
296    if (cwd == NULL) {
297        throwUnixException(env, errno);
298    } else {
299        jsize len = (jsize)strlen(buf);
300        result = (*env)->NewByteArray(env, len);
301        if (result != NULL) {
302            (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)buf);
303        }
304    }
305    return result;
306}
307
308JNIEXPORT jbyteArray
309Java_sun_nio_fs_UnixNativeDispatcher_strerror(JNIEnv* env, jclass this, jint error)
310{
311    char* msg;
312    jsize len;
313    jbyteArray bytes;
314
315#ifdef _AIX
316    /* strerror() is not thread-safe on AIX so we have to use strerror_r() */
317    char buffer[256];
318    msg = (strerror_r((int)error, buffer, 256) == 0) ? buffer : "Error while calling strerror_r";
319#else
320    msg = strerror((int)error);
321#endif
322    len = strlen(msg);
323    bytes = (*env)->NewByteArray(env, len);
324    if (bytes != NULL) {
325        (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)msg);
326    }
327    return bytes;
328}
329
330JNIEXPORT jint
331Java_sun_nio_fs_UnixNativeDispatcher_dup(JNIEnv* env, jclass this, jint fd) {
332
333    int res = -1;
334
335    RESTARTABLE(dup((int)fd), res);
336    if (res == -1) {
337        throwUnixException(env, errno);
338    }
339    return (jint)res;
340}
341
342JNIEXPORT jlong JNICALL
343Java_sun_nio_fs_UnixNativeDispatcher_fopen0(JNIEnv* env, jclass this,
344    jlong pathAddress, jlong modeAddress)
345{
346    FILE* fp = NULL;
347    const char* path = (const char*)jlong_to_ptr(pathAddress);
348    const char* mode = (const char*)jlong_to_ptr(modeAddress);
349
350    do {
351        fp = fopen(path, mode);
352    } while (fp == NULL && errno == EINTR);
353
354    if (fp == NULL) {
355        throwUnixException(env, errno);
356    }
357
358    return ptr_to_jlong(fp);
359}
360
361JNIEXPORT void JNICALL
362Java_sun_nio_fs_UnixNativeDispatcher_fclose(JNIEnv* env, jclass this, jlong stream)
363{
364    FILE* fp = jlong_to_ptr(stream);
365
366    /* NOTE: fclose() wrapper is only used with read-only streams.
367     * If it ever is used with write streams, it might be better to add
368     * RESTARTABLE(fflush(fp)) before closing, to make sure the stream
369     * is completely written even if fclose() failed.
370     */
371    if (fclose(fp) == EOF && errno != EINTR) {
372        throwUnixException(env, errno);
373    }
374}
375
376JNIEXPORT jint JNICALL
377Java_sun_nio_fs_UnixNativeDispatcher_open0(JNIEnv* env, jclass this,
378    jlong pathAddress, jint oflags, jint mode)
379{
380    jint fd;
381    const char* path = (const char*)jlong_to_ptr(pathAddress);
382
383    RESTARTABLE(open64(path, (int)oflags, (mode_t)mode), fd);
384    if (fd == -1) {
385        throwUnixException(env, errno);
386    }
387    return fd;
388}
389
390JNIEXPORT jint JNICALL
391Java_sun_nio_fs_UnixNativeDispatcher_openat0(JNIEnv* env, jclass this, jint dfd,
392    jlong pathAddress, jint oflags, jint mode)
393{
394    jint fd;
395    const char* path = (const char*)jlong_to_ptr(pathAddress);
396
397    if (my_openat64_func == NULL) {
398        JNU_ThrowInternalError(env, "should not reach here");
399        return -1;
400    }
401
402    RESTARTABLE((*my_openat64_func)(dfd, path, (int)oflags, (mode_t)mode), fd);
403    if (fd == -1) {
404        throwUnixException(env, errno);
405    }
406    return fd;
407}
408
409JNIEXPORT void JNICALL
410Java_sun_nio_fs_UnixNativeDispatcher_close(JNIEnv* env, jclass this, jint fd) {
411    int err;
412    /* TDB - need to decide if EIO and other errors should cause exception */
413    RESTARTABLE(close((int)fd), err);
414}
415
416JNIEXPORT jint JNICALL
417Java_sun_nio_fs_UnixNativeDispatcher_read(JNIEnv* env, jclass this, jint fd,
418    jlong address, jint nbytes)
419{
420    ssize_t n;
421    void* bufp = jlong_to_ptr(address);
422    RESTARTABLE(read((int)fd, bufp, (size_t)nbytes), n);
423    if (n == -1) {
424        throwUnixException(env, errno);
425    }
426    return (jint)n;
427}
428
429JNIEXPORT jint JNICALL
430Java_sun_nio_fs_UnixNativeDispatcher_write(JNIEnv* env, jclass this, jint fd,
431    jlong address, jint nbytes)
432{
433    ssize_t n;
434    void* bufp = jlong_to_ptr(address);
435    RESTARTABLE(write((int)fd, bufp, (size_t)nbytes), n);
436    if (n == -1) {
437        throwUnixException(env, errno);
438    }
439    return (jint)n;
440}
441
442/**
443 * Copy stat64 members into sun.nio.fs.UnixFileAttributes
444 */
445static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
446    (*env)->SetIntField(env, attrs, attrs_st_mode, (jint)buf->st_mode);
447    (*env)->SetLongField(env, attrs, attrs_st_ino, (jlong)buf->st_ino);
448    (*env)->SetLongField(env, attrs, attrs_st_dev, (jlong)buf->st_dev);
449    (*env)->SetLongField(env, attrs, attrs_st_rdev, (jlong)buf->st_rdev);
450    (*env)->SetIntField(env, attrs, attrs_st_nlink, (jint)buf->st_nlink);
451    (*env)->SetIntField(env, attrs, attrs_st_uid, (jint)buf->st_uid);
452    (*env)->SetIntField(env, attrs, attrs_st_gid, (jint)buf->st_gid);
453    (*env)->SetLongField(env, attrs, attrs_st_size, (jlong)buf->st_size);
454    (*env)->SetLongField(env, attrs, attrs_st_atime_sec, (jlong)buf->st_atime);
455    (*env)->SetLongField(env, attrs, attrs_st_mtime_sec, (jlong)buf->st_mtime);
456    (*env)->SetLongField(env, attrs, attrs_st_ctime_sec, (jlong)buf->st_ctime);
457
458#ifdef _DARWIN_FEATURE_64_BIT_INODE
459    (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->st_birthtime);
460#endif
461
462#if (_POSIX_C_SOURCE >= 200809L) || defined(__solaris__)
463    (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atim.tv_nsec);
464    (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtim.tv_nsec);
465    (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->st_ctim.tv_nsec);
466#endif
467}
468
469JNIEXPORT void JNICALL
470Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this,
471    jlong pathAddress, jobject attrs)
472{
473    int err;
474    struct stat64 buf;
475    const char* path = (const char*)jlong_to_ptr(pathAddress);
476
477    RESTARTABLE(stat64(path, &buf), err);
478    if (err == -1) {
479        throwUnixException(env, errno);
480    } else {
481        prepAttributes(env, &buf, attrs);
482    }
483}
484
485JNIEXPORT void JNICALL
486Java_sun_nio_fs_UnixNativeDispatcher_lstat0(JNIEnv* env, jclass this,
487    jlong pathAddress, jobject attrs)
488{
489    int err;
490    struct stat64 buf;
491    const char* path = (const char*)jlong_to_ptr(pathAddress);
492
493    RESTARTABLE(lstat64(path, &buf), err);
494    if (err == -1) {
495        throwUnixException(env, errno);
496    } else {
497        prepAttributes(env, &buf, attrs);
498    }
499}
500
501JNIEXPORT void JNICALL
502Java_sun_nio_fs_UnixNativeDispatcher_fstat(JNIEnv* env, jclass this, jint fd,
503    jobject attrs)
504{
505    int err;
506    struct stat64 buf;
507
508    RESTARTABLE(fstat64((int)fd, &buf), err);
509    if (err == -1) {
510        throwUnixException(env, errno);
511    } else {
512        prepAttributes(env, &buf, attrs);
513    }
514}
515
516JNIEXPORT void JNICALL
517Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd,
518    jlong pathAddress, jint flag, jobject attrs)
519{
520    int err;
521    struct stat64 buf;
522    const char* path = (const char*)jlong_to_ptr(pathAddress);
523
524    if (my_fstatat64_func == NULL) {
525        JNU_ThrowInternalError(env, "should not reach here");
526        return;
527    }
528    RESTARTABLE((*my_fstatat64_func)((int)dfd, path, &buf, (int)flag), err);
529    if (err == -1) {
530        throwUnixException(env, errno);
531    } else {
532        prepAttributes(env, &buf, attrs);
533    }
534}
535
536JNIEXPORT void JNICALL
537Java_sun_nio_fs_UnixNativeDispatcher_chmod0(JNIEnv* env, jclass this,
538    jlong pathAddress, jint mode)
539{
540    int err;
541    const char* path = (const char*)jlong_to_ptr(pathAddress);
542
543    RESTARTABLE(chmod(path, (mode_t)mode), err);
544    if (err == -1) {
545        throwUnixException(env, errno);
546    }
547}
548
549JNIEXPORT void JNICALL
550Java_sun_nio_fs_UnixNativeDispatcher_fchmod(JNIEnv* env, jclass this, jint filedes,
551    jint mode)
552{
553    int err;
554
555    RESTARTABLE(fchmod((int)filedes, (mode_t)mode), err);
556    if (err == -1) {
557        throwUnixException(env, errno);
558    }
559}
560
561
562JNIEXPORT void JNICALL
563Java_sun_nio_fs_UnixNativeDispatcher_chown0(JNIEnv* env, jclass this,
564    jlong pathAddress, jint uid, jint gid)
565{
566    int err;
567    const char* path = (const char*)jlong_to_ptr(pathAddress);
568
569    RESTARTABLE(chown(path, (uid_t)uid, (gid_t)gid), err);
570    if (err == -1) {
571        throwUnixException(env, errno);
572    }
573}
574
575JNIEXPORT void JNICALL
576Java_sun_nio_fs_UnixNativeDispatcher_lchown0(JNIEnv* env, jclass this, jlong pathAddress, jint uid, jint gid)
577{
578    int err;
579    const char* path = (const char*)jlong_to_ptr(pathAddress);
580
581    RESTARTABLE(lchown(path, (uid_t)uid, (gid_t)gid), err);
582    if (err == -1) {
583        throwUnixException(env, errno);
584    }
585}
586
587JNIEXPORT void JNICALL
588Java_sun_nio_fs_UnixNativeDispatcher_fchown(JNIEnv* env, jclass this, jint filedes, jint uid, jint gid)
589{
590    int err;
591
592    RESTARTABLE(fchown(filedes, (uid_t)uid, (gid_t)gid), err);
593    if (err == -1) {
594        throwUnixException(env, errno);
595    }
596}
597
598JNIEXPORT void JNICALL
599Java_sun_nio_fs_UnixNativeDispatcher_utimes0(JNIEnv* env, jclass this,
600    jlong pathAddress, jlong accessTime, jlong modificationTime)
601{
602    int err;
603    struct timeval times[2];
604    const char* path = (const char*)jlong_to_ptr(pathAddress);
605
606    times[0].tv_sec = accessTime / 1000000;
607    times[0].tv_usec = accessTime % 1000000;
608
609    times[1].tv_sec = modificationTime / 1000000;
610    times[1].tv_usec = modificationTime % 1000000;
611
612    RESTARTABLE(utimes(path, &times[0]), err);
613    if (err == -1) {
614        throwUnixException(env, errno);
615    }
616}
617
618JNIEXPORT void JNICALL
619Java_sun_nio_fs_UnixNativeDispatcher_futimes(JNIEnv* env, jclass this, jint filedes,
620    jlong accessTime, jlong modificationTime)
621{
622    struct timeval times[2];
623    int err = 0;
624
625    times[0].tv_sec = accessTime / 1000000;
626    times[0].tv_usec = accessTime % 1000000;
627
628    times[1].tv_sec = modificationTime / 1000000;
629    times[1].tv_usec = modificationTime % 1000000;
630
631#ifdef _ALLBSD_SOURCE
632    RESTARTABLE(futimes(filedes, &times[0]), err);
633#else
634    if (my_futimesat_func == NULL) {
635        JNU_ThrowInternalError(env, "my_ftimesat_func is NULL");
636        return;
637    }
638    RESTARTABLE((*my_futimesat_func)(filedes, NULL, &times[0]), err);
639#endif
640    if (err == -1) {
641        throwUnixException(env, errno);
642    }
643}
644
645JNIEXPORT jlong JNICALL
646Java_sun_nio_fs_UnixNativeDispatcher_opendir0(JNIEnv* env, jclass this,
647    jlong pathAddress)
648{
649    DIR* dir;
650    const char* path = (const char*)jlong_to_ptr(pathAddress);
651
652    /* EINTR not listed as a possible error */
653    dir = opendir(path);
654    if (dir == NULL) {
655        throwUnixException(env, errno);
656    }
657    return ptr_to_jlong(dir);
658}
659
660JNIEXPORT jlong JNICALL
661Java_sun_nio_fs_UnixNativeDispatcher_fdopendir(JNIEnv* env, jclass this, int dfd) {
662    DIR* dir;
663
664    if (my_fdopendir_func == NULL) {
665        JNU_ThrowInternalError(env, "should not reach here");
666        return (jlong)-1;
667    }
668
669    /* EINTR not listed as a possible error */
670    dir = (*my_fdopendir_func)((int)dfd);
671    if (dir == NULL) {
672        throwUnixException(env, errno);
673    }
674    return ptr_to_jlong(dir);
675}
676
677JNIEXPORT void JNICALL
678Java_sun_nio_fs_UnixNativeDispatcher_closedir(JNIEnv* env, jclass this, jlong dir) {
679    DIR* dirp = jlong_to_ptr(dir);
680
681    if (closedir(dirp) == -1 && errno != EINTR) {
682        throwUnixException(env, errno);
683    }
684}
685
686JNIEXPORT jbyteArray JNICALL
687Java_sun_nio_fs_UnixNativeDispatcher_readdir(JNIEnv* env, jclass this, jlong value) {
688    struct dirent64* result;
689    struct {
690        struct dirent64 buf;
691        char name_extra[PATH_MAX + 1 - sizeof result->d_name];
692    } entry;
693    struct dirent64* ptr = &entry.buf;
694    int res;
695    DIR* dirp = jlong_to_ptr(value);
696
697    /* EINTR not listed as a possible error */
698    /* TDB: reentrant version probably not required here */
699    res = readdir64_r(dirp, ptr, &result);
700
701#ifdef _AIX
702    /* On AIX, readdir_r() returns EBADF (i.e. '9') and sets 'result' to NULL for the */
703    /* directory stream end. Otherwise, 'errno' will contain the error code. */
704    if (res != 0) {
705        res = (result == NULL && res == EBADF) ? 0 : errno;
706    }
707#endif
708
709    if (res != 0) {
710        throwUnixException(env, res);
711        return NULL;
712    } else {
713        if (result == NULL) {
714            return NULL;
715        } else {
716            jsize len = strlen(ptr->d_name);
717            jbyteArray bytes = (*env)->NewByteArray(env, len);
718            if (bytes != NULL) {
719                (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)(ptr->d_name));
720            }
721            return bytes;
722        }
723    }
724}
725
726JNIEXPORT void JNICALL
727Java_sun_nio_fs_UnixNativeDispatcher_mkdir0(JNIEnv* env, jclass this,
728    jlong pathAddress, jint mode)
729{
730    const char* path = (const char*)jlong_to_ptr(pathAddress);
731
732    /* EINTR not listed as a possible error */
733    if (mkdir(path, (mode_t)mode) == -1) {
734        throwUnixException(env, errno);
735    }
736}
737
738JNIEXPORT void JNICALL
739Java_sun_nio_fs_UnixNativeDispatcher_rmdir0(JNIEnv* env, jclass this,
740    jlong pathAddress)
741{
742    const char* path = (const char*)jlong_to_ptr(pathAddress);
743
744    /* EINTR not listed as a possible error */
745    if (rmdir(path) == -1) {
746        throwUnixException(env, errno);
747    }
748}
749
750JNIEXPORT void JNICALL
751Java_sun_nio_fs_UnixNativeDispatcher_link0(JNIEnv* env, jclass this,
752    jlong existingAddress, jlong newAddress)
753{
754    int err;
755    const char* existing = (const char*)jlong_to_ptr(existingAddress);
756    const char* newname = (const char*)jlong_to_ptr(newAddress);
757
758    RESTARTABLE(link(existing, newname), err);
759    if (err == -1) {
760        throwUnixException(env, errno);
761    }
762}
763
764
765JNIEXPORT void JNICALL
766Java_sun_nio_fs_UnixNativeDispatcher_unlink0(JNIEnv* env, jclass this,
767    jlong pathAddress)
768{
769    const char* path = (const char*)jlong_to_ptr(pathAddress);
770
771    /* EINTR not listed as a possible error */
772    if (unlink(path) == -1) {
773        throwUnixException(env, errno);
774    }
775}
776
777JNIEXPORT void JNICALL
778Java_sun_nio_fs_UnixNativeDispatcher_unlinkat0(JNIEnv* env, jclass this, jint dfd,
779                                               jlong pathAddress, jint flags)
780{
781    const char* path = (const char*)jlong_to_ptr(pathAddress);
782
783    if (my_unlinkat_func == NULL) {
784        JNU_ThrowInternalError(env, "should not reach here");
785        return;
786    }
787
788    /* EINTR not listed as a possible error */
789    if ((*my_unlinkat_func)((int)dfd, path, (int)flags) == -1) {
790        throwUnixException(env, errno);
791    }
792}
793
794JNIEXPORT void JNICALL
795Java_sun_nio_fs_UnixNativeDispatcher_rename0(JNIEnv* env, jclass this,
796    jlong fromAddress, jlong toAddress)
797{
798    const char* from = (const char*)jlong_to_ptr(fromAddress);
799    const char* to = (const char*)jlong_to_ptr(toAddress);
800
801    /* EINTR not listed as a possible error */
802    if (rename(from, to) == -1) {
803        throwUnixException(env, errno);
804    }
805}
806
807JNIEXPORT void JNICALL
808Java_sun_nio_fs_UnixNativeDispatcher_renameat0(JNIEnv* env, jclass this,
809    jint fromfd, jlong fromAddress, jint tofd, jlong toAddress)
810{
811    const char* from = (const char*)jlong_to_ptr(fromAddress);
812    const char* to = (const char*)jlong_to_ptr(toAddress);
813
814    if (my_renameat_func == NULL) {
815        JNU_ThrowInternalError(env, "should not reach here");
816        return;
817    }
818
819    /* EINTR not listed as a possible error */
820    if ((*my_renameat_func)((int)fromfd, from, (int)tofd, to) == -1) {
821        throwUnixException(env, errno);
822    }
823}
824
825JNIEXPORT void JNICALL
826Java_sun_nio_fs_UnixNativeDispatcher_symlink0(JNIEnv* env, jclass this,
827    jlong targetAddress, jlong linkAddress)
828{
829    const char* target = (const char*)jlong_to_ptr(targetAddress);
830    const char* link = (const char*)jlong_to_ptr(linkAddress);
831
832    /* EINTR not listed as a possible error */
833    if (symlink(target, link) == -1) {
834        throwUnixException(env, errno);
835    }
836}
837
838JNIEXPORT jbyteArray JNICALL
839Java_sun_nio_fs_UnixNativeDispatcher_readlink0(JNIEnv* env, jclass this,
840    jlong pathAddress)
841{
842    jbyteArray result = NULL;
843    char target[PATH_MAX+1];
844    const char* path = (const char*)jlong_to_ptr(pathAddress);
845
846    /* EINTR not listed as a possible error */
847    int n = readlink(path, target, sizeof(target));
848    if (n == -1) {
849        throwUnixException(env, errno);
850    } else {
851        jsize len;
852        if (n == sizeof(target)) {
853            n--;
854        }
855        target[n] = '\0';
856        len = (jsize)strlen(target);
857        result = (*env)->NewByteArray(env, len);
858        if (result != NULL) {
859            (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)target);
860        }
861    }
862    return result;
863}
864
865JNIEXPORT jbyteArray JNICALL
866Java_sun_nio_fs_UnixNativeDispatcher_realpath0(JNIEnv* env, jclass this,
867    jlong pathAddress)
868{
869    jbyteArray result = NULL;
870    char resolved[PATH_MAX+1];
871    const char* path = (const char*)jlong_to_ptr(pathAddress);
872
873    /* EINTR not listed as a possible error */
874    if (realpath(path, resolved) == NULL) {
875        throwUnixException(env, errno);
876    } else {
877        jsize len = (jsize)strlen(resolved);
878        result = (*env)->NewByteArray(env, len);
879        if (result != NULL) {
880            (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)resolved);
881        }
882    }
883    return result;
884}
885
886JNIEXPORT void JNICALL
887Java_sun_nio_fs_UnixNativeDispatcher_access0(JNIEnv* env, jclass this,
888    jlong pathAddress, jint amode)
889{
890    int err;
891    const char* path = (const char*)jlong_to_ptr(pathAddress);
892
893    RESTARTABLE(access(path, (int)amode), err);
894    if (err == -1) {
895        throwUnixException(env, errno);
896    }
897}
898
899JNIEXPORT void JNICALL
900Java_sun_nio_fs_UnixNativeDispatcher_statvfs0(JNIEnv* env, jclass this,
901    jlong pathAddress, jobject attrs)
902{
903    int err;
904    struct statvfs64 buf;
905    const char* path = (const char*)jlong_to_ptr(pathAddress);
906
907
908    RESTARTABLE(statvfs64(path, &buf), err);
909    if (err == -1) {
910        throwUnixException(env, errno);
911    } else {
912#ifdef _AIX
913        /* AIX returns ULONG_MAX in buf.f_blocks for the /proc file system. */
914        /* This is too big for a Java signed long and fools various tests.  */
915        if (buf.f_blocks == ULONG_MAX) {
916            buf.f_blocks = 0;
917        }
918        /* The number of free or available blocks can never exceed the total number of blocks */
919        if (buf.f_blocks == 0) {
920            buf.f_bfree = 0;
921            buf.f_bavail = 0;
922        }
923#endif
924        (*env)->SetLongField(env, attrs, attrs_f_frsize, long_to_jlong(buf.f_frsize));
925        (*env)->SetLongField(env, attrs, attrs_f_blocks, long_to_jlong(buf.f_blocks));
926        (*env)->SetLongField(env, attrs, attrs_f_bfree,  long_to_jlong(buf.f_bfree));
927        (*env)->SetLongField(env, attrs, attrs_f_bavail, long_to_jlong(buf.f_bavail));
928    }
929}
930
931JNIEXPORT jlong JNICALL
932Java_sun_nio_fs_UnixNativeDispatcher_pathconf0(JNIEnv* env, jclass this,
933    jlong pathAddress, jint name)
934{
935    long err;
936    const char* path = (const char*)jlong_to_ptr(pathAddress);
937
938    err = pathconf(path, (int)name);
939    if (err == -1) {
940        throwUnixException(env, errno);
941    }
942    return (jlong)err;
943}
944
945JNIEXPORT jlong JNICALL
946Java_sun_nio_fs_UnixNativeDispatcher_fpathconf(JNIEnv* env, jclass this,
947    jint fd, jint name)
948{
949    long err;
950
951    err = fpathconf((int)fd, (int)name);
952    if (err == -1) {
953        throwUnixException(env, errno);
954    }
955    return (jlong)err;
956}
957
958JNIEXPORT void JNICALL
959Java_sun_nio_fs_UnixNativeDispatcher_mknod0(JNIEnv* env, jclass this,
960    jlong pathAddress, jint mode, jlong dev)
961{
962    int err;
963    const char* path = (const char*)jlong_to_ptr(pathAddress);
964
965    RESTARTABLE(mknod(path, (mode_t)mode, (dev_t)dev), err);
966    if (err == -1) {
967        throwUnixException(env, errno);
968    }
969}
970
971JNIEXPORT jbyteArray JNICALL
972Java_sun_nio_fs_UnixNativeDispatcher_getpwuid(JNIEnv* env, jclass this, jint uid)
973{
974    jbyteArray result = NULL;
975    int buflen;
976    char* pwbuf;
977
978    /* allocate buffer for password record */
979    buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
980    if (buflen == -1)
981        buflen = ENT_BUF_SIZE;
982    pwbuf = (char*)malloc(buflen);
983    if (pwbuf == NULL) {
984        JNU_ThrowOutOfMemoryError(env, "native heap");
985    } else {
986        struct passwd pwent;
987        struct passwd* p = NULL;
988        int res = 0;
989
990        errno = 0;
991        #ifdef __solaris__
992            RESTARTABLE_RETURN_PTR(getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen), p);
993        #else
994            RESTARTABLE(getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen, &p), res);
995        #endif
996
997        if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') {
998            /* not found or error */
999            if (errno == 0)
1000                errno = ENOENT;
1001            throwUnixException(env, errno);
1002        } else {
1003            jsize len = strlen(p->pw_name);
1004            result = (*env)->NewByteArray(env, len);
1005            if (result != NULL) {
1006                (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(p->pw_name));
1007            }
1008        }
1009        free(pwbuf);
1010    }
1011
1012    return result;
1013}
1014
1015
1016JNIEXPORT jbyteArray JNICALL
1017Java_sun_nio_fs_UnixNativeDispatcher_getgrgid(JNIEnv* env, jclass this, jint gid)
1018{
1019    jbyteArray result = NULL;
1020    int buflen;
1021    int retry;
1022
1023    /* initial size of buffer for group record */
1024    buflen = (int)sysconf(_SC_GETGR_R_SIZE_MAX);
1025    if (buflen == -1)
1026        buflen = ENT_BUF_SIZE;
1027
1028    do {
1029        struct group grent;
1030        struct group* g = NULL;
1031        int res = 0;
1032
1033        char* grbuf = (char*)malloc(buflen);
1034        if (grbuf == NULL) {
1035            JNU_ThrowOutOfMemoryError(env, "native heap");
1036            return NULL;
1037        }
1038
1039        errno = 0;
1040        #ifdef __solaris__
1041            RESTARTABLE_RETURN_PTR(getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen), g);
1042        #else
1043            RESTARTABLE(getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen, &g), res);
1044        #endif
1045
1046        retry = 0;
1047        if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') {
1048            /* not found or error */
1049            if (errno == ERANGE) {
1050                /* insufficient buffer size so need larger buffer */
1051                buflen += ENT_BUF_SIZE;
1052                retry = 1;
1053            } else {
1054                if (errno == 0)
1055                    errno = ENOENT;
1056                throwUnixException(env, errno);
1057            }
1058        } else {
1059            jsize len = strlen(g->gr_name);
1060            result = (*env)->NewByteArray(env, len);
1061            if (result != NULL) {
1062                (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(g->gr_name));
1063            }
1064        }
1065
1066        free(grbuf);
1067
1068    } while (retry);
1069
1070    return result;
1071}
1072
1073JNIEXPORT jint JNICALL
1074Java_sun_nio_fs_UnixNativeDispatcher_getpwnam0(JNIEnv* env, jclass this,
1075    jlong nameAddress)
1076{
1077    jint uid = -1;
1078    int buflen;
1079    char* pwbuf;
1080
1081    /* allocate buffer for password record */
1082    buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
1083    if (buflen == -1)
1084        buflen = ENT_BUF_SIZE;
1085    pwbuf = (char*)malloc(buflen);
1086    if (pwbuf == NULL) {
1087        JNU_ThrowOutOfMemoryError(env, "native heap");
1088    } else {
1089        struct passwd pwent;
1090        struct passwd* p = NULL;
1091        int res = 0;
1092        const char* name = (const char*)jlong_to_ptr(nameAddress);
1093
1094        errno = 0;
1095        #ifdef __solaris__
1096            RESTARTABLE_RETURN_PTR(getpwnam_r(name, &pwent, pwbuf, (size_t)buflen), p);
1097        #else
1098            RESTARTABLE(getpwnam_r(name, &pwent, pwbuf, (size_t)buflen, &p), res);
1099        #endif
1100
1101        if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') {
1102            /* not found or error */
1103            if (errno != 0 && errno != ENOENT && errno != ESRCH)
1104                throwUnixException(env, errno);
1105        } else {
1106            uid = p->pw_uid;
1107        }
1108        free(pwbuf);
1109    }
1110
1111    return uid;
1112}
1113
1114JNIEXPORT jint JNICALL
1115Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0(JNIEnv* env, jclass this,
1116    jlong nameAddress)
1117{
1118    jint gid = -1;
1119    int buflen, retry;
1120
1121    /* initial size of buffer for group record */
1122    buflen = (int)sysconf(_SC_GETGR_R_SIZE_MAX);
1123    if (buflen == -1)
1124        buflen = ENT_BUF_SIZE;
1125
1126    do {
1127        struct group grent;
1128        struct group* g = NULL;
1129        int res = 0;
1130        char *grbuf;
1131        const char* name = (const char*)jlong_to_ptr(nameAddress);
1132
1133        grbuf = (char*)malloc(buflen);
1134        if (grbuf == NULL) {
1135            JNU_ThrowOutOfMemoryError(env, "native heap");
1136            return -1;
1137        }
1138
1139        errno = 0;
1140        #ifdef __solaris__
1141            RESTARTABLE_RETURN_PTR(getgrnam_r(name, &grent, grbuf, (size_t)buflen), g);
1142        #else
1143            RESTARTABLE(getgrnam_r(name, &grent, grbuf, (size_t)buflen, &g), res);
1144        #endif
1145
1146        retry = 0;
1147        if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') {
1148            /* not found or error */
1149            if (errno != 0 && errno != ENOENT && errno != ESRCH) {
1150                if (errno == ERANGE) {
1151                    /* insufficient buffer size so need larger buffer */
1152                    buflen += ENT_BUF_SIZE;
1153                    retry = 1;
1154                } else {
1155                    throwUnixException(env, errno);
1156                }
1157            }
1158        } else {
1159            gid = g->gr_gid;
1160        }
1161
1162        free(grbuf);
1163
1164    } while (retry);
1165
1166    return gid;
1167}
1168