@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 UnixFileAttributes readAttributes() throws IOException {
   checkReadExtended();
   try {
     return UnixFileAttributes.get(file, followLinks);
   } catch (UnixException x) {
     x.rethrowAsIOException(file);
     return null; // keep compiler happy
   }
 }
 @Override
 public BasicFileAttributes readAttributes() throws IOException {
   file.checkRead();
   try {
     UnixFileAttributes attrs = UnixFileAttributes.get(file, followLinks);
     return attrs.asBasicFileAttributes();
   } catch (UnixException x) {
     x.rethrowAsIOException(file);
     return null; // keep compiler happy
   }
 }
  @Override
  public UserPrincipal getOwner() throws IOException {
    checkAccess(file, true, false);

    try {
      UnixFileAttributes attrs = UnixFileAttributes.get(file, followLinks);
      return UnixUserPrincipals.fromUid(attrs.uid());
    } catch (UnixException x) {
      x.rethrowAsIOException(file);
      return null; // keep compile happy
    }
  }
 // chown
 final void setOwners(int uid, int gid) throws IOException {
   checkWriteExtended();
   try {
     if (followLinks) {
       chown(file, uid, gid);
     } else {
       lchown(file, uid, gid);
     }
   } catch (UnixException x) {
     x.rethrowAsIOException(file);
   }
 }
 // chmod
 final void setMode(int mode) throws IOException {
   checkWriteExtended();
   try {
     if (followLinks) {
       chmod(file, mode);
     } else {
       int fd = file.openForAttributeAccess(false);
       try {
         fchmod(fd, mode);
       } finally {
         close(fd);
       }
     }
   } catch (UnixException x) {
     x.rethrowAsIOException(file);
   }
 }
  @Override
  public void setOwner(UserPrincipal owner) throws IOException {
    checkAccess(file, true, false);

    if (!(owner instanceof UnixUserPrincipals.User)) throw new ProviderMismatchException();
    if (owner instanceof UnixUserPrincipals.Group)
      throw new IOException("'owner' parameter is a group");
    int uid = ((UnixUserPrincipals.User) owner).uid();

    try {
      if (followLinks) {
        lchown(file, uid, -1);
      } else {
        chown(file, uid, -1);
      }
    } catch (UnixException x) {
      x.rethrowAsIOException(file);
    }
  }
    @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);
      }
    }