From 02084fb40bebb15ee0b2db30260cf53205d89921 Mon Sep 17 00:00:00 2001
From: juanf <juanf@ethz.ch>
Date: Mon, 12 Jun 2023 09:59:13 +0200
Subject: [PATCH] SSDM-13521: moving posix functionality to original unix class

---
 .../java/ch/systemsx/cisd/base/unix/Unix.java | 1566 ++++++-----------
 .../cisd/base/unix/UnixRootTests.java         |  108 --
 .../ch/systemsx/cisd/base/unix/UnixTests.java |  340 +---
 .../ch/systemsx/cisd/common/io/Posix.java     |   71 +-
 .../ch/systemsx/cisd/common/io/PosixTest.java |  117 --
 5 files changed, 578 insertions(+), 1624 deletions(-)
 delete mode 100644 lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixRootTests.java

diff --git a/lib-base/source/java/ch/systemsx/cisd/base/unix/Unix.java b/lib-base/source/java/ch/systemsx/cisd/base/unix/Unix.java
index 7c65aa9a551..63ad45b0968 100644
--- a/lib-base/source/java/ch/systemsx/cisd/base/unix/Unix.java
+++ b/lib-base/source/java/ch/systemsx/cisd/base/unix/Unix.java
@@ -16,123 +16,319 @@
 
 package ch.systemsx.cisd.base.unix;
 
+import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
+
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
-import ch.rinn.restrictions.Private;
-import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
-import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
-import ch.systemsx.cisd.base.utilities.NativeLibraryUtilities;
-
-/**
- * A utility class that provides access to common Unix system calls. Obviously, this will only work on Unix platforms and it requires a native library
- * to be loaded.
- * <p>
- * <i>Check with {@link #isOperational()} if this class is operational and only call the other methods if
- * <code>Unix.isOperational() == true</code>.</i>
- * 
- * @author Bernd Rinn
- */
 public final class Unix
 {
+    // Make constructor private to make clear is a utility class
+    private Unix() {
 
-    /**
-     * Method how processes are detected on this host.
-     *
-     */
-    private enum ProcessDetection
+    }
+
+    public static boolean isOperational() {
+        return File.separatorChar == '/'; //On Posix systems the value of this field is '/'
+    }
+
+    //
+    // User related methods
+    //
+
+    private static Integer gid = null;
+
+    public static int getGid()
     {
-        /**
-         * Process detection via <code>procfs</code> (Linux only).
-         */
-        PROCFS,
-        
-        /**
-         * Process detection via the command line tool <code>ps</code>.
-         */
-        PS,
-        
-        /**
-         * No working process detection found. 
-         */
-        NONE
+        if (gid == null)
+        {
+            try
+            {
+                Process process = Runtime.getRuntime().exec("id -g -r");
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String output = reader.readLine();
+                reader.close();
+                gid = Integer.parseInt(output);
+            } catch (IOException e)
+            {
+                throw new IOExceptionUnchecked(e);
+            }
+        }
+        return gid;
     }
 
-    private final static boolean operational;
-    
-    private final static ProcessDetection processDetection;
+    private static Integer uid = null;
+
+    public static int getUid() throws IOExceptionUnchecked
+    {
+        if (uid == null)
+        {
+            try
+            {
+                Process process = Runtime.getRuntime().exec("id -u -r");
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String output = reader.readLine();
+                reader.close();
+                uid = Integer.parseInt(output);
+            } catch (IOException e)
+            {
+                throw new IOExceptionUnchecked(e);
+            }
+        }
+        return uid;
+    }
 
-    private static volatile boolean useUnixRealtimeTimer = false;
+    private static Map<String, Integer> uidByUserName = new HashMap<>();
 
-    static
+    /**
+     * Returns the uid of the <var>userName</var>, or <code>-1</code>, if no user with this name exists.
+     */
+    public static final int getUidForUserName(String userName)
     {
-        operational = NativeLibraryUtilities.loadNativeLibrary("unix");
-        if (operational)
+        if (userName == null)
+        {
+            throw new NullPointerException("userName");
+        }
+
+        if (uidByUserName.get(userName) == null)
         {
-            init();
-            final int myPid = getPid();
-            if (isProcessRunningProcFS(myPid))
+            try
             {
-                processDetection = ProcessDetection.PROCFS;
-            } else if (isProcessRunningPS(myPid))
+                Process process = Runtime.getRuntime().exec("id -u " + userName);
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String output = reader.readLine();
+                reader.close();
+                int uid = Integer.parseInt(output);
+                uidByUserName.put(userName, uid);
+            } catch (IOException e)
             {
-                processDetection = ProcessDetection.PS;
-            } else
+                throw new IOExceptionUnchecked(e);
+            }
+        }
+
+        return uidByUserName.get(userName);
+    }
+
+    private static Map<String, Integer> gidByGroupName = new HashMap<>();
+
+    /**
+     * Returns the gid of the <var>groupName</var>, or <code>-1</code>, if no group with this name exists.
+     */
+    public static final int getGidForGroupName(String groupName)
+    {
+        if (groupName == null)
+        {
+            throw new NullPointerException("groupName");
+        }
+
+        if (gidByGroupName.get(groupName) == null)
+        {
+            try
+            {
+                Process process = Runtime.getRuntime().exec("id -g " + groupName);
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String output = reader.readLine();
+                reader.close();
+                int uid = Integer.parseInt(output);
+                gidByGroupName.put(groupName, uid);
+            } catch (IOException e)
             {
-                processDetection = ProcessDetection.NONE;
+                throw new IOExceptionUnchecked(e);
             }
-        } else
+        }
+
+        return gidByGroupName.get(groupName);
+    }
+
+    private static Integer euid = null;
+
+    public static int getEuid()
+    {
+        if (euid == null)
         {
-            processDetection = ProcessDetection.NONE;
+            try
+            {
+                Process process = Runtime.getRuntime().exec("id -u");
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String output = reader.readLine();
+                reader.close();
+                euid = Integer.parseInt(output);
+            } catch (IOException e)
+            {
+                throw new IOExceptionUnchecked(e);
+            }
         }
-        useUnixRealtimeTimer = Boolean.getBoolean("unix.realtime.timer");
+        return euid;
+    }
+
+    /**
+     * Returns the effective gid that determines the permissions of this process.
+     */
+    public static final int getEgid()
+    {
+        return getGid();
     }
 
-    /** set user ID on execution */
-    public static final short S_ISUID = 04000;
+    /**
+     * Sets the owner of <var>fileName</var> to the specified <var>uid</var> and <var>gid</var> values.
+     * Dereferences a symbolic link.
+     */
+    public static void setOwner(String path, int userId, int groupId)
+    {
+        try
+        {
+            Files.setAttribute(Path.of(path), "unix:uid", userId);
+            Files.setAttribute(Path.of(path), "unix:gid", groupId);
+        } catch (IOException e)
+        {
+            throw new IOExceptionUnchecked(e);
+        }
+    }
 
-    /** set group ID on execution */
-    public static final short S_ISGID = 02000;
+    public static int getUid(String path, boolean followLinks)
+    {
+        try
+        {
+            if (followLinks)
+            {
+                return (int) Files.getAttribute(Path.of(path), "unix:uid");
+            } else {
+                return (int) Files.getAttribute(Path.of(path), "unix:uid", LinkOption.NOFOLLOW_LINKS);
+            }
+        } catch (IOException e)
+        {
+            throw new IOExceptionUnchecked(e);
+        }
+    }
 
-    /** sticky bit */
-    public static final short S_ISVTX = 01000;
+    public static int getUid(String path)
+    {
+        return getUid(path, true);
+    }
 
-    /** read by owner */
-    public static final short S_IRUSR = 00400;
+    public static int getGid(String path, boolean followLinks)
+    {
+        try
+        {
+            if (followLinks)
+            {
+                return (int) Files.getAttribute(Path.of(path), "unix:gid");
+            } else {
+                return (int) Files.getAttribute(Path.of(path), "unix:gid", LinkOption.NOFOLLOW_LINKS);
+            }
+        } catch (IOException e)
+        {
+            throw new IOExceptionUnchecked(e);
+        }
+    }
 
-    /** write by owner */
-    public static final short S_IWUSR = 00200;
+    public static int getGid(String path)
+    {
+        return getGid(path, true);
+    }
 
-    /** execute/search by owner */
-    public static final short S_IXUSR = 00100;
+    private static Map<Integer, String> userNameByUid = new HashMap<>();
 
-    /** read by group */
-    public static final short S_IRGRP = 00040;
+    public static String tryGetUserNameForUid(int uid)
+    {
+        if (userNameByUid.get(uid) == null)
+        {
+            try
+            {
+                Process process = Runtime.getRuntime().exec("id -un " + uid);
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String output = reader.readLine();
+                reader.close();
+                userNameByUid.put(uid, output);
+                uidByUserName.put(output, uid);
+            } catch (IOException e)
+            {
+                throw new IOExceptionUnchecked(e);
+            }
+        }
+        return userNameByUid.get(uid);
+    }
 
-    /** write by group */
-    public static final short S_IWGRP = 00020;
+    private static Map<Integer, String> groupNameByGid = new HashMap<>();
 
-    /** execute/search by group */
-    public static final short S_IXGRP = 00010;
+    public static String tryGetGroupNameForGid(int gid)
+    {
+        if (groupNameByGid.get(gid) == null)
+        {
+            try
+            {
+                Process process = Runtime.getRuntime().exec("id -gn " + gid);
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String output = reader.readLine();
+                reader.close();
+                groupNameByGid.put(gid, output);
+                gidByGroupName.put(output, gid);
+            } catch (IOException e)
+            {
+                throw new IOExceptionUnchecked(e);
+            }
+        }
+        return groupNameByGid.get(gid);
+    }
 
-    /** read by others */
-    public static final short S_IROTH = 00004;
+    public static Time getSystemTime()
+    {
+        return Time.getInstance();
+    }
 
-    /** write by others */
-    public static final short S_IWOTH = 00002;
+    public static String tryReadSymbolicLink(String absolutePath)
+    {
+        Stat stat = tryGetLinkInfo(absolutePath);
+        return stat.isSymbolicLink() ? stat.tryGetSymbolicLink() : null;
+    }
 
-    /** execute/search by others */
-    public static final short S_IXOTH = 00001;
+    //
+    // File related methods
+    //
 
     /**
-     * A class to represent a Unix <code>struct timespec</code> that holds a system time in nano-second resolution. 
+     * A class to represent a Unix <code>struct timespec</code> that holds a system time in nano-second resolution.
      */
     public static final class Time
     {
         private final long secs;
-        
+
         private final long nanos;
 
+        public static Time getInstance() {
+            Instant now = Instant.now();
+            return new Time(now);
+        }
+
+        private Time(Instant now) {
+            this(now.getEpochSecond(), now.getNano());
+        }
+
+        private Time(FileTime fileTime)
+        {
+            this(fileTime.toInstant().getEpochSecond(), fileTime.toInstant().getNano());
+        }
+
         private Time(long secs, long nanos)
         {
             this.secs = secs;
@@ -170,7 +366,7 @@ public final class Unix
                 return nanos / 1_000_000;
             }
         }
-        
+
         public long getMillis()
         {
             return secs * 1_000 + getMilliSecPart();
@@ -220,121 +416,69 @@ public final class Unix
         }
 
     }
