@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
    }
  }
  /** Decode the buffer, returning an ACL */
  private static List<AclEntry> decode(long address, int n) {
    ArrayList<AclEntry> acl = new ArrayList<>(n);
    for (int i = 0; i < n; i++) {
      long offset = address + i * SIZEOF_ACE_T;

      int uid = unsafe.getInt(offset + OFFSETOF_UID);
      int mask = unsafe.getInt(offset + OFFSETOF_MASK);
      int flags = (int) unsafe.getShort(offset + OFFSETOF_FLAGS);
      int type = (int) unsafe.getShort(offset + OFFSETOF_TYPE);

      // map uid and flags to UserPrincipal
      UnixUserPrincipals.User who = null;
      if ((flags & ACE_OWNER) > 0) {
        who = UnixUserPrincipals.SPECIAL_OWNER;
      } else if ((flags & ACE_GROUP) > 0) {
        who = UnixUserPrincipals.SPECIAL_GROUP;
      } else if ((flags & ACE_EVERYONE) > 0) {
        who = UnixUserPrincipals.SPECIAL_EVERYONE;
      } else if ((flags & ACE_IDENTIFIER_GROUP) > 0) {
        who = UnixUserPrincipals.fromGid(uid);
      } else {
        who = UnixUserPrincipals.fromUid(uid);
      }

      AclEntryType aceType = null;
      switch (type) {
        case ACE_ACCESS_ALLOWED_ACE_TYPE:
          aceType = AclEntryType.ALLOW;
          break;
        case ACE_ACCESS_DENIED_ACE_TYPE:
          aceType = AclEntryType.DENY;
          break;
        case ACE_SYSTEM_AUDIT_ACE_TYPE:
          aceType = AclEntryType.AUDIT;
          break;
        case ACE_SYSTEM_ALARM_ACE_TYPE:
          aceType = AclEntryType.ALARM;
          break;
        default:
          assert false;
      }

      Set<AclEntryPermission> aceMask = EnumSet.noneOf(AclEntryPermission.class);
      if ((mask & ACE_READ_DATA) > 0) aceMask.add(AclEntryPermission.READ_DATA);
      if ((mask & ACE_WRITE_DATA) > 0) aceMask.add(AclEntryPermission.WRITE_DATA);
      if ((mask & ACE_APPEND_DATA) > 0) aceMask.add(AclEntryPermission.APPEND_DATA);
      if ((mask & ACE_READ_NAMED_ATTRS) > 0) aceMask.add(AclEntryPermission.READ_NAMED_ATTRS);
      if ((mask & ACE_WRITE_NAMED_ATTRS) > 0) aceMask.add(AclEntryPermission.WRITE_NAMED_ATTRS);
      if ((mask & ACE_EXECUTE) > 0) aceMask.add(AclEntryPermission.EXECUTE);
      if ((mask & ACE_DELETE_CHILD) > 0) aceMask.add(AclEntryPermission.DELETE_CHILD);
      if ((mask & ACE_READ_ATTRIBUTES) > 0) aceMask.add(AclEntryPermission.READ_ATTRIBUTES);
      if ((mask & ACE_WRITE_ATTRIBUTES) > 0) aceMask.add(AclEntryPermission.WRITE_ATTRIBUTES);
      if ((mask & ACE_DELETE) > 0) aceMask.add(AclEntryPermission.DELETE);
      if ((mask & ACE_READ_ACL) > 0) aceMask.add(AclEntryPermission.READ_ACL);
      if ((mask & ACE_WRITE_ACL) > 0) aceMask.add(AclEntryPermission.WRITE_ACL);
      if ((mask & ACE_WRITE_OWNER) > 0) aceMask.add(AclEntryPermission.WRITE_OWNER);
      if ((mask & ACE_SYNCHRONIZE) > 0) aceMask.add(AclEntryPermission.SYNCHRONIZE);

      Set<AclEntryFlag> aceFlags = EnumSet.noneOf(AclEntryFlag.class);
      if ((flags & ACE_FILE_INHERIT_ACE) > 0) aceFlags.add(AclEntryFlag.FILE_INHERIT);
      if ((flags & ACE_DIRECTORY_INHERIT_ACE) > 0) aceFlags.add(AclEntryFlag.DIRECTORY_INHERIT);
      if ((flags & ACE_NO_PROPAGATE_INHERIT_ACE) > 0)
        aceFlags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
      if ((flags & ACE_INHERIT_ONLY_ACE) > 0) aceFlags.add(AclEntryFlag.INHERIT_ONLY);

      // build the ACL entry and add it to the list
      AclEntry ace =
          AclEntry.newBuilder()
              .setType(aceType)
              .setPrincipal(who)
              .setPermissions(aceMask)
              .setFlags(aceFlags)
              .build();
      acl.add(ace);
    }

    return acl;
  }