1 /*
  2  * Copyright (c) 2008, 2019, 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 package sun.nio.fs;
 27 
 28 import jdk.internal.misc.Blocker;
 29 
 30 /**
 31  * Unix system and library calls.
 32  */
 33 
 34 class UnixNativeDispatcher {
 35     protected UnixNativeDispatcher() { }
 36 
 37     // returns a NativeBuffer containing the given path
 38     static NativeBuffer copyToNativeBuffer(UnixPath path) {
 39         byte[] cstr = path.getByteArrayForSysCalls();
 40         int size = cstr.length + 1;
 41         NativeBuffer buffer = NativeBuffers.getNativeBufferFromCache(size);
 42         if (buffer == null) {
 43             buffer = NativeBuffers.allocNativeBuffer(size);
 44         } else {
 45             // buffer already contains the path
 46             if (buffer.owner() == path)
 47                 return buffer;
 48         }
 49         NativeBuffers.copyCStringToNativeBuffer(cstr, buffer);
 50         buffer.setOwner(path);
 51         return buffer;
 52     }
 53 
 54     /**
 55      * char *getcwd(char *buf, size_t size);
 56      */
 57     static native byte[] getcwd();
 58 
 59     /**
 60      * int dup(int filedes)
 61      */
 62     static native int dup(int filedes) throws UnixException;
 63 
 64     /**
 65      * int open(const char* path, int oflag, mode_t mode)
 66      */
 67     static int open(UnixPath path, int flags, int mode) throws UnixException {
 68         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
 69             if (Thread.currentThread().isVirtual()) {
 70                 return Blocker.managedBlock(() -> open0(buffer.address(), flags, mode));
 71             } else {
 72                 return open0(buffer.address(), flags, mode);
 73             }
 74         }
 75     }
 76     private static native int open0(long pathAddress, int flags, int mode)
 77         throws UnixException;
 78 
 79     /**
 80      * int openat(int dfd, const char* path, int oflag, mode_t mode)
 81      */
 82     static int openat(int dfd, byte[] path, int flags, int mode) throws UnixException {
 83         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(path)) {
 84             if (Thread.currentThread().isVirtual()) {
 85                 return Blocker.managedBlock(() -> openat0(dfd, buffer.address(), flags, mode));
 86             } else {
 87                 return openat0(dfd, buffer.address(), flags, mode);
 88             }
 89         }
 90     }
 91     private static native int openat0(int dfd, long pathAddress, int flags, int mode)
 92         throws UnixException;
 93 
 94     /**
 95      * close(int filedes). If fd is -1 this is a no-op.
 96      */
 97     static void close(int fd) {
 98         if (fd != -1) {
 99             close0(fd);
100         }
101     }
102     private static native void close0(int fd);
103 
104     /**
105      * void rewind(FILE* stream);
106      */
107     static native void rewind(long stream) throws UnixException;
108 
109     /**
110      * ssize_t getline(char **lineptr, size_t *n, FILE *stream);
111      */
112     static native int getlinelen(long stream) throws UnixException;
113 
114     /**
115      * link(const char* existing, const char* new)
116      */
117     static void link(UnixPath existing, UnixPath newfile) throws UnixException {
118         try (NativeBuffer existingBuffer = copyToNativeBuffer(existing);
119              NativeBuffer newBuffer = copyToNativeBuffer(newfile)) {
120             long existingAddress = existingBuffer.address();
121             long newAddress = newBuffer.address();
122             if (Thread.currentThread().isVirtual()) {
123                 Blocker.managedBlock(() -> link0(existingAddress, newAddress));
124             } else {
125                 link0(existingAddress, newAddress);
126             }
127         }
128     }
129     private static native void link0(long existingAddress, long newAddress)
130         throws UnixException;
131 
132     /**
133      * unlink(const char* path)
134      */
135     static void unlink(UnixPath path) throws UnixException {
136         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
137             if (Thread.currentThread().isVirtual()) {
138                 Blocker.managedBlock(() -> unlink0(buffer.address()));
139             } else {
140                 unlink0(buffer.address());
141             }
142         }
143     }
144     private static native void unlink0(long pathAddress) throws UnixException;
145 
146     /**
147      * unlinkat(int dfd, const char* path, int flag)
148      */
149     static void unlinkat(int dfd, byte[] path, int flag) throws UnixException {
150         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(path)) {
151             if (Thread.currentThread().isVirtual()) {
152                 Blocker.managedBlock(() -> unlinkat0(dfd, buffer.address(), flag));
153             } else {
154                 unlinkat0(dfd, buffer.address(), flag);
155             }
156         }
157     }
158     private static native void unlinkat0(int dfd, long pathAddress, int flag)
159         throws UnixException;
160 
161     /**
162      * mknod(const char* path, mode_t mode, dev_t dev)
163      */
164     static void mknod(UnixPath path, int mode, long dev) throws UnixException {
165         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
166             if (Thread.currentThread().isVirtual()) {
167                 Blocker.managedBlock(() -> mknod0(buffer.address(), mode, dev));
168             } else {
169                 mknod0(buffer.address(), mode, dev);
170             }
171         }
172     }
173     private static native void mknod0(long pathAddress, int mode, long dev)
174         throws UnixException;
175 
176     /**
177      *  rename(const char* old, const char* new)
178      */
179     static void rename(UnixPath from, UnixPath to) throws UnixException {
180         try (NativeBuffer fromBuffer = copyToNativeBuffer(from);
181              NativeBuffer toBuffer = copyToNativeBuffer(to)) {
182             long fromAddress = fromBuffer.address();
183             long toAddress = toBuffer.address();
184             if (Thread.currentThread().isVirtual()) {
185                 Blocker.managedBlock(() -> rename0(fromAddress, toAddress));
186             } else {
187                 rename0(fromAddress, toAddress);
188             }
189         }
190     }
191     private static native void rename0(long fromAddress, long toAddress)
192         throws UnixException;
193 
194     /**
195      *  renameat(int fromfd, const char* old, int tofd, const char* new)
196      */
197     static void renameat(int fromfd, byte[] from, int tofd, byte[] to) throws UnixException {
198         try (NativeBuffer fromBuffer = NativeBuffers.asNativeBuffer(from);
199              NativeBuffer toBuffer = NativeBuffers.asNativeBuffer(to)) {
200             long fromAddress = fromBuffer.address();
201             long toAddress = toBuffer.address();
202             if (Thread.currentThread().isVirtual()) {
203                 Blocker.managedBlock(() -> renameat0(fromfd, fromAddress, tofd, toAddress));
204             } else {
205                 renameat0(fromfd, fromAddress, tofd, toAddress);
206             }
207         }
208     }
209     private static native void renameat0(int fromfd, long fromAddress, int tofd, long toAddress)
210         throws UnixException;
211 
212     /**
213      * mkdir(const char* path, mode_t mode)
214      */
215     static void mkdir(UnixPath path, int mode) throws UnixException {
216         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
217             if (Thread.currentThread().isVirtual()) {
218                 Blocker.managedBlock(() -> mkdir0(buffer.address(), mode));
219             } else {
220                 mkdir0(buffer.address(), mode);
221             }
222         }
223     }
224     private static native void mkdir0(long pathAddress, int mode) throws UnixException;
225 
226     /**
227      * rmdir(const char* path)
228      */
229     static void rmdir(UnixPath path) throws UnixException {
230         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
231             if (Thread.currentThread().isVirtual()) {
232                 Blocker.managedBlock(() -> rmdir0(buffer.address()));
233             } else {
234                 rmdir0(buffer.address());
235             }
236         }
237     }
238     private static native void rmdir0(long pathAddress) throws UnixException;
239 
240     /**
241      * readlink(const char* path, char* buf, size_t bufsize)
242      *
243      * @return  link target
244      */
245     static byte[] readlink(UnixPath path) throws UnixException {
246         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
247             if (Thread.currentThread().isVirtual()) {
248                 return Blocker.managedBlock(() -> readlink0(buffer.address()));
249             } else {
250                 return readlink0(buffer.address());
251             }
252         }
253     }
254     private static native byte[] readlink0(long pathAddress) throws UnixException;
255 
256     /**
257      * realpath(const char* path, char* resolved_name)
258      *
259      * @return  resolved path
260      */
261     static byte[] realpath(UnixPath path) throws UnixException {
262         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
263             if (Thread.currentThread().isVirtual()) {
264                 return Blocker.managedBlock(() -> realpath0(buffer.address()));
265             } else {
266                 return realpath0(buffer.address());
267             }
268         }
269     }
270     private static native byte[] realpath0(long pathAddress) throws UnixException;
271 
272     /**
273      * symlink(const char* name1, const char* name2)
274      */
275     static void symlink(byte[] name1, UnixPath name2) throws UnixException {
276         try (NativeBuffer targetBuffer = NativeBuffers.asNativeBuffer(name1);
277              NativeBuffer linkBuffer = copyToNativeBuffer(name2)) {
278             long targetAddress = targetBuffer.address();
279             long linkAddress = linkBuffer.address();
280             if (Thread.currentThread().isVirtual()) {
281                 Blocker.managedBlock(() -> symlink0(targetAddress, linkAddress));
282             } else {
283                 symlink0(targetAddress, linkAddress);
284             }
285         }
286     }
287     private static native void symlink0(long name1, long name2)
288         throws UnixException;
289 
290     /**
291      * stat(const char* path, struct stat* buf)
292      */
293     static void stat(UnixPath path, UnixFileAttributes attrs) throws UnixException {
294         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
295             if (Thread.currentThread().isVirtual()) {
296                 Blocker.managedBlock(() -> stat0(buffer.address(), attrs));
297             } else {
298                 stat0(buffer.address(), attrs);
299             }
300         }
301     }
302     private static native void stat0(long pathAddress, UnixFileAttributes attrs)
303         throws UnixException;
304 
305 
306     /**
307      * stat(const char* path, struct stat* buf)
308      *
309      * @return st_mode (file type and mode) or 0 if an error occurs.
310      */
311     static int stat(UnixPath path) {
312         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
313             if (Thread.currentThread().isVirtual()) {
314                 return Blocker.managedBlock(() -> stat1(buffer.address()));
315             } else {
316                 return stat1(buffer.address());
317             }
318         }
319     }
320     private static native int stat1(long pathAddress);
321 
322 
323     /**
324      * lstat(const char* path, struct stat* buf)
325      */
326     static void lstat(UnixPath path, UnixFileAttributes attrs) throws UnixException {
327         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
328             if (Thread.currentThread().isVirtual()) {
329                 Blocker.managedBlock(() -> lstat0(buffer.address(), attrs));
330             } else {
331                 lstat0(buffer.address(), attrs);
332             }
333         }
334     }
335     private static native void lstat0(long pathAddress, UnixFileAttributes attrs)
336         throws UnixException;
337 
338     /**
339      * fstat(int filedes, struct stat* buf)
340      */
341     static void fstat(int fd, UnixFileAttributes attrs) throws UnixException {
342         if (Thread.currentThread().isVirtual()) {
343             Blocker.managedBlock(() -> fstat0(fd, attrs));
344         } else {
345             fstat0(fd, attrs);
346         }
347     }
348     private static native void fstat0(int fd, UnixFileAttributes attrs)
349         throws UnixException;
350 
351     /**
352      * fstatat(int filedes,const char* path,  struct stat* buf, int flag)
353      */
354     static void fstatat(int dfd, byte[] path, int flag, UnixFileAttributes attrs)
355         throws UnixException
356     {
357         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(path)) {
358             if (Thread.currentThread().isVirtual()) {
359                 Blocker.managedBlock(() -> fstatat0(dfd, buffer.address(), flag, attrs));
360             } else {
361                 fstatat0(dfd, buffer.address(), flag, attrs);
362             }
363         }
364     }
365     private static native void fstatat0(int dfd, long pathAddress, int flag,
366         UnixFileAttributes attrs) throws UnixException;
367 
368     /**
369      * chown(const char* path, uid_t owner, gid_t group)
370      */
371     static void chown(UnixPath path, int uid, int gid) throws UnixException {
372         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
373             if (Thread.currentThread().isVirtual()) {
374                 Blocker.managedBlock(() -> chown0(buffer.address(), uid, gid));
375             } else {
376                 chown0(buffer.address(), uid, gid);
377             }
378         }
379     }
380     private static native void chown0(long pathAddress, int uid, int gid)
381         throws UnixException;
382 
383     /**
384      * lchown(const char* path, uid_t owner, gid_t group)
385      */
386     static void lchown(UnixPath path, int uid, int gid) throws UnixException {
387         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
388             if (Thread.currentThread().isVirtual()) {
389                 Blocker.managedBlock(() -> lchown0(buffer.address(), uid, gid));
390             } else {
391                 lchown0(buffer.address(), uid, gid);
392             }
393         }
394     }
395     private static native void lchown0(long pathAddress, int uid, int gid)
396         throws UnixException;
397 
398     /**
399      * fchown(int filedes, uid_t owner, gid_t group)
400      */
401     static void fchown(int fd, int uid, int gid) throws UnixException {
402         if (Thread.currentThread().isVirtual()) {
403             Blocker.managedBlock(() -> fchown0(fd, uid, gid));
404         } else {
405             fchown0(fd, uid, gid);
406         }
407     }
408     static native void fchown0(int fd, int uid, int gid) throws UnixException;
409 
410     /**
411      * chmod(const char* path, mode_t mode)
412      */
413     static void chmod(UnixPath path, int mode) throws UnixException {
414         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
415             if (Thread.currentThread().isVirtual()) {
416                 Blocker.managedBlock(() -> chmod0(buffer.address(), mode));
417             } else {
418                 chmod0(buffer.address(), mode);
419             }
420         }
421     }
422     private static native void chmod0(long pathAddress, int mode)
423         throws UnixException;
424 
425     /**
426      * fchmod(int fildes, mode_t mode)
427      */
428     static void fchmod(int fd, int mode) throws UnixException {
429         if (Thread.currentThread().isVirtual()) {
430             Blocker.managedBlock(() -> fchmod0(fd, mode));
431         } else {
432             fchmod0(fd, mode);
433         }
434     }
435     private static native void fchmod0(int fd, int mode) throws UnixException;
436 
437     /**
438      * utimes(const char* path, const struct timeval times[2])
439      */
440     static void utimes(UnixPath path, long times0, long times1)
441         throws UnixException
442     {
443         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
444             if (Thread.currentThread().isVirtual()) {
445                 Blocker.managedBlock(() -> utimes0(buffer.address(), times0, times1));
446             } else {
447                 utimes0(buffer.address(), times0, times1);
448             }
449         }
450     }
451     private static native void utimes0(long pathAddress, long times0, long times1)
452         throws UnixException;
453 
454     /**
455      * futimes(int fildes, const struct timeval times[2])
456      */
457     static void futimes(int fd, long times0, long times1) throws UnixException {
458         if (Thread.currentThread().isVirtual()) {
459             Blocker.managedBlock(() -> futimes0(fd, times0, times1));
460         } else {
461             futimes0(fd, times0, times1);
462         }
463     }
464     private static native void futimes0(int fd, long times0, long times1)
465         throws UnixException;
466 
467     /**
468      * futimens(int fildes, const struct timespec times[2])
469      */
470     static void futimens(int fd, long times0, long times1) throws UnixException {
471         if (Thread.currentThread().isVirtual()) {
472             Blocker.managedBlock(() -> futimens0(fd, times0, times1));
473         } else {
474             futimens0(fd, times0, times1);
475         }
476     }
477     private static native void futimens0(int fd, long times0, long times1)
478         throws UnixException;
479 
480     /**
481      * lutimes(const char* path, const struct timeval times[2])
482      */
483     static void lutimes(UnixPath path, long times0, long times1)
484         throws UnixException
485     {
486         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
487             if (Thread.currentThread().isVirtual()) {
488                 Blocker.managedBlock(() -> lutimes0(buffer.address(), times0, times1));
489             } else {
490                 lutimes0(buffer.address(), times0, times1);
491             }
492         }
493     }
494     private static native void lutimes0(long pathAddress, long times0, long times1)
495         throws UnixException;
496 
497     /**
498      * DIR *opendir(const char* dirname)
499      */
500     static long opendir(UnixPath path) throws UnixException {
501         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
502             if (Thread.currentThread().isVirtual()) {
503                 return Blocker.managedBlock(() -> opendir0(buffer.address()));
504             } else {
505                 return opendir0(buffer.address());
506             }
507         }
508     }
509     private static native long opendir0(long pathAddress) throws UnixException;
510 
511     /**
512      * DIR* fdopendir(int filedes)
513      */
514     static native long fdopendir(int dfd) throws UnixException;
515 
516 
517     /**
518      * closedir(DIR* dirp)
519      */
520     static native void closedir(long dir) throws UnixException;
521 
522     /**
523      * struct dirent* readdir(DIR *dirp)
524      *
525      * @return  dirent->d_name
526      */
527     static byte[] readdir(long dir) throws UnixException {
528         if (Thread.currentThread().isVirtual()) {
529             return Blocker.managedBlock(() -> readdir0(dir));
530         } else {
531             return readdir0(dir);
532         }
533     }
534     static native byte[] readdir0(long dir) throws UnixException;
535 
536     /**
537      * size_t read(int fildes, void* buf, size_t nbyte)
538      */
539     static int read(int fildes, long buf, int nbyte) throws UnixException {
540         if (Thread.currentThread().isVirtual()) {
541             return Blocker.managedBlock(() -> read0(fildes, buf, nbyte));
542         } else {
543             return read0(fildes, buf, nbyte);
544         }
545     }
546     private static native int read0(int fildes, long buf, int nbyte) throws UnixException;
547 
548     /**
549      * size_t writeint fildes, void* buf, size_t nbyte)
550      */
551     static int write(int fildes, long buf, int nbyte) throws UnixException {
552         if (Thread.currentThread().isVirtual()) {
553             return Blocker.managedBlock(() -> write0(fildes, buf, nbyte));
554         } else {
555             return write0(fildes, buf, nbyte);
556         }
557     }
558     private static native int write0(int fildes, long buf, int nbyte) throws UnixException;
559 
560     /**
561      * access(const char* path, int amode);
562      */
563     static void access(UnixPath path, int amode) throws UnixException {
564         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
565             if (Thread.currentThread().isVirtual()) {
566                 Blocker.managedBlock(() -> access0(buffer.address(), amode));
567             } else {
568                 access0(buffer.address(), amode);
569             }
570         }
571     }
572     private static native void access0(long pathAddress, int amode) throws UnixException;
573 
574     /**
575      * access(constant char* path, F_OK)
576      *
577      * @return true if the file exists, false otherwise
578      */
579     static boolean exists(UnixPath path) {
580         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
581             if (Thread.currentThread().isVirtual()) {
582                 return Blocker.managedBlock(() -> exists0(buffer.address()));
583             } else {
584                 return exists0(buffer.address());
585             }
586         }
587     }
588     private static native boolean exists0(long pathAddress);
589 
590     /**
591      * struct passwd *getpwuid(uid_t uid);
592      *
593      * @return  passwd->pw_name
594      */
595     static native byte[] getpwuid(int uid) throws UnixException;
596 
597     /**
598      * struct group *getgrgid(gid_t gid);
599      *
600      * @return  group->gr_name
601      */
602     static native byte[] getgrgid(int gid) throws UnixException;
603 
604     /**
605      * struct passwd *getpwnam(const char *name);
606      *
607      * @return  passwd->pw_uid
608      */
609     static int getpwnam(String name) throws UnixException {
610         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(Util.toBytes(name))) {
611             if (Thread.currentThread().isVirtual()) {
612                 return Blocker.managedBlock(() -> getpwnam0(buffer.address()));
613             } else {
614                 return getpwnam0(buffer.address());
615             }
616         }
617     }
618     private static native int getpwnam0(long nameAddress) throws UnixException;
619 
620     /**
621      * struct group *getgrnam(const char *name);
622      *
623      * @return  group->gr_name
624      */
625     static int getgrnam(String name) throws UnixException {
626         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(Util.toBytes(name))) {
627             if (Thread.currentThread().isVirtual()) {
628                 return Blocker.managedBlock(() -> getgrnam0(buffer.address()));
629             } else {
630                 return getgrnam0(buffer.address());
631             }
632         }
633     }
634     private static native int getgrnam0(long nameAddress) throws UnixException;
635 
636     /**
637      * statvfs(const char* path, struct statvfs *buf)
638      */
639     static void statvfs(UnixPath path, UnixFileStoreAttributes attrs)
640         throws UnixException
641     {
642         try (NativeBuffer buffer = copyToNativeBuffer(path)) {
643             if (Thread.currentThread().isVirtual()) {
644                 Blocker.managedBlock(() -> statvfs0(buffer.address(), attrs));
645             } else {
646                 statvfs0(buffer.address(), attrs);
647             }
648         }
649     }
650     private static native void statvfs0(long pathAddress, UnixFileStoreAttributes attrs)
651         throws UnixException;
652 
653     /**
654      * char* strerror(int errnum)
655      */
656     static native byte[] strerror(int errnum);
657 
658     /**
659      * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
660      */
661     static int fgetxattr(int filedes, byte[] name, long valueAddress, int valueLen)
662         throws UnixException
663     {
664         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(name)) {
665             if (Thread.currentThread().isVirtual()) {
666                 return Blocker.managedBlock(() ->
667                         fgetxattr0(filedes, buffer.address(), valueAddress, valueLen));
668             } else {
669                 return fgetxattr0(filedes, buffer.address(), valueAddress, valueLen);
670             }
671         }
672     }
673 
674     private static native int fgetxattr0(int filedes, long nameAddress,
675         long valueAddress, int valueLen) throws UnixException;
676 
677     /**
678      *  fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
679      */
680     static void fsetxattr(int filedes, byte[] name, long valueAddress, int valueLen)
681         throws UnixException
682     {
683         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(name)) {
684             if (Thread.currentThread().isVirtual()) {
685                 Blocker.managedBlock(() ->
686                         fsetxattr0(filedes, buffer.address(), valueAddress, valueLen));
687             } else {
688                 fsetxattr0(filedes, buffer.address(), valueAddress, valueLen);
689             }
690         }
691     }
692 
693     private static native void fsetxattr0(int filedes, long nameAddress,
694         long valueAddress, int valueLen) throws UnixException;
695 
696     /**
697      * fremovexattr(int filedes, const char *name);
698      */
699     static void fremovexattr(int filedes, byte[] name) throws UnixException {
700         try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(name)) {
701             if (Thread.currentThread().isVirtual()) {
702                 Blocker.managedBlock(() -> fremovexattr0(filedes, buffer.address()));
703             } else {
704                 fremovexattr0(filedes, buffer.address());
705             }
706         }
707     }
708 
709     private static native void fremovexattr0(int filedes, long nameAddress)
710         throws UnixException;
711 
712     /**
713      * size_t flistxattr(int filedes, const char *list, size_t size)
714      */
715     static native int flistxattr(int filedes, long listAddress, int size)
716         throws UnixException;
717 
718     /**
719      * Capabilities
720      */
721     private static final int SUPPORTS_OPENAT        = 1 << 1;  // syscalls
722     private static final int SUPPORTS_FUTIMES       = 1 << 2;
723     private static final int SUPPORTS_FUTIMENS      = 1 << 3;
724     private static final int SUPPORTS_LUTIMES       = 1 << 4;
725     private static final int SUPPORTS_XATTR         = 1 << 5;
726     private static final int SUPPORTS_BIRTHTIME     = 1 << 16; // other features
727     private static final int capabilities;
728 
729     /**
730      * Supports openat and other *at calls.
731      */
732     static boolean openatSupported() {
733         return (capabilities & SUPPORTS_OPENAT) != 0;
734     }
735 
736     /**
737      * Supports futimes or futimesat
738      */
739     static boolean futimesSupported() {
740         return (capabilities & SUPPORTS_FUTIMES) != 0;
741     }
742 
743     /**
744      * Supports futimens
745      */
746     static boolean futimensSupported() {
747         return (capabilities & SUPPORTS_FUTIMENS) != 0;
748     }
749 
750     /**
751      * Supports lutimes
752      */
753     static boolean lutimesSupported() {
754         return (capabilities & SUPPORTS_LUTIMES) != 0;
755     }
756 
757     /**
758      * Supports file birth (creation) time attribute
759      */
760     static boolean birthtimeSupported() {
761         return (capabilities & SUPPORTS_BIRTHTIME) != 0;
762     }
763 
764     /**
765      * Supports extended attributes
766      */
767     static boolean xattrSupported() {
768         return (capabilities & SUPPORTS_XATTR) != 0;
769     }
770 
771     private static native int init();
772     static {
773         jdk.internal.loader.BootLoader.loadLibrary("nio");
774         capabilities = init();
775     }
776 }