-    
-    /**
-     * A class representing the Unix <code>stat</code> structure.
-     */
-    public static final class Stat
-    {
-        private final long deviceId;
 
-        private final long inode;
+    public static class Stat
+    {
+        private final Path path;
 
         private final short permissions;
 
         private final FileLinkType linkType;
 
-        private String symbolicLinkOrNull;
+        private final Time lastModified;
 
-        private final int numberOfHardLinks;
+        private final Time lastAccessed;
 
         private final int uid;
 
         private final int gid;
 
-        private final Time lastAccess;
-
-        private final Time lastModified;
-
-        private final Time lastStatusChange;
+        private final String symbolicLinkOrNull;
 
         private final long size;
 
-        private final long numberOfBlocks;
-
-        private final int blockSize;
-
-        Stat(long deviceId, long inode, short permissions, byte linkType, int numberOfHardLinks,
-                int uid, int gid, long lastAccess, long lastModified, long lastStatusChange,
-                long size, long numberOfBlocks, int blockSize)
-        {
-            this(deviceId, inode, permissions, linkType, numberOfHardLinks, uid, gid,
-                    lastAccess, 0, lastModified, 0, lastStatusChange, 0, size, numberOfBlocks, blockSize);
-        }
-
-        Stat(long deviceId, long inode, short permissions, byte linkType, int numberOfHardLinks,
-                int uid, int gid, long lastAccess, long lastAccessNanos,
-                long lastModified, long lastModifiedNanos,
-                long lastStatusChange, long lastStatusChangeNanos,
-                long size, long numberOfBlocks, int blockSize)
+        public Stat(Path path, short permissions, FileLinkType linkType, FileTime lastModified, FileTime lastAccessed, int uid, int gid,
+                String symbolicLinkOrNull, long size)
         {
-            this.deviceId = deviceId;
-            this.inode = inode;
+            this.path = path;
             this.permissions = permissions;
-            this.linkType = FileLinkType.values()[linkType];
-            this.numberOfHardLinks = numberOfHardLinks;
+            this.linkType = linkType;
+            this.lastModified = new Time(lastModified);
+            this.lastAccessed = new Time(lastAccessed);
             this.uid = uid;
             this.gid = gid;
-            this.lastAccess = new Time(lastAccess, lastAccessNanos);
-            this.lastModified = new Time(lastModified, lastModifiedNanos);
-            this.lastStatusChange = new Time(lastStatusChange, lastStatusChangeNanos);
-            this.size = size;
-            this.numberOfBlocks = numberOfBlocks;
-            this.blockSize = blockSize;
-        }
-
-        void setSymbolicLinkOrNull(String symbolicLinkOrNull)
-        {
             this.symbolicLinkOrNull = symbolicLinkOrNull;
+            this.size = size;
         }
 
-        /**
-         * Get link target of the symbolic link or <code>null</code>, if this is not a link or the link target has not been read.
-         */
-        public String tryGetSymbolicLink()
-        {
-            return symbolicLinkOrNull;
-        }
-
-        public long getDeviceId()
+        public short getPermissions()
         {
-            return deviceId;
+            return permissions;
         }
 
-        /**
-         * Returns the inode. 
-         */
-        public long getInode()
+        public FileLinkType getLinkType()
         {
-            return inode;
+            return linkType;
         }
 
-        public short getPermissions()
+        public long getLastAccess()
         {
-            return permissions;
+            return lastAccessed.getSecs();
         }
 
-        public FileLinkType getLinkType()
+        public Time getLastAccessTime()
         {
-            return linkType;
+            return lastAccessed;
         }
 
-        /**
-         * Returns <code>true</code>, if this link is a symbolic link.
-         */
-        public final boolean isSymbolicLink()
+        public long getLastModified()
         {
-            return FileLinkType.SYMLINK == linkType;
+            return lastModified.getSecs();
         }
 
-        /**
-         * Returns the number of hard links for the <var>linkName</var>. Does not dereference a symbolic link.
-         * 
-         * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
-         */ 
-        public int getNumberOfHardLinks()
+        public Time getLastModifiedTime()
         {
-            return numberOfHardLinks;
+            return lastModified;
         }
 
         public int getUid()
@@ -347,456 +491,306 @@ public final class Unix
             return gid;
         }
 
-        /**
-         * Time when file data last accessed.
-         * <p>
-         * Changed by the mknod(2), utimes(2) and read(2) system calls.
-         * 
-         * @return {@link Time} object containing seconds (to nano-second resolution) since the epoch.
-         */
-        public Time getLastAccessTime()
+        public String tryGetSymbolicLink()
         {
-            return lastAccess;
+            return symbolicLinkOrNull;
         }
 
-        /**
-         * Time when file data last accessed.
-         * <p>
-         * Changed by the mknod(2), utimes(2) and read(2) system calls.
-         * 
-         * @return Seconds since the epoch.
-         */
-        public long getLastAccess()
-        {
-            return lastAccess.getSecs();
+        public long getSize() {
+            return size;
         }
 
-        /**
-         * Time when file data last modified.
-         * <p>
-         * Changed by the mknod(2), utimes(2) and write(2) system calls.
-         * 
-         * @return {@link Time} object containing seconds (to nano-second resolution) since the epoch.
-         */
-        public Time getLastModifiedTime()
+        public boolean isSymbolicLink()
         {
-            return lastModified;
+            return symbolicLinkOrNull != null;
         }
 
         /**
-         * Time when file data last modified.
-         * <p>
-         * Changed by the mknod(2), utimes(2) and write(2) system calls.
-         * 
-         * @return Seconds since the epoch.
+         * Returns the number of hard links for the <var>linkName</var>. Does not dereference a symbolic link.
+         *
+         * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
          */
-        public long getLastModified()
-        {
-            return lastModified.getSecs();
+        public int getNumberOfHardLinks() throws IOException
+        {
+            Number count = (Number) Files.getAttribute(path, "unix:nlink", LinkOption.NOFOLLOW_LINKS);
+            return count.intValue();
+        }
+    }
+
+    private static short getNumericAccessMode(Set<PosixFilePermission> permissions) {
+        short posixPermissions = 0;
+
+        for (PosixFilePermission permission : permissions) {
+            switch (permission) {
+                case OWNER_READ:
+                    posixPermissions |= 0400;
+                    break;
+                case OWNER_WRITE:
+                    posixPermissions |= 0200;
+                    break;
+                case OWNER_EXECUTE:
+                    posixPermissions |= 0100;
+                    break;
+                case GROUP_READ:
+                    posixPermissions |= 0040;
+                    break;
+                case GROUP_WRITE:
+                    posixPermissions |= 0020;
+                    break;
+                case GROUP_EXECUTE:
+                    posixPermissions |= 0010;
+                    break;
+                case OTHERS_READ:
+                    posixPermissions |= 0004;
+                    break;
+                case OTHERS_WRITE:
+                    posixPermissions |= 0002;
+                    break;
+                case OTHERS_EXECUTE:
+                    posixPermissions |= 0001;
+                    break;
+            }
         }
 
-        /**
-         * Time when file status was last changed (inode data modification).
-         * <p>
-         * Changed by the chmod(2), chown(2), link(2), mknod(2), rename(2), unlink(2), utimes(2) and write(2) system calls.
-         * 
-         * @return {@link Time} object containing seconds (to nano-second resolution) since the epoch.
-         */
-        public Time getLastStatusChangeTime()
-        {
-            return lastStatusChange;
-        }
+        return posixPermissions;
+    }
 
-        /**
-         * Time when file status was last changed (inode data modification).
-         * <p>
-         * Changed by the chmod(2), chown(2), link(2), mknod(2), rename(2), unlink(2), utimes(2) and write(2) system calls.
-         * 
-         * @return Seconds since the epoch.
-         */
-        public long getLastStatusChange()
-        {
-            return lastStatusChange.getSecs();
-        }
+    private static Set<PosixFilePermission> getFilePermissionsMode(short permissions) {
+        Set<PosixFilePermission> posixPermissions = new HashSet<>();
 
-        public long getSize()
-        {
-            return size;
+        if ((permissions & 0400) != 0) {
+            posixPermissions.add(PosixFilePermission.OWNER_READ);
         }
-
-        public long getNumberOfBlocks()
-        {
-            return numberOfBlocks;
+        if ((permissions & 0200) != 0) {
+            posixPermissions.add(PosixFilePermission.OWNER_WRITE);
         }
-
-        public int getBlockSize()
-        {
-            return blockSize;
+        if ((permissions & 0100) != 0) {
+            posixPermissions.add(PosixFilePermission.OWNER_EXECUTE);
+        }
+        if ((permissions & 0040) != 0) {
+            posixPermissions.add(PosixFilePermission.GROUP_READ);
+        }
+        if ((permissions & 0020) != 0) {
+            posixPermissions.add(PosixFilePermission.GROUP_WRITE);
+        }
+        if ((permissions & 0010) != 0) {
+            posixPermissions.add(PosixFilePermission.GROUP_EXECUTE);
+        }
+        if ((permissions & 0004) != 0) {
+            posixPermissions.add(PosixFilePermission.OTHERS_READ);
+        }
+        if ((permissions & 0002) != 0) {
+            posixPermissions.add(PosixFilePermission.OTHERS_WRITE);
+        }
+        if ((permissions & 0001) != 0) {
+            posixPermissions.add(PosixFilePermission.OTHERS_EXECUTE);
         }
 
+        return posixPermissions;
     }
 
     /**
-     * A class representing the Unix <code>group</code> struct.
+     * Returns the information about <var>linkName</var>.
+     *
+     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
      */
-    public static final class Group
+    public static Stat getLinkInfo(String absolutePath)
     {
-        private final String groupName;
-
-        private final String groupPasswordHash;
-
-        private final int gid;
+        return getLinkInfo(absolutePath, true);
+    }
 
-        private final String[] groupMembers;
+    /**
+     * Returns the information about <var>linkName</var>. If <code>readSymbolicLinkTarget == true</code>, then the symbolic link target is read when
+     * <var>linkName</var> is a symbolic link.
+     *
+     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
+     */
+    public static Stat getLinkInfo(String pathAsString, boolean readSymbolicLinkTarget)
+    {
+        try {
+            if (pathAsString == null)
+            {
+                throw new NullPointerException("linkName");
+            }
 
-        Group(String groupName, String groupPasswordHash, int gid, String[] groupMembers)
-        {
-            this.groupName = groupName;
-            this.groupPasswordHash = groupPasswordHash;
-            this.gid = gid;
-            this.groupMembers = groupMembers;
-        }
+            Path path = Path.of(pathAsString);
+            if (Files.exists(path, LinkOption.NOFOLLOW_LINKS) == false)
+            {
+                return null;
+            }
 
-        public String getGroupName()
-        {
-            return groupName;
-        }
 
-        public String getGroupPasswordHash()
-        {
-            return groupPasswordHash;
-        }
+            PosixFileAttributes attrs = null;
+            FileLinkType linkType;
+            short permissions;
+            int uid;
+            int gid;
+            if(readSymbolicLinkTarget && Files.exists(path))
+            {
+                permissions = getNumericAccessMode(Files.getPosixFilePermissions(path));
+                attrs = Files.readAttributes(path, PosixFileAttributes.class);
+                if (Files.isSymbolicLink(path)) {
+                    linkType = FileLinkType.SYMLINK;
+                } else if (Files.isDirectory(path)) {
+                    linkType = FileLinkType.DIRECTORY;
+                } else if (Files.isRegularFile(path)) {
+                    linkType = FileLinkType.REGULAR_FILE;
+                } else {
+                    linkType = FileLinkType.OTHER;
+                }
+                uid = getUid(pathAsString);
+                gid = getGid(pathAsString);
+            } else {
+                permissions = getNumericAccessMode(Files.getPosixFilePermissions(path, LinkOption.NOFOLLOW_LINKS));
+                attrs = Files.readAttributes(path, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+                if (Files.isSymbolicLink(path)) {
+                    linkType = FileLinkType.SYMLINK;
+                } else if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) {
+                    linkType = FileLinkType.DIRECTORY;
+                } else if (Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS)) {
+                    linkType = FileLinkType.REGULAR_FILE;
+                } else {
+                    linkType = FileLinkType.OTHER;
+                }
+                uid = getUid(pathAsString, false);
+                gid = getGid(pathAsString, false);
+            }
 
-        public int getGid()
-        {
-            return gid;
-        }
+            FileTime lastModified = attrs.lastModifiedTime();
+            FileTime lastAccessed = attrs.lastAccessTime();
 
-        public String[] getGroupMembers()
+            String symbolicLinkOrNull = null;
+            if (linkType == FileLinkType.SYMLINK && readSymbolicLinkTarget) {
+                symbolicLinkOrNull = Files.readSymbolicLink(path).toString();
+            }
+            long size = attrs.size();
+            return new Stat(path, permissions, linkType, lastModified, lastAccessed, uid, gid, symbolicLinkOrNull, size);
+        } catch (IOException e)
         {
-            return groupMembers;
+            throw new IOExceptionUnchecked(e);
         }
     }
 
     /**
-     * A class representing the Unix <code>passwd</code> struct.
+     * Returns the information about <var>linkName</var>, or {@link NullPointerException}, if the information could not be obtained, e.g. because the
+     * link does not exist.
      */
-    public static final class Password
-    {
-        private final String userName;
-
-        private final String passwordHash;
-
-        private final int uid;
-
-        private final int gid;
-
-        private final String userFullName;
-
-        private final String homeDirectory;
-
-        private final String shell;
-
-        Password(String userName, String passwordHash, int uid, int gid, String userFullName,
-                String homeDirectory, String shell)
-        {
-            this.userName = userName;
-            this.passwordHash = passwordHash;
-            this.uid = uid;
-            this.gid = gid;
-            this.userFullName = userFullName;
-            this.homeDirectory = homeDirectory;
-            this.shell = shell;
-        }
-
-        public String getUserName()
-        {
-            return userName;
-        }
-
-        public String getPasswordHash()
-        {
-            return passwordHash;
-        }
-
-        public int getUid()
-        {
-            return uid;
-        }
-
-        public int getGid()
-        {
-            return gid;
-        }
-
-        public String getUserFullName()
-        {
-            return userFullName;
-        }
-
-        public String getHomeDirectory()
-        {
-            return homeDirectory;
-        }
-
-        public String getShell()
-        {
-            return shell;
-        }
-    }
-
-    private static void throwLinkCreationException(String type, String source, String target,
-            String errorMessage)
-    {
-        throw new IOExceptionUnchecked(new IOException(String.format(
-                "Creating %s link '%s' -> '%s': %s", type, target, source, errorMessage)));
-    }
-
-    private static void throwStatException(String filename, String errorMessage)
-    {
-        throw new IOExceptionUnchecked(new IOException(String.format(
-                "Cannot obtain inode info for file '%s': %s", filename, errorMessage)));
-    }
-
-    private static void throwFileException(String operation, String filename, String errorMessage)
-    {
-        throw new IOExceptionUnchecked(new IOException(String.format("Cannot %s of file '%s': %s",
-                operation, filename, errorMessage)));
-    }
-
-    private static void throwRuntimeException(String operation, String errorMessage)
-    {
-        throw new RuntimeException(String.format("Error on %s: %s", operation, errorMessage));
-    }
-
-    private static native int init();
-
-    public static boolean isUseUnixRealtimeTimer()
-    {
-        return useUnixRealtimeTimer;
+    public static Stat tryGetLinkInfo(String pathAsString){
+        return getLinkInfo(pathAsString, true);
     }
 
     /**
-     * Sets whether to use the Unix realttime timer.
-     * <p>
-     * <i>Note that old versions of Linux and MacOSX do not yet support this and will terminate the Java program when 
-     * this flag is set to <code>true</code> and {@link #getSystemTime()} is called!</i>
-     * @param useUnixRealTimeTimer if <code>true</code>, the realtime timer (nano-second resolution) will be used, 
-     * otherwise the regular timer (micro-second resolution) will be used.
-     */
-    public static void setUseUnixRealtimeTimer(boolean useUnixRealTimeTimer)
-    {
-        Unix.useUnixRealtimeTimer = useUnixRealTimeTimer;
-    }
-
-    private static native int getpid();
-
-    private static native int getuid();
-
-    private static native int geteuid();
-
-    private static native int getgid();
-
-    private static native int getegid();
-
-    private static native int link(String filename, String linktarget);
-
-    private static native int symlink(String filename, String linktarget);
-
-    private static native Stat stat(String filename);
-
-    private static native Stat lstat(String filename);
-
-    private static native String readlink(String filename, int linkvallen);
-
-    private static native int chmod(String filename, short mode);
-
-    private static native int chown(String filename, int uid, int gid);
-
-    private static native int lchown(String filename, int uid, int gid);
-
-    private static native int clock_gettime(final long[] time);
-    
-    private static native int clock_gettime2(final long[] time);
-    
-    private static native int lutimes(String filename,
-            long accessTimeSecs, long accessTimeMicroSecs,
-            long modificationTimeSecs, long modificationTimeMicroSecs);
-
-    private static native int utimes(String filename,
-            long accessTimeSecs, long accessTimeMicroSecs,
-            long modificationTimeSecs, long modificationTimeMicroSecs);
-
-    private static native String getuser(int uid);
-
-    private static native String getgroup(int gid);
-
-    private static native int getuid(String user);
-
-    private static native Password getpwnam(String user);
-
-    private static native Password getpwuid(int uid);
-
-    private static native int getgid(String group);
-
-    private static native Group getgrnam(String group);
-
-    private static native Group getgrgid(int gid);
-
-    private static native String strerror(int errnum);
-
-    private static native String strerror();
-    
-    @Private
-    static boolean isProcessRunningProcFS(int pid)
-    {
-        return new File("/proc/" + pid).isDirectory();
-    }
-
-    @Private
-    static boolean isProcessRunningPS(int pid)
-    {
-        try
-        {
-            return Runtime.getRuntime().exec(new String[] { "ps", "-p", Integer.toString(pid) }).waitFor() == 0;
-        } catch (IOException ex)
-        {
-            return false;
-        } catch (InterruptedException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        }
-    }
-
-    //
-    // Public
-    //
-
-    /**
-     * Returns <code>true</code>, if the native library has been loaded successfully and the link utilities are operational, <code>false</code>
-     * otherwise.
+     * Returns the information about <var>fileName</var>, or {@link NullPointerException}, if the information could not be obtained, e.g. because the
+     * file does not exist.
      */
-    public static final boolean isOperational()
+    public static Stat tryGetFileInfo(String absolutePath)
     {
-        return operational;
+        return getFileInfo(absolutePath, true);
     }
 
     /**
-     * Returns <code>true</code>, if process detection is available on this system.
+     * Returns the information about <var>fileName</var>.
+     *
+     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the file does not exist.
      */
-    public static boolean canDetectProcesses()
+    public static Stat getFileInfo(String pathAsString)
     {
-        return processDetection != ProcessDetection.NONE;
+        return getFileInfo(pathAsString, true);
     }
 
     /**
-     * Returns the last error that occurred in this class. Use this to find out what went wrong after {@link #tryGetLinkInfo(String)} or
-     * {@link #tryGetFileInfo(String)} returned <code>null</code>.
+     * Returns the information about <var>fileName</var>.
+     *
+     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the file does not exist.
      */
-    public static String getLastError()
+    public static Stat getFileInfo(String pathAsString, boolean readSymbolicLinkTarget)
+            throws IOExceptionUnchecked
     {
-        return strerror();
-    }
+        try {
+            if (pathAsString == null)
+            {
+                throw new NullPointerException("linkName");
+            }
 
-    //
-    // Process functions
-    //
+            Path path = Path.of(pathAsString);
+            if (Files.exists(path) == false)
+            {
+                return null;
+            }
 
-    /**
-     * Returns the process identifier of the current process.
-     */
-    public static int getPid()
-    {
-        return getpid();
-    }
+            short permissions = getNumericAccessMode(Files.getPosixFilePermissions(path));
+            PosixFileAttributes attrs = Files.readAttributes(path, PosixFileAttributes.class);
+
+            FileLinkType linkType;
+            if (attrs.isSymbolicLink()) {
+                linkType = FileLinkType.SYMLINK;
+            } else if (attrs.isDirectory()) {
+                linkType = FileLinkType.DIRECTORY;
+            } else if (attrs.isRegularFile()) {
+                linkType = FileLinkType.REGULAR_FILE;
+            } else {
+                linkType = FileLinkType.OTHER;
+            }
 
-    /**
-     * Returns <code>true</code>, if the process with <var>pid</var> is currently running and <code>false</code>, if it is not running or if process
-     * detection is not available ( {@link #canDetectProcesses()} <code>== false</code>).
-     */
-    public static boolean isProcessRunning(int pid)
-    {
-        switch (processDetection)
+            FileTime lastModified = attrs.lastModifiedTime();
+            FileTime lastAccessed = attrs.lastAccessTime();
+            int uid = getUid(pathAsString);
+            int gid = getGid(pathAsString);
+            String symbolicLinkOrNull = null;
+            if (linkType == FileLinkType.SYMLINK && readSymbolicLinkTarget) {
+                symbolicLinkOrNull = Files.readSymbolicLink(path).toString();
+            }
+            long size = attrs.size();
+            return new Stat(path, permissions, linkType, lastModified, lastAccessed, uid, gid, symbolicLinkOrNull, size);
+        } catch (IOException e)
         {
-            case PROCFS:
-                return isProcessRunningProcFS(pid);
-            case PS:
-                return isProcessRunningPS(pid);
-            default:
-                return false;
+            throw new IOExceptionUnchecked(e);
         }
     }
 
-    /**
-     * Returns the uid of the user that started this process.
-     */
-    public static final int getUid()
-    {
-        return getuid();
-    }
-
-    /**
-     * Returns the effective uid that determines the permissions of this process.
-     */
-    public static final int getEuid()
-    {
-        return geteuid();
+    public static Set<PosixFilePermission> getPermissions(String path) throws IOExceptionUnchecked {
+        try
+        {
+            return Files.getPosixFilePermissions(Path.of(path));
+        } catch (IOException e)
+        {
+            throw new IOExceptionUnchecked(e);
+        }
     }
 
-    /**
-     * Returns the gid of the user that started this process.
-     */
-    public static final int getGid()
-    {
-        return getgid();
+    public static void setAccessMode(String path, Set<PosixFilePermission> permissions) throws IOExceptionUnchecked {
+        try {
+            Files.setPosixFilePermissions(Path.of(path), permissions);
+        } catch (IOException e) {
+            throw new IOExceptionUnchecked(e);
+        }
     }
 
     /**
-     * Returns the effective gid that determines the permissions of this process.
+     * Sets the access mode of <var>filename</var> to the specified <var>mode</var> value.
+     * Dereferences a symbolic link.
      */
-    public static final int getEgid()
-    {
-        return getegid();
+    public static void setAccessMode(String path, short mode) throws IOExceptionUnchecked {
+        Set<PosixFilePermission> permissions = getFilePermissionsMode(mode);
+        setAccessMode(path, permissions);
     }
 
-    //
-    // Time functions
-    //
 
-    /**
-     * Gets the current system time.
-     * 
-     * @return the system time as <i>seconds since the epoch</i> and, in addition, 
-     *         nano-seconds since the current second.
-     */
-    public static final Time getSystemTime()
-    {
-        final long[] time = new long[2];
-        final int result = useUnixRealtimeTimer ? clock_gettime(time) : clock_gettime2(time);
-        if (result < 0)
-        {
-            throwRuntimeException("get system time", strerror(result));
+    public static void setAccessMode777(String path) throws IOExceptionUnchecked {
+        try {
+            Files.setPosixFilePermissions(Path.of(path), Set.of(PosixFilePermission.values()));
+        } catch (IOException e) {
+            throw new IOExceptionUnchecked(e);
         }
-        return new Time(time[0], time[1]);
     }
-    
-    /**
-     * Gets the current system time.
-     * 
-     * @return the system time as <i>milli-seconds since the epoch</i>.
-     */
-    public static final long getSystemTimeMillis()
-    {
-        return getSystemTime().getMillis();
+
+    public static boolean isSymbolicLink(String absolutePath) {
+        return Files.isSymbolicLink(Path.of(absolutePath));
     }
-    
-    //
-    // File functions
-    //
 
     /**
      * Creates a hard link <var>linkName</var> that points to <var>fileName</var>.
-     * 
+     *
      * @throws IOExceptionUnchecked If the underlying system call fails, e.g. because <var>linkName</var> already exists or <var>fileName</var> does
      *             not exist.
      */
@@ -811,16 +805,12 @@ public final class Unix
         {
             throw new NullPointerException("linkName");
         }
-        final int result = link(fileName, linkName);
-        if (result < 0)
-        {
-            throwLinkCreationException("hard", fileName, linkName, strerror(result));
-        }
+        link(fileName, linkName);
     }
 
     /**
      * Creates a symbolic link <var>linkName</var> that points to <var>fileName</var>.
-     * 
+     *
      * @throws IOExceptionUnchecked If the underlying system call fails, e.g. because <var>linkName</var> already exists.
      */
     public static final void createSymbolicLink(String fileName, String linkName)
@@ -834,494 +824,34 @@ public final class Unix
         {
             throw new NullPointerException("linkName");
         }
-        final int result = symlink(fileName, linkName);
-        if (result < 0)
-        {
-            throwLinkCreationException("symbolic", fileName, linkName, strerror(result));
-        }
-    }
 
-    private static Stat tryGetStat(String fileName) throws IOExceptionUnchecked
-    {
-        if (fileName == null)
-        {
-            throw new NullPointerException("fileName");
-        }
-        return stat(fileName);
-    }
-
-    private static Stat getStat(String fileName) throws IOExceptionUnchecked
-    {
-        if (fileName == null)
-        {
-            throw new NullPointerException("fileName");
-        }
-        final Stat result = stat(fileName);
-        if (result == null)
-        {
-            throwStatException(fileName, strerror());
-        }
-        return result;
-    }
-
-    private static Stat tryGetLStat(String linkName) throws IOExceptionUnchecked
-    {
-        if (linkName == null)
-        {
-            throw new NullPointerException("linkName");
-        }
-        return lstat(linkName);
-    }
-
-    private static Stat getLStat(String linkName) throws IOExceptionUnchecked
-    {
-        if (linkName == null)
-        {
-            throw new NullPointerException("linkName");
-        }
-        final Stat result = lstat(linkName);
-        if (result == null)
-        {
-            throwStatException(linkName, strerror());
-        }
-        return result;
-    }
-
-    /**
-     * Returns the inode for the <var>fileName</var>. Does not dereference a symbolic link. 
-     * 
-     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
-     * 
-     * @deprecated Use method {@link Stat#getInode(String)} from {@link #getLinkInfo(String)} instead.
-     */
-    @Deprecated
-    public static final long getInode(String linkName) throws IOExceptionUnchecked
-    {
-        return getLStat(linkName).getInode();
+        symlink(fileName, linkName);
     }
 
-    /**
-     * Returns the number of hard links for the <var>linkName</var>. Does not dereference a symbolic link.
-     * 
-     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
-     * 
-     * @deprecated Use method {@link Stat#getNumberOfHardLinks(String)} from {@link #getLinkInfo(String)} instead.
+    /*
+     * This method manages symbolic link creation using NIO API.
      */
-    @Deprecated
-    public static final int getNumberOfHardLinks(String linkName) throws IOExceptionUnchecked
-    {
-        return getLStat(linkName).getNumberOfHardLinks();
-    }
-
-    /**
-     * Returns <code>true</code> if <var>linkName</var> is a symbolic link and <code>false</code> otherwise.
-     * 
-     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
-     */
-    public static final boolean isSymbolicLink(String linkName) throws IOExceptionUnchecked
-    {
-        return getLStat(linkName).isSymbolicLink();
-    }
-
-    /**
-     * Returns the value of the symbolik link <var>linkName</var>, or <code>null</code>, if <var>linkName</var> is not a symbolic link.
-     * 
-     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
-     */
-    public static final String tryReadSymbolicLink(String linkName) throws IOExceptionUnchecked
-    {
-        final Stat stat = getLStat(linkName);
-        return stat.isSymbolicLink() ? readlink(linkName, (int) stat.getSize()) : null;
-    }
-
-    /**
-     * Returns the information about <var>fileName</var>.
-     * 
-     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the file does not exist.
-     */
-    public static final Stat getFileInfo(String fileName) throws IOExceptionUnchecked
-    {
-        return getStat(fileName);
-    }
-
-    /**
-     * Returns the information about <var>fileName</var>, or {@link NullPointerException}, if the information could not be obtained, e.g. because the
-     * file does not exist (call {@link #getLastError()} to find out what went wrong).
-     */
-    public static final Stat tryGetFileInfo(String fileName) throws IOExceptionUnchecked
-    {
-        return tryGetStat(fileName);
-    }
-
-    /**
-     * Returns the information about <var>linkName</var>.
-     * 
-     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
-     */
-    public static final Stat getLinkInfo(String linkName) throws IOExceptionUnchecked
-    {
-        return getLinkInfo(linkName, true);
-    }
-
-    /**
-     * Returns the information about <var>linkName</var>. If <code>readSymbolicLinkTarget == true</code>, then the symbolic link target is read when
-     * <var>linkName</var> is a symbolic link.
-     * 
-     * @throws IOExceptionUnchecked If the information could not be obtained, e.g. because the link does not exist.
-     */
-    public static final Stat getLinkInfo(String linkName, boolean readSymbolicLinkTarget)
-            throws IOExceptionUnchecked
-    {
-        final Stat stat = getLStat(linkName);
-        final String symbolicLinkOrNull =
-                (readSymbolicLinkTarget && stat.isSymbolicLink()) ? readlink(linkName,
-                        (int) stat.getSize()) : null;
-        stat.setSymbolicLinkOrNull(symbolicLinkOrNull);
-        return stat;
-    }
-
-    /**
-     * Returns the information about <var>linkName</var>, or {@link NullPointerException}, if the information could not be obtained, e.g. because the
-     * link does not exist (call {@link #getLastError()} to find out what went wrong).
-     */
-    public static final Stat tryGetLinkInfo(String linkName) throws IOExceptionUnchecked
-    {
-        return tryGetLinkInfo(linkName, true);
-    }
-
-    /**
-     * Returns the information about <var>linkName</var>, or <code>null</code> if the information can not be obtained, e.g. because the link does not
-     * exist (call {@link #getLastError()} to find out what went wrong). If <code>readSymbolicLinkTarget == true</code>, then the symbolic link target
-     * is read when <var>linkName</var> is a symbolic link.
-     */
-    public static final Stat tryGetLinkInfo(String linkName, boolean readSymbolicLinkTarget)
-            throws IOExceptionUnchecked
-    {
-        final Stat stat = tryGetLStat(linkName);
-        if (stat == null)
-        {
-            return null;
-        }
-        final String symbolicLinkOrNull =
-                (readSymbolicLinkTarget && stat.isSymbolicLink()) ? readlink(linkName,
-                        (int) stat.getSize()) : null;
-        stat.setSymbolicLinkOrNull(symbolicLinkOrNull);
-        return stat;
-    }
-
-    /**
-     * Sets the access mode of <var>filename</var> to the specified <var>mode</var> value.
-     * Dereferences a symbolic link.
-     */
-    public static final void setAccessMode(String fileName, short mode) throws IOExceptionUnchecked
-    {
-        if (fileName == null)
-        {
-            throw new NullPointerException("fileName");
-        }
-        final int result = chmod(fileName, mode);
-        if (result < 0)
-        {
-            throwFileException("set mode", fileName, strerror(result));
-        }
-    }
-
-    /**
-     * Sets the owner of <var>fileName</var> to the specified <var>uid</var> and <var>gid</var> values. 
-     * Dereferences a symbolic link.
-     */
-    public static final void setOwner(String fileName, int uid, int gid)
-            throws IOExceptionUnchecked
-    {
-        if (fileName == null)
-        {
-            throw new NullPointerException("fileName");
-        }
-        final int result = chown(fileName, uid, gid);
-        if (result < 0)
-        {
-            throwFileException("set owner", fileName, strerror(result));
-        }
-    }
-
-    /**
-     * Sets the owner of <var>fileName</var> to the <var>uid</var> and <var>gid</var> of the specified <code>user</code>. 
-     * Dereferences a symbolic link.
-     */
-    public static final void setOwner(String fileName, Password user)
-            throws IOExceptionUnchecked
-    {
-        if (fileName == null)
-        {
-            throw new NullPointerException("fileName");
-        }
-        final int result = chown(fileName, user.getUid(), user.getGid());
-        if (result < 0)
-        {
-            throwFileException("set owner", fileName, strerror(result));
-        }
-    }
-
-    /**
-     * Sets the owner of <var>linkName</var> to the specified <var>uid</var> and <var>gid</var> values. 
-     * Does not dereference a symbolic link.
-     */
-    public static final void setLinkOwner(String linkName, int uid, int gid)
-            throws IOExceptionUnchecked
-    {
-        if (linkName == null)
-        {
-            throw new NullPointerException("linkName");
-        }
-        final int result = lchown(linkName, uid, gid);
-        if (result < 0)
-        {
-            throwFileException("set link owner", linkName, strerror(result));
-        }
-    }
-
-    /**
-     * Sets the owner of <var>linkName</var> to the <var>uid</var> and <var>gid</var> of the specified <code>user</code>. 
-     * Does not dereference a symbolic link.
-     */
-    public static final void setLinkOwner(String linkName, Password user)
-            throws IOExceptionUnchecked
-    {
-        if (linkName == null)
-        {
-            throw new NullPointerException("linkName");
-        }
-        final int result = lchown(linkName, user.getUid(), user.getGid());
-        if (result < 0)
-        {
-            throwFileException("set owner", linkName, strerror(result));
-        }
-    }
-
-    /**
-     * Change link timestamps of a file, directory or link. Does not dereference a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     * @param accessTimeSecs The new access time in seconds since start of the epoch.
-     * @param accessTimeMicroSecs The micro-second part of the new access time.
-     * @param modificationTimeSecs The new modification time in seconds since start of the epoch.
-     * @param modificationTimeMicroSecs The micro-second part of the new modification time.
-     */
-    public static void setLinkTimestamps(String fileName,
-            long accessTimeSecs, long accessTimeMicroSecs,
-            long modificationTimeSecs, long modificationTimeMicroSecs) throws IOExceptionUnchecked
-    {
-        final int result = lutimes(fileName, accessTimeSecs, accessTimeMicroSecs,
-                            modificationTimeSecs, modificationTimeMicroSecs);
-        if (result < 0)
-        {
-            throwFileException("set file timestamps", fileName, strerror(result));
-        }
-    }
-
-    /**
-     * Change file timestamps of a file, directory or link. Does not dereference a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     * @param accessTimeSecs The new access time in seconds since start of the epoch.
-     * @param modificationTimeSecs The new modification time in seconds since start of the epoch.
-     */
-    public static void setLinkTimestamps(String fileName,
-            long accessTimeSecs, long modificationTimeSecs) throws IOExceptionUnchecked
-    {
-        setLinkTimestamps(fileName, accessTimeSecs, 0, modificationTimeSecs, 0);
-    }
-
-    /**
-     * Change file timestamps of a file, directory or link. Does not dereference a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     * @param accessTime The new access time as {@link Time} object.
-     * @param modificationTime The new modification time as {@link Time} object.
-     */
-    public static void setLinkTimestamps(String fileName,
-            Time accessTime, Time modificationTime) throws IOExceptionUnchecked
-    {
-        setLinkTimestamps(fileName, accessTime.getSecs(), accessTime.getMicroSecPart(), 
-                modificationTime.getSecs(), modificationTime.getMicroSecPart());
-    }
-
-    /**
-     * Change file timestamps of a file, directory or link to the current time. Does not dereference a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     */
-    public static void setLinkTimestamps(String fileName) throws IOExceptionUnchecked
-    {
-        final Time now = getSystemTime();
-        final int result = lutimes(fileName, now.getSecs(), now.getMicroSecPart(), now.getSecs(), now.getMicroSecPart());
-        if (result < 0)
-        {
-            throwFileException("set file timestamps", fileName, strerror(result));
+    public static final void symlink(String fileName, String linkName) throws IOExceptionUnchecked {
+        try {
+            Path file = Path.of(fileName);
+            Path link = Path.of(linkName);
+            Files.createSymbolicLink(link, file);// Creates the link
+        } catch (IOException exception) {
+            throw new IOExceptionUnchecked(exception);
         }
     }
 
-    /**
-     * Change file timestamps of a file, directory or link. Dereferences a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     * @param accessTimeSecs The new access time in seconds since start of the epoch.
-     * @param accessTimeMicroSecs The micro-second part of the new access time.
-     * @param modificationTimeSecs The new modification time in seconds since start of the epoch.
-     * @param modificationTimeMicroSecs The micro-second part of the new modification time.
+    /*
+     * This method manages link creation using NIO API.
      */
-    public static void setFileTimestamps(String fileName,
-            long accessTimeSecs, long accessTimeMicroSecs,
-            long modificationTimeSecs, long modificationTimeMicroSecs) throws IOExceptionUnchecked
-    {
-        final int result = utimes(fileName, accessTimeSecs, accessTimeMicroSecs,
-                            modificationTimeSecs, modificationTimeMicroSecs);
-        if (result < 0)
-        {
-            throwFileException("set file timestamps", fileName, strerror(result));
+    public static final void link(String fileName, String linkName) throws IOExceptionUnchecked {
+        try {
+            Path file = Path.of(fileName);
+            Path link = Path.of(linkName);
+            Files.createLink(link, file); // Creates the link
+        } catch (IOException exception) {
+            throw new IOExceptionUnchecked(exception);
         }
     }
 
-    /**
-     * Change file timestamps of a file, directory or link. Does not dereference a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     * @param accessTimeSecs The new access time in seconds since start of the epoch.
-     * @param modificationTimeSecs The new modification time in seconds since start of the epoch.
-     */
-    public static void setFileTimestamps(String fileName,
-            long accessTimeSecs, long modificationTimeSecs) throws IOExceptionUnchecked
-    {
-        setFileTimestamps(fileName, accessTimeSecs, 0, modificationTimeSecs, 0);
-    }
-
-    /**
-     * Change file timestamps of a file, directory or link. Does not dereference a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     * @param accessTime The new access time as {@link Time} object.
-     * @param modificationTime The new modification time as {@link Time} object.
-     */
-    public static void setFileTimestamps(String fileName,
-            Time accessTime, Time modificationTime) throws IOExceptionUnchecked
-    {
-        setFileTimestamps(fileName, accessTime.getSecs(), accessTime.getMicroSecPart(), 
-                modificationTime.getSecs(), modificationTime.getMicroSecPart());
-    }
-
-    /**
-     * Change file timestamps of a file, directory or link to the current time. Does not dereference a symbolic link.
-     * 
-     * @param fileName The name of the file or link to change the timestamp of.
-     */
-    public static void setFileTimestamps(String fileName) throws IOExceptionUnchecked
-    {
-        final Time now = getSystemTime();
-        final int result = utimes(fileName, now.getSecs(), now.getMicroSecPart(), now.getSecs(), now.getMicroSecPart());
-        if (result < 0)
-        {
-            throwFileException("set file timestamps", fileName, strerror(result));
-        }
-    }
-
-    //
-    // User functions
-    //
-
-    /**
-     * Returns the name of the user identified by <var>uid</var>.
-     */
-    public static final String tryGetUserNameForUid(int uid)
-    {
-        return getuser(uid);
-    }
-
-    /**
-     * Returns the uid of the <var>userName</var>, or <code>-1</code>, if no user with this name exists.
-     */
-    public static final int getUidForUserName(String userName)
-    {
-        if (userName == null)
-        {
-            throw new NullPointerException("userName");
-        }
-        return getuid(userName);
-    }
-
-    /**
-     * Returns the {@link Password} for the given <var>userName</var>, or <code>null</code>, if no user with that name exists.
-     */
-    public static final Password tryGetUserByName(String userName)
-    {
-        if (userName == null)
-        {
-            throw new NullPointerException("userName");
-        }
-        return getpwnam(userName);
-    }
-
-    /**
-     * Returns the {@link Password} for the given <var>userName</var>, or <code>null</code>, if no user with that name exists.
-     */
-    public static final Password tryGetUserByUid(int uid)
-    {
-        return getpwuid(uid);
-    }
-
-    //
-    // Group functions
-    //
-
-    /**
-     * Returns the name of the group identified by <var>gid</var>, or <code>null</code>, if no group with that <var>gid</var> exists.
-     */
-    public static final String tryGetGroupNameForGid(int gid)
-    {
-        return getgroup(gid);
-    }
-
-    /**
-     * Returns the gid of the <var>groupName</var>, or <code>-1</code>, if no group with this name exists.
-     */
-    public static final int getGidForGroupName(String groupName)
-    {
-        if (groupName == null)
-        {
-            throw new NullPointerException("groupName");
-        }
-        return getgid(groupName);
-    }
-
-    /**
-     * Returns the {@link Group} for the given <var>groupName</var>, or <code>null</code>, if no group with that name exists.
-     */
-    public static final Group tryGetGroupByName(String groupName)
-    {
-        if (groupName == null)
-        {
-            throw new NullPointerException("groupName");
-        }
-        return getgrnam(groupName);
-    }
-
-    /**
-     * Returns the {@link Group} for the given <var>gid</var>, or <code>null</code>, if no group with that gid exists.
-     */
-    public static final Group tryGetGroupByGid(int gid)
-    {
-        return getgrgid(gid);
-    }
-
-    //
-    // Error
-    //
-
-    /**
-     * Returns the error string for the given <var>errnum</var>.
-     */
-    public static final String getErrorString(int errnum)
-    {
-        return strerror(errnum);
-    }
-
 }
diff --git a/lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixRootTests.java b/lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixRootTests.java
deleted file mode 100644
index a122ca813fa..00000000000
--- a/lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixRootTests.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package ch.systemsx.cisd.base.unix;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.charset.Charset;
-
-import org.apache.commons.io.FileUtils;
-import org.testng.annotations.Test;
-
-import ch.systemsx.cisd.base.BuildAndEnvironmentInfo;
-import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
-import ch.systemsx.cisd.base.unix.Unix.Stat;
-
-/**
- * Tests of the {@link Unix} class that can only be performed as user root.
- */
-public class UnixRootTests extends AbstractFileSystemTestCase
-{
-    @Test(groups =
-        { "requires_unix" })
-    public void testChown() throws IOException
-    {
-        if (Unix.getUid() != 0)
-        {
-            System.out.println("Skipping test as we are not root.");
-            return;
-        }
-        final short accessMode = (short) 0777;
-        final String content = "someText\n";
-        final File f = new File(workingDirectory, "someFile");
-        final File s = new File(workingDirectory, "MyLink");
-        FileUtils.writeStringToFile(f, content, Charset.defaultCharset());
-        Unix.setAccessMode(f.getAbsolutePath(), accessMode);
-        final Stat info = Unix.getLinkInfo(f.getAbsolutePath());
-        Unix.setOwner(f.getAbsolutePath(), info.getUid(), info.getGid());
-        assertEquals(1, info.getNumberOfHardLinks());
-        assertEquals(content.length(), info.getSize());
-        assertEquals(accessMode, info.getPermissions());
-        final Unix.Password nobody = Unix.tryGetUserByName("nobody");
-        assertNotNull(nobody);
-        final Unix.Password daemon = Unix.tryGetUserByName("daemon");
-        assertNotNull(daemon);
-        Unix.setOwner(f.getAbsolutePath(), nobody);
-        Unix.createSymbolicLink(f.getAbsolutePath(), s.getAbsolutePath());
-        Unix.setLinkOwner(s.getAbsolutePath(), daemon);
-
-        final Unix.Stat fileInfo = Unix.getFileInfo(s.getAbsolutePath());
-        assertEquals(nobody.getUid(), fileInfo.getUid());
-        assertEquals(nobody.getGid(), fileInfo.getGid());
-        
-        final Unix.Stat linkInfo = Unix.getLinkInfo(s.getAbsolutePath());
-        assertEquals(daemon.getUid(), linkInfo.getUid());
-        assertEquals(daemon.getGid(), linkInfo.getGid());
-    }
-    
-    public static void main(String[] args)  throws Throwable
-    {
-        System.out.println(BuildAndEnvironmentInfo.INSTANCE);
-        System.out.println("Test class: " + UnixRootTests.class.getSimpleName());
-        System.out.println();
-        if (Unix.isOperational() == false)
-        {
-            System.err.println("No unix library found.");
-            System.exit(1);
-        }
-        final UnixRootTests test = new UnixRootTests();
-        try
-        {
-            for (Method m : UnixRootTests.class.getMethods())
-            {
-                final Test testAnnotation = m.getAnnotation(Test.class);
-                if (testAnnotation == null)
-                {
-                    continue;
-                }
-                System.out.println("Running " + m.getName());
-                test.setUp();
-                try
-                {
-                    m.invoke(test);
-                } catch (InvocationTargetException wrapperThrowable)
-                {
-                    final Throwable th = wrapperThrowable.getCause();
-                    boolean exceptionFound = false;
-                    for (Class<?> expectedExClazz : testAnnotation.expectedExceptions())
-                    {
-                        if (expectedExClazz == th.getClass())
-                        {
-                            exceptionFound = true;
-                            break;
-                        }
-                    }
-                    if (exceptionFound == false)
-                    {
-                        throw th;
-                    }
-                }
-            }
-            System.out.println("Tests OK!");
-        } finally
-        {
-            test.afterClass();
-        }
-    }
-
-}
diff --git a/lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixTests.java b/lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixTests.java
index 324c1fc59f6..9520db6a074 100644
--- a/lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixTests.java
+++ b/lib-base/sourceTest/java/ch/systemsx/cisd/base/unix/UnixTests.java
@@ -16,28 +16,21 @@
 
 package ch.systemsx.cisd.base.unix;
 
-import static org.testng.Assert.assertNotEquals;
+import ch.rinn.restrictions.Friend;
+import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
+import org.apache.commons.io.FileUtils;
+import org.testng.annotations.Test;
 
 import java.io.File;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.nio.charset.Charset;
 
-import org.apache.commons.io.FileUtils;
-import org.testng.annotations.Test;
-
-import ch.rinn.restrictions.Friend;
-import ch.systemsx.cisd.base.BuildAndEnvironmentInfo;
-import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
-import ch.systemsx.cisd.base.unix.Unix.Group;
-import ch.systemsx.cisd.base.unix.Unix.Password;
-import ch.systemsx.cisd.base.unix.Unix.Stat;
+import static ch.systemsx.cisd.base.unix.Unix.Stat;
 
 /**
  * Test cases for the {@link Unix} system calls.
- * 
- * @author Bernd Rinn
+ *
+ * @author Juan Fuentes
  */
 @Friend(toClasses = Unix.class)
 public class UnixTests extends AbstractFileSystemTestCase
@@ -52,8 +45,7 @@ public class UnixTests extends AbstractFileSystemTestCase
         super(cleanAfterMethod);
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetLinkInfoRegularFile() throws IOException
     {
         final short accessMode = (short) 0777;
@@ -72,15 +64,13 @@ public class UnixTests extends AbstractFileSystemTestCase
         assertEquals(f.lastModified()/1000, info.getLastModified());
     }
 
-    @Test(groups =
-        { "requires_unix" }, expectedExceptions = NullPointerException.class)
+    @Test(expectedExceptions = NullPointerException.class)
     public void testGetLinkNull() throws IOException
     {
         Unix.getLinkInfo(null);
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetLinkInfoDirectory() throws IOException
     {
         final File d = new File(workingDirectory, "someDir");
@@ -91,8 +81,7 @@ public class UnixTests extends AbstractFileSystemTestCase
         assertFalse(info.isSymbolicLink());
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetLinkInfoSymLink() throws IOException
     {
         final File f = new File(workingDirectory, "someOtherFile");
@@ -116,126 +105,7 @@ public class UnixTests extends AbstractFileSystemTestCase
         assertNull(info2.tryGetSymbolicLink());
     }
 
-    @Test(groups =
-        { "requires_unix" })
-    public void testTouchSymLinkAndFileRealtimeTimer() throws IOException, InterruptedException
-    {
-        if (BuildAndEnvironmentInfo.INSTANCE.getOS().contains("2.6.32"))
-        {
-            System.out.println("  ...skipping as CentOS6 does not yet support the realtime timer.");
-            return;
-        }
-        Unix.setUseUnixRealtimeTimer(true);
-        final File f = new File(workingDirectory, "someOtherFile");
-        final String content = "someMoreText\n";
-        FileUtils.writeStringToFile(f, content, Charset.defaultCharset());
-        final File s = new File(workingDirectory, "someLink");
-        Unix.createSymbolicLink(f.getAbsolutePath(), s.getAbsolutePath());
-        final Stat info = Unix.getLinkInfo(s.getAbsolutePath());
-        assertEquals(1, info.getNumberOfHardLinks());
-        assertEquals(FileLinkType.SYMLINK, info.getLinkType());
-        assertTrue(info.isSymbolicLink());
-        assertEquals(f.getAbsolutePath(), info.tryGetSymbolicLink());
-        assertEquals(f.getAbsolutePath(), Unix.tryReadSymbolicLink(s.getAbsolutePath()));
-        assertNull(Unix.getLinkInfo(s.getAbsolutePath(), false).tryGetSymbolicLink());
-        final long lastMicros = info.getLastModifiedTime().getMicroSecPart();
-        final long newLastModifiedLink = info.getLastModifiedTime().getSecs() - 24 * 3600;
-        Unix.setLinkTimestamps(s.getAbsolutePath(), newLastModifiedLink, lastMicros, newLastModifiedLink, lastMicros);
-
-        final long newLastModifiedFile = info.getLastModifiedTime().getSecs() - 2 * 24 * 3600;
-        Unix.setFileTimestamps(f.getAbsolutePath(), newLastModifiedFile, lastMicros, newLastModifiedFile, lastMicros);
-
-        final Stat info2l = Unix.getLinkInfo(s.getAbsolutePath(), false);
-        assertEquals(newLastModifiedLink, info2l.getLastModifiedTime().getSecs());
-        assertEquals(lastMicros, info2l.getLastModifiedTime().getMicroSecPart());
-        assertEquals(newLastModifiedLink, info2l.getLastAccessTime().getSecs());
-        assertEquals(lastMicros, info2l.getLastAccessTime().getMicroSecPart());
-
-        final Stat info2f = Unix.getFileInfo(s.getAbsolutePath());
-        final Stat info2f2 = Unix.getLinkInfo(f.getAbsolutePath());
-        assertNotEquals(info2l.getLastModifiedTime(), info2f2.getLastModifiedTime());
-        assertEquals(info2f2.getLastModifiedTime(), info2f.getLastModifiedTime());
-        assertEquals(newLastModifiedFile, info2f.getLastModifiedTime().getSecs());
-        assertEquals(lastMicros, info2f.getLastModifiedTime().getMicroSecPart());
-        assertEquals(newLastModifiedFile, info2f.getLastAccessTime().getSecs());
-        assertEquals(lastMicros, info2f.getLastAccessTime().getMicroSecPart());
-
-        Thread.sleep(10);
-
-        final Unix.Time now1 = Unix.getSystemTime();
-        assertNotEquals(0, now1.getNanoSecPart() % 1_000);
-        Unix.setLinkTimestamps(s.getAbsolutePath());
-        final Unix.Time now2 = Unix.getSystemTime();
-        final Stat info3 = Unix.getLinkInfo(s.getAbsolutePath());
-        
-        assertTrue(now1.getSecs() <= info3.getLastModified() && info3.getLastModified() <= now2.getSecs());
-        assertTrue(now1.getMicroSecPart() <= info3.getLastModifiedTime().getMicroSecPart() && info.getLastModifiedTime().getMilliSecPart() <= now2.getMicroSecPart());
-        assertTrue(now1.getSecs() <= info3.getLastAccess() && info3.getLastAccess() <= now2.getSecs());
-        assertTrue(now1.getMicroSecPart() <= info3.getLastAccessTime().getMicroSecPart() && info.getLastAccessTime().getMilliSecPart() <= now2.getMicroSecPart());
-        assertNotEquals(lastMicros, info3.getLastModifiedTime().getMicroSecPart());
-        assertNotEquals(lastMicros, info3.getLastAccessTime().getMicroSecPart());
-
-    }
-
-    @Test(groups =
-        { "requires_unix" })
-    public void testTouchSymLinkAndFile() throws IOException, InterruptedException
-    {
-        Unix.setUseUnixRealtimeTimer(false);
-        final File f = new File(workingDirectory, "someOtherFile");
-        final String content = "someMoreText\n";
-        FileUtils.writeStringToFile(f, content, Charset.defaultCharset());
-        final File s = new File(workingDirectory, "someLink");
-        Unix.createSymbolicLink(f.getAbsolutePath(), s.getAbsolutePath());
-        final Stat info = Unix.getLinkInfo(s.getAbsolutePath());
-        assertEquals(1, info.getNumberOfHardLinks());
-        assertEquals(FileLinkType.SYMLINK, info.getLinkType());
-        assertTrue(info.isSymbolicLink());
-        assertEquals(f.getAbsolutePath(), info.tryGetSymbolicLink());
-        assertEquals(f.getAbsolutePath(), Unix.tryReadSymbolicLink(s.getAbsolutePath()));
-        assertNull(Unix.getLinkInfo(s.getAbsolutePath(), false).tryGetSymbolicLink());
-        final long lastMicros = info.getLastModifiedTime().getMicroSecPart();
-        final long newLastModifiedLink = info.getLastModifiedTime().getSecs() - 24 * 3600;
-        Unix.setLinkTimestamps(s.getAbsolutePath(), newLastModifiedLink, lastMicros, newLastModifiedLink, lastMicros);
-
-        final long newLastModifiedFile = info.getLastModifiedTime().getSecs() - 2 * 24 * 3600;
-        Unix.setFileTimestamps(f.getAbsolutePath(), newLastModifiedFile, lastMicros, newLastModifiedFile, lastMicros);
-
-        final Stat info2l = Unix.getLinkInfo(s.getAbsolutePath(), false);
-        assertEquals(newLastModifiedLink, info2l.getLastModifiedTime().getSecs());
-        assertEquals(lastMicros, info2l.getLastModifiedTime().getMicroSecPart());
-        assertEquals(newLastModifiedLink, info2l.getLastAccessTime().getSecs());
-        assertEquals(lastMicros, info2l.getLastAccessTime().getMicroSecPart());
-
-        final Stat info2f = Unix.getFileInfo(s.getAbsolutePath());
-        final Stat info2f2 = Unix.getLinkInfo(f.getAbsolutePath());
-        assertNotEquals(info2l.getLastModifiedTime(), info2f2.getLastModifiedTime());
-        assertEquals(info2f2.getLastModifiedTime(), info2f.getLastModifiedTime());
-        assertEquals(newLastModifiedFile, info2f.getLastModifiedTime().getSecs());
-        assertEquals(lastMicros, info2f.getLastModifiedTime().getMicroSecPart());
-        assertEquals(newLastModifiedFile, info2f.getLastAccessTime().getSecs());
-        assertEquals(lastMicros, info2f.getLastAccessTime().getMicroSecPart());
-
-
-        Thread.sleep(10);
-
-        final Unix.Time now1 = Unix.getSystemTime();
-        assertEquals(0, now1.getNanoSecPart() % 1_000);
-        Unix.setLinkTimestamps(s.getAbsolutePath());
-        final Unix.Time now2 = Unix.getSystemTime();
-        final Stat info3 = Unix.getLinkInfo(s.getAbsolutePath());
-        
-        assertTrue(now1.getSecs() <= info3.getLastModified() && info3.getLastModified() <= now2.getSecs());
-        assertTrue(now1.getMicroSecPart() <= info3.getLastModifiedTime().getMicroSecPart() && info.getLastModifiedTime().getMilliSecPart() <= now2.getMicroSecPart());
-        assertTrue(now1.getSecs() <= info3.getLastAccess() && info3.getLastAccess() <= now2.getSecs());
-        assertTrue(now1.getMicroSecPart() <= info3.getLastAccessTime().getMicroSecPart() && info.getLastAccessTime().getMilliSecPart() <= now2.getMicroSecPart());
-        assertNotEquals(lastMicros, info3.getLastModifiedTime().getMicroSecPart());
-        assertNotEquals(lastMicros, info3.getLastAccessTime().getMicroSecPart());
-
-    }
-
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetLinkInfoSymLinkDanglingLink() throws IOException
     {
         final File s = new File(workingDirectory, "someDanglingLink");
@@ -247,38 +117,34 @@ public class UnixTests extends AbstractFileSystemTestCase
         assertTrue(info.isSymbolicLink());
         final Stat info2 = Unix.tryGetFileInfo(s.getAbsolutePath());
         assertNull(info2);
-        assertEquals("No such file or directory", Unix.getLastError());
+        //        assertEquals("No such file or directory", Unix.getLastError());
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetLinkInfoNonExistent() throws IOException
     {
         final File s = new File(workingDirectory, "nonExistent");
         final Stat info = Unix.tryGetLinkInfo(s.getAbsolutePath());
         assertNull(info);
-        assertEquals("No such file or directory", Unix.getLastError());
+        //        assertEquals("No such file or directory", Unix.getLastError());
         final Stat info2 = Unix.tryGetFileInfo(s.getAbsolutePath());
         assertNull(info2);
-        assertEquals("No such file or directory", Unix.getLastError());
+        //        assertEquals("No such file or directory", Unix.getLastError());
     }
 
-    @Test(groups =
-        { "requires_unix" }, expectedExceptions = NullPointerException.class)
+    @Test(expectedExceptions = NullPointerException.class)
     public void testCreateSymbolicLinkNull() throws IOException
     {
         Unix.createSymbolicLink(null, null);
     }
 
-    @Test(groups =
-        { "requires_unix" }, expectedExceptions = NullPointerException.class)
+    @Test(expectedExceptions = NullPointerException.class)
     public void testCreateHardLinkNull() throws IOException
     {
         Unix.createHardLink(null, null);
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetLinkInfoHardLink() throws IOException
     {
         final File f = new File(workingDirectory, "someOtherFile");
@@ -292,52 +158,45 @@ public class UnixTests extends AbstractFileSystemTestCase
         assertNull(info.tryGetSymbolicLink());
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetUid()
     {
         assertTrue(Unix.getUid() >= 0);
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetEuid()
     {
         assertTrue(Unix.getEuid() >= 0);
         assertEquals(Unix.getUid(), Unix.getEuid());
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetGid()
     {
         assertTrue(Unix.getGid() >= 0);
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetEgid()
     {
         assertTrue(Unix.getEgid() >= 0);
         assertEquals(Unix.getGid(), Unix.getEgid());
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetUidForUserName()
     {
         assertEquals(0, Unix.getUidForUserName("root"));
     }
 
-    @Test(groups =
-        { "requires_unix" }, expectedExceptions = NullPointerException.class)
+    @Test(expectedExceptions = NullPointerException.class)
     public void testGetUidForUserNameNull() throws IOException
     {
         Unix.getUidForUserName(null);
     }
 
-    @Test(groups =
-        { "requires_unix" })
+    @Test
     public void testGetGidForGroupName()
     {
         final String rootGroup = Unix.tryGetGroupNameForGid(0);
@@ -345,154 +204,9 @@ public class UnixTests extends AbstractFileSystemTestCase
         assertEquals(0, Unix.getGidForGroupName(rootGroup));
     }
 
-    @Test(groups =
-        { "requires_unix" }, expectedExceptions = NullPointerException.class)
+    @Test(expectedExceptions = NullPointerException.class)
     public void testGetGidForGroupNameNull() throws IOException
     {
         Unix.getGidForGroupName(null);
     }
-
-    @Test(groups =
-        { "requires_unix" })
-    public void testTryGetGroupByName()
-    {
-        final String rootGroup = Unix.tryGetGroupNameForGid(0);
-        final Group group = Unix.tryGetGroupByName(rootGroup);
-        assertNotNull(group);
-        assertEquals(rootGroup, group.getGroupName());
-        assertEquals(0, group.getGid());
-        assertNotNull(group.getGroupMembers());
-    }
-
-    @Test(groups =
-        { "requires_unix" }, expectedExceptions = NullPointerException.class)
-    public void testTryGetGroupByNameNull() throws IOException
-    {
-        Unix.tryGetGroupByName(null);
-    }
-
-    @Test(groups =
-        { "requires_unix" })
-    public void testTryGetGroupByGid()
-    {
-        final Group group = Unix.tryGetGroupByGid(0);
-        assertNotNull(group);
-        final String rootGroup = group.getGroupName();
-        assertTrue(rootGroup, "root".equals(rootGroup) || "wheel".equals(rootGroup));
-        assertEquals(0, group.getGid());
-        assertNotNull(group.getGroupMembers());
-    }
-
-    @Test(groups =
-        { "requires_unix" })
-    public void testTryGetUserByName()
-    {
-        final Password user = Unix.tryGetUserByName("root");
-        assertNotNull(user);
-        assertEquals("root", user.getUserName());
-        assertEquals(0, user.getUid());
-        assertEquals(0, user.getGid());
-        assertNotNull(user.getUserFullName());
-        assertNotNull(user.getHomeDirectory());
-        assertNotNull(user.getShell());
-        assertTrue(user.getShell().startsWith("/"));
-    }
-
-    @Test(groups =
-        { "requires_unix" }, expectedExceptions = NullPointerException.class)
-    public void testTryGetUserByNameNull() throws IOException
-    {
-        Unix.tryGetUserByName(null);
-    }
-
-    @Test(groups =
-        { "requires_unix" })
-    public void testTryGetUserByUid()
-    {
-        final Password user = Unix.tryGetUserByUid(0);
-        assertNotNull(user);
-        assertEquals("root", user.getUserName());
-        assertEquals(0, user.getUid());
-        assertEquals(0, user.getGid());
-        assertNotNull(user.getUserFullName());
-        assertNotNull(user.getHomeDirectory());
-        assertNotNull(user.getShell());
-        assertTrue(user.getShell().startsWith("/"));
-    }
-
-    @Test(groups =
-        { "requires_unix" })
-    public void testDetectProcess()
-    {
-        assertTrue(Unix.canDetectProcesses());
-        assertTrue(Unix.isProcessRunningPS(Unix.getPid()));
-    }
-
-    public static void main(String[] args) throws Throwable
-    {
-        System.out.println(BuildAndEnvironmentInfo.INSTANCE);
-        System.out.println("Test class: " + UnixTests.class.getSimpleName());
-        System.out.println();
-        if (Unix.isOperational() == false)
-        {
-            System.err.println("No unix library found.");
-            System.exit(1);
-        }
-        boolean stopOnError = args.length > 0 && "stopOnError".equalsIgnoreCase(args[0]);
-        int failed = 0;
-        final UnixTests test = new UnixTests();
-        try
-        {
-            for (Method m : UnixTests.class.getMethods())
-            {
-                final Test testAnnotation = m.getAnnotation(Test.class);
-                if (testAnnotation == null)
-                {
-                    continue;
-                }
-                System.out.println("Running " + m.getName());
-                test.setUp();
-                try
-                {
-                    m.invoke(test);
-                } catch (InvocationTargetException wrapperThrowable)
-                {
-                    final Throwable th = wrapperThrowable.getCause();
-                    boolean exceptionFound = false;
-                    for (Class<?> expectedExClazz : testAnnotation.expectedExceptions())
-                    {
-                        if (expectedExClazz == th.getClass())
-                        {
-                            exceptionFound = true;
-                            break;
-                        }
-                    }
-                    if (exceptionFound == false)
-                    {
-                        ++failed;
-                        System.out.println("Caught exception in method " + m.getName());
-                        th.printStackTrace();
-                        if (stopOnError)
-                        {
-                            System.exit(1);
-                        }
-                    }
-                }
-            }
-            if (failed == 0)
-            {
-                System.out.println("Tests OK!");
-            } else
-            {   
-                System.out.printf("%d tests FAILED!\n", failed);
-            }
-        } finally
-        {
-            if (failed == 0)
-            {
-                test.afterClass();
-            }
-        }
-    }
-
 }
diff --git a/lib-commonbase/source/java/ch/systemsx/cisd/common/io/Posix.java b/lib-commonbase/source/java/ch/systemsx/cisd/common/io/Posix.java
index 248a13f7478..3a955314b0d 100644
--- a/lib-commonbase/source/java/ch/systemsx/cisd/common/io/Posix.java
+++ b/lib-commonbase/source/java/ch/systemsx/cisd/common/io/Posix.java
@@ -26,9 +26,10 @@ import java.io.InputStreamReader;
 import java.nio.file.Files;
 import java.nio.file.LinkOption;
 import java.nio.file.Path;
-import java.nio.file.attribute.*;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
 import java.time.Instant;
-import java.time.temporal.ChronoUnit;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -302,72 +303,6 @@ public final class Posix
         return stat.isSymbolicLink() ? stat.tryGetSymbolicLink() : null;
     }
 
-//    /**
-//     * Change link timestamps of a file, directory or link. Does not dereference a symbolic link.
-//     *
-//     * @param fileName The name of the file or link to change the timestamp of.
-//     * @param accessTimeSecs The new access time in seconds since start of the epoch.
-//     * @param accessTimeMicroSecs The micro-second part of the new access time.
-//     * @param modificationTimeSecs The new modification time in seconds since start of the epoch.
-//     * @param modificationTimeMicroSecs The micro-second part of the new modification time.
-//     */
-//    public static void setLinkTimestamps(String fileName,
-//            long accessTimeSecs,
-//            long accessTimeMicroSecs,
-//            long modificationTimeSecs,
-//            long modificationTimeMicroSecs)
-//    {
-//        try
-//        {
-//            Instant accessTimeInstant = Instant.ofEpochSecond(accessTimeSecs).plus(accessTimeMicroSecs, ChronoUnit.MICROS);
-//            FileTime accessTime = FileTime.from(accessTimeInstant);
-//            Instant modifiedTimeInstant = Instant.ofEpochSecond(modificationTimeSecs).plus(modificationTimeMicroSecs, ChronoUnit.MICROS);
-//            FileTime modifiedTime = FileTime.from(modifiedTimeInstant);
-//            Files.getFileAttributeView(Path.of(fileName), PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).setTimes(modifiedTime, accessTime, null);
-//            Files.getFileAttributeView(Path.of(fileName), PosixFileAttributeView.class).setTimes(modifiedTime, accessTime, null);
-//        } catch (IOException e)
-//        {
-//            throw new IOExceptionUnchecked(e);
-//        }
-//    }
-//
-//    /**
-//     * Change file timestamps of a file, directory or link to the current time. Does not dereference a symbolic link.
-//     *
-//     * @param fileName The name of the file or link to change the timestamp of.
-//     */
-//    public static void setLinkTimestamps(String fileName) throws IOExceptionUnchecked
-//    {
-//        Time time = Time.getInstance();
-//        setLinkTimestamps(fileName, time.getSecs(), time.getMicroSecPart(), time.getSecs(), time.getMicroSecPart());
-//    }
-//
-//    /**
-//     * Change file timestamps of a file, directory or link. Dereferences a symbolic link.
-//     *
-//     * @param fileName The name of the file or link to change the timestamp of.
-//     * @param accessTimeSecs The new access time in seconds since start of the epoch.
-//     * @param accessTimeMicroSecs The micro-second part of the new access time.
-//     * @param modificationTimeSecs The new modification time in seconds since start of the epoch.
-//     * @param modificationTimeMicroSecs The micro-second part of the new modification time.
-//     */
-//    public static void setFileTimestamps(String fileName,
-//            long accessTimeSecs, long accessTimeMicroSecs,
-//            long modificationTimeSecs, long modificationTimeMicroSecs) throws IOExceptionUnchecked
-//    {
-//        try
-//        {
-//            Instant accessTimeInstant = Instant.ofEpochSecond(accessTimeSecs).plus(accessTimeMicroSecs, ChronoUnit.MICROS);
-//            FileTime accessTime = FileTime.from(accessTimeInstant);
-//            Instant modifiedTimeInstant = Instant.ofEpochSecond(modificationTimeSecs).plus(modificationTimeMicroSecs, ChronoUnit.MICROS);
-//            FileTime modifiedTime = FileTime.from(modifiedTimeInstant);
-//            Files.getFileAttributeView(Path.of(fileName), PosixFileAttributeView.class).setTimes(modifiedTime, accessTime, null);
-//        } catch (IOException e)
-//        {
-//            throw new IOExceptionUnchecked(e);
-//        }
-//    }
-
     //
     // File related methods
     //
diff --git a/lib-commonbase/sourceTest/java/ch/systemsx/cisd/common/io/PosixTest.java b/lib-commonbase/sourceTest/java/ch/systemsx/cisd/common/io/PosixTest.java
index f22ecb6e7ee..ee1bf76c81a 100644
--- a/lib-commonbase/sourceTest/java/ch/systemsx/cisd/common/io/PosixTest.java
+++ b/lib-commonbase/sourceTest/java/ch/systemsx/cisd/common/io/PosixTest.java
@@ -17,7 +17,6 @@
 package ch.systemsx.cisd.common.io;
 
 import ch.rinn.restrictions.Friend;
-import ch.systemsx.cisd.base.BuildAndEnvironmentInfo;
 import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
 import ch.systemsx.cisd.base.unix.FileLinkType;
 import org.apache.commons.io.FileUtils;
@@ -27,7 +26,6 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
 
-import static org.testng.Assert.assertNotEquals;
 import static ch.systemsx.cisd.common.io.Posix.Stat;
 
 /**
@@ -108,121 +106,6 @@ public class PosixTest extends AbstractFileSystemTestCase
         assertNull(info2.tryGetSymbolicLink());
     }
 
-//    @Test
-//    public void testTouchSymLinkAndFileRealtimeTimer() throws IOException, InterruptedException
-//    {
-//        if (BuildAndEnvironmentInfo.INSTANCE.getOS().contains("2.6.32"))
-//        {
-//            System.out.println("  ...skipping as CentOS6 does not yet support the realtime timer.");
-//            return;
-//        }
-////        Posix.setUseUnixRealtimeTimer(true);
-//        final File f = new File(workingDirectory, "someOtherFile");
-//        final String content = "someMoreText\n";
-//        FileUtils.writeStringToFile(f, content, Charset.defaultCharset());
-//        final File s = new File(workingDirectory, "someLink");
-//        Posix.createSymbolicLink(f.getAbsolutePath(), s.getAbsolutePath());
-//        final Stat info = Posix.getLinkInfo(s.getAbsolutePath());
-//        assertEquals(1, info.getNumberOfHardLinks());
-//        assertEquals(FileLinkType.SYMLINK, info.getLinkType());
-//        assertTrue(info.isSymbolicLink());
-//        assertEquals(f.getAbsolutePath(), info.tryGetSymbolicLink());
-//        assertEquals(f.getAbsolutePath(), Posix.tryReadSymbolicLink(s.getAbsolutePath()));
-//        assertNull(Posix.getLinkInfo(s.getAbsolutePath(), false).tryGetSymbolicLink());
-//        final long lastMicros = info.getLastModifiedTime().getMicroSecPart();
-//        final long newLastModifiedLink = info.getLastModifiedTime().getSecs() - 24 * 3600;
-//        Posix.setLinkTimestamps(s.getAbsolutePath(), newLastModifiedLink, lastMicros, newLastModifiedLink, lastMicros);
-//
-//        final long newLastModifiedFile = info.getLastModifiedTime().getSecs() - 2 * 24 * 3600;
-//        Posix.setFileTimestamps(f.getAbsolutePath(), newLastModifiedFile, lastMicros, newLastModifiedFile, lastMicros);
-//
-//        final Stat info2l = Posix.getLinkInfo(s.getAbsolutePath(), false);
-//        assertEquals(newLastModifiedLink, info2l.getLastModifiedTime().getSecs());
-//        assertEquals(lastMicros, info2l.getLastModifiedTime().getMicroSecPart());
-//        assertEquals(newLastModifiedLink, info2l.getLastAccessTime().getSecs());
-//        assertEquals(lastMicros, info2l.getLastAccessTime().getMicroSecPart());
-//
-//        final Stat info2f = Posix.getFileInfo(s.getAbsolutePath());
-//        final Stat info2f2 = Posix.getLinkInfo(f.getAbsolutePath());
-//        assertNotEquals(info2l.getLastModifiedTime(), info2f2.getLastModifiedTime());
-//        assertEquals(info2f2.getLastModifiedTime(), info2f.getLastModifiedTime());
-//        assertEquals(newLastModifiedFile, info2f.getLastModifiedTime().getSecs());
-//        assertEquals(lastMicros, info2f.getLastModifiedTime().getMicroSecPart());
-//        assertEquals(newLastModifiedFile, info2f.getLastAccessTime().getSecs());
-//        assertEquals(lastMicros, info2f.getLastAccessTime().getMicroSecPart());
-//
-//        Thread.sleep(10);
-//
-//        final Posix.Time now1 = Posix.getSystemTime();
-//        assertNotEquals(0, now1.getNanoSecPart() % 1_000);
-//        Posix.setLinkTimestamps(s.getAbsolutePath());
-//        final Posix.Time now2 = Posix.getSystemTime();
-//        final Stat info3 = Posix.getLinkInfo(s.getAbsolutePath());
-//
-//        assertTrue(now1.getSecs() <= info3.getLastModified() && info3.getLastModified() <= now2.getSecs());
-//        assertTrue(now1.getMicroSecPart() <= info3.getLastModifiedTime().getMicroSecPart() && info.getLastModifiedTime().getMilliSecPart() <= now2.getMicroSecPart());
-//        assertTrue(now1.getSecs() <= info3.getLastAccess() && info3.getLastAccess() <= now2.getSecs());
-//        assertTrue(now1.getMicroSecPart() <= info3.getLastAccessTime().getMicroSecPart() && info.getLastAccessTime().getMilliSecPart() <= now2.getMicroSecPart());
-//        assertNotEquals(lastMicros, info3.getLastModifiedTime().getMicroSecPart());
-//        assertNotEquals(lastMicros, info3.getLastAccessTime().getMicroSecPart());
-//
-//    }
-//
-//    @Test
-//    public void testTouchSymLinkAndFile() throws IOException, InterruptedException
-//    {
-////        Posix.setUseUnixRealtimeTimer(false);
-//        final File someFile = new File(workingDirectory, "someOtherFile");
-//        final String content = "someMoreText\n";
-//        FileUtils.writeStringToFile(someFile, content, Charset.defaultCharset());
-//        final File someSymlink = new File(workingDirectory, "someLink");
-//        Posix.createSymbolicLink(someFile.getAbsolutePath(), someSymlink.getAbsolutePath());
-//        final Stat infoSymlink = Posix.getLinkInfo(someSymlink.getAbsolutePath());
-//        assertEquals(1, infoSymlink.getNumberOfHardLinks());
-//        assertEquals(FileLinkType.SYMLINK, infoSymlink.getLinkType());
-//        assertTrue(infoSymlink.isSymbolicLink());
-//        assertEquals(someFile.getAbsolutePath(), infoSymlink.tryGetSymbolicLink());
-//        assertEquals(someFile.getAbsolutePath(), Posix.tryReadSymbolicLink(someSymlink.getAbsolutePath()));
-//        assertNull(Posix.getLinkInfo(someSymlink.getAbsolutePath(), false).tryGetSymbolicLink());
-//        final long newLastModifiedLinkMicros = infoSymlink.getLastModifiedTime().getMicroSecPart();
-//        final long newLastModifiedLinkSec = infoSymlink.getLastModifiedTime().getSecs() - 24 * 3600;
-//        Posix.setLinkTimestamps(someSymlink.getAbsolutePath(), newLastModifiedLinkSec, newLastModifiedLinkMicros, newLastModifiedLinkSec, newLastModifiedLinkMicros);
-//
-//        final long newLastModifiedSecFile = infoSymlink.getLastModifiedTime().getSecs() - 2 * 24 * 3600;
-//        Posix.setFileTimestamps(someFile.getAbsolutePath(), newLastModifiedSecFile, newLastModifiedLinkMicros, newLastModifiedSecFile, newLastModifiedLinkMicros);
-//
-//        final Stat infoSymlink2 = Posix.getLinkInfo(someSymlink.getAbsolutePath(), false);
-//        assertEquals(newLastModifiedLinkSec, infoSymlink2.getLastModifiedTime().getSecs());
-//        assertEquals(newLastModifiedLinkMicros, infoSymlink2.getLastModifiedTime().getMicroSecPart());
-//        assertEquals(newLastModifiedLinkSec, infoSymlink2.getLastAccessTime().getSecs());
-//        assertEquals(newLastModifiedLinkMicros, infoSymlink2.getLastAccessTime().getMicroSecPart());
-//
-//        final Stat info2f = Posix.getFileInfo(someSymlink.getAbsolutePath());
-//        final Stat info2f2 = Posix.getLinkInfo(someFile.getAbsolutePath());
-//        assertNotEquals(infoSymlink2.getLastModifiedTime(), info2f2.getLastModifiedTime());
-//        assertEquals(info2f2.getLastModifiedTime(), info2f.getLastModifiedTime());
-//        assertEquals(newLastModifiedSecFile, info2f.getLastModifiedTime().getSecs());
-//        assertEquals(newLastModifiedLinkMicros, info2f.getLastModifiedTime().getMicroSecPart());
-//        assertEquals(newLastModifiedSecFile, info2f.getLastAccessTime().getSecs());
-//        assertEquals(newLastModifiedLinkMicros, info2f.getLastAccessTime().getMicroSecPart());
-//
-//
-//        Thread.sleep(10);
-//
-//        final Posix.Time now1 = Posix.getSystemTime();
-//        assertEquals(0, now1.getNanoSecPart() % 1_000);
-//        Posix.setLinkTimestamps(someSymlink.getAbsolutePath()); // Modifies the link, not the linked file
-//        final Posix.Time now2 = Posix.getSystemTime();
-//        final Stat info3 = Posix.getLinkInfo(someSymlink.getAbsolutePath()); // Returns the linked file info
-//
-//        assertTrue(now1.getSecs() <= info3.getLastModified() && info3.getLastModified() <= now2.getSecs());
-//        assertTrue(now1.getMicroSecPart() <= info3.getLastModifiedTime().getMicroSecPart() && infoSymlink.getLastModifiedTime().getMilliSecPart() <= now2.getMicroSecPart());
-//        assertTrue(now1.getSecs() <= info3.getLastAccess() && info3.getLastAccess() <= now2.getSecs());
-//        assertTrue(now1.getMicroSecPart() <= info3.getLastAccessTime().getMicroSecPart() && infoSymlink.getLastAccessTime().getMilliSecPart() <= now2.getMicroSecPart());
-//        assertNotEquals(newLastModifiedLinkMicros, info3.getLastModifiedTime().getMicroSecPart());
-//        assertNotEquals(newLastModifiedLinkMicros, info3.getLastAccessTime().getMicroSecPart());
-//    }
-
     @Test
     public void testGetLinkInfoSymLinkDanglingLink() throws IOException
     {
-- 
GitLab