This is an automated email from the ASF dual-hosted git repository.

bodewig pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ant.git

commit e60943e10cb4e9c08b9fae8dc0d23b594d6f80ae
Author: Stefan Bodewig <[email protected]>
AuthorDate: Sun Feb 8 11:38:08 2026 +0100

    allow permissions to be set on links themselves rather their targets
---
 WHATSNEW                                           |  5 ++
 manual/Tasks/setpermissions.html                   |  7 ++
 .../apache/tools/ant/taskdefs/SetPermissions.java  | 22 +++++-
 .../org/apache/tools/ant/util/PermissionUtils.java | 82 ++++++++++++++++++++--
 4 files changed, 109 insertions(+), 7 deletions(-)

diff --git a/WHATSNEW b/WHATSNEW
index dfd580f62..dfc5fa1cb 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -82,6 +82,11 @@ Other changes:
  * added <windowsjunction> file selector which only select directories
    that are Windows junctions.
 
+ * added a new actOnLinkTargets attribute to <setpermissions> to
+   control whether the permissions apply to a symbolic link or Windows
+   junction or the target of the respective links. The old behavior
+   of changing the link's target remains as default.
+
 Changes from Ant 1.10.14 TO Ant 1.10.15
 =======================================
 
diff --git a/manual/Tasks/setpermissions.html b/manual/Tasks/setpermissions.html
index f6ac15ef2..0805c372e 100644
--- a/manual/Tasks/setpermissions.html
+++ b/manual/Tasks/setpermissions.html
@@ -73,6 +73,13 @@ <h3>Parameters</h3>
     <td>Whether to stop the build if setting permissions fails.</td>
     <td>No; defaults to <q>true</q></td>
   </tr>
+  <tr>
+    <td>actOnLinkTargets</td>
+    <td>Whether to set the permissions of the targets of symbolic
+    links or Windows junctions instead of the links
+    themselves. <em>since Ant 1.10.16</em>.</td>
+    <td>No; defaults to <q>true</q></td>
+  </tr>
 </table>
 <h3>Parameters specified as nested elements</h3>
 
diff --git a/src/main/org/apache/tools/ant/taskdefs/SetPermissions.java 
b/src/main/org/apache/tools/ant/taskdefs/SetPermissions.java
index 3e708f442..269c94c1e 100644
--- a/src/main/org/apache/tools/ant/taskdefs/SetPermissions.java
+++ b/src/main/org/apache/tools/ant/taskdefs/SetPermissions.java
@@ -20,6 +20,7 @@ package org.apache.tools.ant.taskdefs;
 
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.LinkOption;
 import java.nio.file.Path;
 import java.nio.file.attribute.DosFileAttributeView;
 import java.nio.file.attribute.PosixFilePermission;
