@Override
  public List<AclEntry> getAcl() throws IOException {
    // permission check
    checkAccess(file, true, false);

    // open file (will fail if file is a link and not following links)
    int fd = file.openForAttributeAccess(followLinks);
    try {
      long address = unsafe.allocateMemory(SIZEOF_ACE_T * MAX_ACL_ENTRIES);
      try {
        // read ACL and decode it
        int n = facl(fd, ACE_GETACL, MAX_ACL_ENTRIES, address);
        assert n >= 0;
        return decode(address, n);
      } catch (UnixException x) {
        if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
          throw new FileSystemException(
              file.getPathForExceptionMessage(),
              null,
              x.getMessage() + " (file system does not support NFSv4 ACLs)");
        }
        x.rethrowAsIOException(file);
        return null; // keep compiler happy
      } finally {
        unsafe.freeMemory(address);
      }
    } finally {
      close(fd);
    }
  }
  @Override
  public void setAcl(List<AclEntry> acl) throws IOException {
    // permission check
    checkAccess(file, false, true);

    // open file (will fail if file is a link and not following links)
    int fd = file.openForAttributeAccess(followLinks);
    try {
      // SECURITY: need to copy list as can change during processing
      acl = new ArrayList<AclEntry>(acl);
      int n = acl.size();

      long address = unsafe.allocateMemory(SIZEOF_ACE_T * n);
      try {
        encode(acl, address);
        facl(fd, ACE_SETACL, n, address);
      } catch (UnixException x) {
        if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
          throw new FileSystemException(
              file.getPathForExceptionMessage(),
              null,
              x.getMessage() + " (file system does not support NFSv4 ACLs)");
        }
        if (x.errno() == EINVAL && (n < 3))
          throw new IOException("ACL must contain at least 3 entries");
        x.rethrowAsIOException(file);
      } finally {
        unsafe.freeMemory(address);
      }
    } finally {
      close(fd);
    }
  }
    @Override
    public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime)
        throws IOException {
      // null => don't change
      if (lastModifiedTime == null && lastAccessTime == null) {
        // no effect
        return;
      }

      // permission check
      file.checkWrite();

      int fd = file.openForAttributeAccess(followLinks);
      try {
        // if not changing both attributes then need existing attributes
        if (lastModifiedTime == null || lastAccessTime == null) {
          try {
            UnixFileAttributes attrs = UnixFileAttributes.get(fd);
            if (lastModifiedTime == null) lastModifiedTime = attrs.lastModifiedTime();
            if (lastAccessTime == null) lastAccessTime = attrs.lastAccessTime();
          } catch (UnixException x) {
            x.rethrowAsIOException(file);
          }
        }

        // uptime times
        long modValue = lastModifiedTime.to(TimeUnit.MICROSECONDS);
        long accessValue = lastAccessTime.to(TimeUnit.MICROSECONDS);

        boolean retry = false;
        try {
          futimes(fd, accessValue, modValue);
        } catch (UnixException x) {
          // if futimes fails with EINVAL and one/both of the times is
          // negative then we adjust the value to the epoch and retry.
          if (x.errno() == UnixConstants.EINVAL && (modValue < 0L || accessValue < 0L)) {
            retry = true;
          } else {
            x.rethrowAsIOException(file);
          }
        }
        if (retry) {
          if (modValue < 0L) modValue = 0L;
          if (accessValue < 0L) accessValue = 0L;
          try {
            futimes(fd, accessValue, modValue);
          } catch (UnixException x) {
            x.rethrowAsIOException(file);
          }
        }
      } finally {
        close(fd);
      }
    }