@@ -56,6 +57,7 @@ public class SetPermissions extends Task {
         EnumSet.noneOf(PosixFilePermission.class);
     private Resources resources = null;
     private boolean failonerror = true;
+    private boolean actOnLinkTargets = true;
     private NonPosixMode nonPosixMode = NonPosixMode.fail;
 
     /**
@@ -126,6 +128,19 @@ public class SetPermissions extends Task {
         this.nonPosixMode = m;
     }
 
+    /**
+     * Sets whether permissions should be applied to symbolic links or
+     * Windows junctions themselves or the link's targets.
+     *
+     * <p>The default value is {@code true}.</p>
+     *
+     * @param actOnLinkTargets true if permissions of link targets should be 
set.
+     * @since Ant 1.10.16
+     */
+    public void setActOnLinkTarget(boolean actOnLinkTargets) {
+        this.actOnLinkTargets = actOnLinkTargets;
+    }
+
     /**
      * Adds a collection of resources to set permissions on.
      * @param rc a resource collection
@@ -147,7 +162,8 @@ public class SetPermissions extends Task {
             for (Resource r : resources) {
                 currentResource = r;
                 try {
-                    PermissionUtils.setPermissions(r, permissions, 
this::posixPermissionsNotSupported);
+                    PermissionUtils.setPermissions(r, permissions, 
actOnLinkTargets,
+                                                   
this::posixPermissionsNotSupported);
                 } catch (IOException ioe) {
                     maybeThrowException(ioe, "Failed to set permissions on 
'%s' due to %s", r, ioe.getMessage());
                 }
@@ -199,7 +215,9 @@ public class SetPermissions extends Task {
     private void tryDos(Path p, boolean failIfDosIsNotSupported) {
         log("Falling back to DosFileAttributeView", Project.MSG_DEBUG);
         boolean readOnly = !isWritable();
-        DosFileAttributeView view = Files.getFileAttributeView(p, 
DosFileAttributeView.class);
+        DosFileAttributeView view = actOnLinkTargets
+            ? Files.getFileAttributeView(p, DosFileAttributeView.class)
+            : Files.getFileAttributeView(p, DosFileAttributeView.class, 
LinkOption.NOFOLLOW_LINKS);
         if (view != null) {
             try {
                 view.setReadOnly(readOnly);
diff --git a/src/main/org/apache/tools/ant/util/PermissionUtils.java 
b/src/main/org/apache/tools/ant/util/PermissionUtils.java
index b32e410e7..fba630cc2 100644
--- a/src/main/org/apache/tools/ant/util/PermissionUtils.java
+++ b/src/main/org/apache/tools/ant/util/PermissionUtils.java
@@ -103,6 +103,10 @@ public class PermissionUtils {
      *  <li>{@link ArchiveResource}</li>
      * </ul>
      *
+     * <p>When applied to a {@code FileProvider} providing a symbolic
+     * link or Windows junction, the permission of the link target are
+     * set, not the permssions of the link itself.</p>
+     *
      * @param r the resource to set permissions for
      * @param permissions the permissions
      * @param posixNotSupportedCallback optional callback that is
@@ -114,11 +118,42 @@ public class PermissionUtils {
     public static void setPermissions(Resource r, Set<PosixFilePermission> 
permissions,
                                       Consumer<Path> posixNotSupportedCallback)
         throws IOException {
+        setPermissions(r, permissions, true, posixNotSupportedCallback);
+    }
+
+    /**
+     * Sets permissions on a {@link Resource} - doesn't do anything
+     * for unsupported resource types.
+     *
+     * <p>Supported types are:</p>
+     * <ul>
+     *  <li>any {@link FileProvider}</li>
+     *  <li>{@link ArchiveResource}</li>
+     * </ul>
+     *
+     * @param r the resource to set permissions for
+     * @param permissions the permissions
+     * @param actOnLinkTarget whether to set the permissions on a
+     * symbolic link or Windows junction itself instead of the link's
+     * target. Only applies to resources that are {@code
+     * FileProvider}s.
+     * @param posixNotSupportedCallback optional callback that is
+     * invoked for a file provider resource if the file-system holding
+     * the file doesn't support PosixFilePermissions. The Path
+     * corresponding to the file is passed to the callback.
+     * @throws IOException if something goes wrong
+     * @since Ant 1.10.16
+     */
+    public static void setPermissions(Resource r, Set<PosixFilePermission> 
permissions,
+                                      boolean actOnLinkTarget,
+                                      Consumer<Path> posixNotSupportedCallback)
+        throws IOException {
         FileProvider f = r.as(FileProvider.class);
         if (f != null) {
             Path p = f.getFile().toPath();
-            PosixFileAttributeView view =
-                Files.getFileAttributeView(p, PosixFileAttributeView.class);
+            PosixFileAttributeView view = actOnLinkTarget
+                ? Files.getFileAttributeView(p, PosixFileAttributeView.class)
+                : Files.getFileAttributeView(p, PosixFileAttributeView.class, 
LinkOption.NOFOLLOW_LINKS);
             if (view != null) {
                 view.setPermissions(permissions);
             } else if (posixNotSupportedCallback != null) {
@@ -131,7 +166,37 @@ public class PermissionUtils {
     }
 
     /**
-     * Sets permissions of a {@link Resource} - returns an empty set
+     * Reads permissions of a {@link Resource} - returns an empty set
+     * for unsupported resource types or file systems that don't
+     * support PosixFilePermissions and no fallback has been
+     * provided..
+     *
+     * <p>Supported types are:</p>
+     * <ul>
+     *  <li>any {@link FileProvider}</li>
+     *  <li>{@link ArchiveResource}</li>
+     * </ul>
+     *
+     * <p>When applied to a {@code FileProvider} providing a symbolic
+     * link or Windows junction, the permission of the link target are
+     * read, not the permssions of the link itself.</p>
+     *
+     * @param r the resource to read permissions from
+     * @param posixNotSupportedFallback optional fallback function to provide
+     * permissions for file system that don't support
+     * PosixFilePermissions. The Path corresponding to the file is
+     * passed to the callback.
+     * @return the permissions
+     * @throws IOException if something goes wrong
+     */
+    public static Set<PosixFilePermission> getPermissions(Resource r,
+            Function<Path, Set<PosixFilePermission>> posixNotSupportedFallback)
+        throws IOException {
+        return getPermissions(r, true, posixNotSupportedFallback);
+    }
+
+    /**
+     * Reads permissions of a {@link Resource} - returns an empty set
      * for unsupported resource types or file systems that don't
      * support PosixFilePermissions and no fallback has been
      * provided..
@@ -143,21 +208,28 @@ public class PermissionUtils {
      * </ul>
      *
      * @param r the resource to read permissions from
+     * @param readFromLinkTarget whether to read the permissions of a
+     * symbolic link or Windows junction itself instead of the link's
+     * target. Only applies to resources that are {@code
+     * FileProvider}s.
      * @param posixNotSupportedFallback optional fallback function to provide
      * permissions for file system that don't support
      * PosixFilePermissions. The Path corresponding to the file is
      * passed to the callback.
      * @return the permissions
      * @throws IOException if something goes wrong
+     * @sine Ant 1.10.16
      */
     public static Set<PosixFilePermission> getPermissions(Resource r,
+            boolean readFromLinkTarget,
             Function<Path, Set<PosixFilePermission>> posixNotSupportedFallback)
         throws IOException {
         FileProvider f = r.as(FileProvider.class);
         if (f != null) {
             Path p = f.getFile().toPath();
-            PosixFileAttributeView view =
-                Files.getFileAttributeView(p, PosixFileAttributeView.class);
+            PosixFileAttributeView view = readFromLinkTarget
+                ? Files.getFileAttributeView(p, PosixFileAttributeView.class)
+                : Files.getFileAttributeView(p, PosixFileAttributeView.class, 
LinkOption.NOFOLLOW_LINKS);
             if (view != null) {
                 return view.readAttributes().permissions();
             } else if (posixNotSupportedFallback != null) {

Reply via email to