diff --git a/src/etc/testcases/taskdefs/tar.xml b/src/etc/testcases/taskdefs/tar.xml index b0f99d5b7..08ac2f79a 100644 --- a/src/etc/testcases/taskdefs/tar.xml +++ b/src/etc/testcases/taskdefs/tar.xml @@ -203,4 +203,12 @@ + + + + + + + + diff --git a/src/etc/testcases/testtarwithsymlinks.tar.gz b/src/etc/testcases/testtarwithsymlinks.tar.gz new file mode 100644 index 000000000..782a23727 Binary files /dev/null and b/src/etc/testcases/testtarwithsymlinks.tar.gz differ diff --git a/src/main/org/apache/tools/ant/taskdefs/Tar.java b/src/main/org/apache/tools/ant/taskdefs/Tar.java index 76446fdc8..1ab537962 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Tar.java +++ b/src/main/org/apache/tools/ant/taskdefs/Tar.java @@ -405,12 +405,7 @@ public class Tar extends MatchingTask { return; } - String prefix = tarFileSet.getPrefix(this.getProject()); - // '/' is appended for compatibility with the zip task. - if (!prefix.isEmpty() && !prefix.endsWith("/")) { - prefix += "/"; - } - vPath = prefix + vPath; + vPath = getCanonicalPrefix(tarFileSet, this.getProject()) + vPath; } else { vPath = fullpath; } @@ -464,6 +459,14 @@ public class Tar extends MatchingTask { te.setUserId(tr.getLongUid()); te.setGroupName(tr.getGroup()); te.setGroupId(tr.getLongGid()); + String linkName = tr.getLinkName(); + byte linkFlag = tr.getLinkFlag(); + if (linkFlag == TarConstants.LF_LINK && + linkName != null && linkName.length() > 0 && !linkName.startsWith("/")) { + linkName = getCanonicalPrefix(tarFileSet, this.getProject()) + linkName; + } + te.setLinkName(linkName); + te.setLinkFlag(linkFlag); } } @@ -785,6 +788,15 @@ public class Tar extends MatchingTask { return tfs; } + private static String getCanonicalPrefix(TarFileSet tarFileSet, Project project) { + String prefix = tarFileSet.getPrefix(project); + // '/' is appended for compatibility with the zip task. + if (prefix.isEmpty() || prefix.endsWith("/")) { + return prefix; + } + return prefix += "/"; + } + /** * This is a FileSet with the option to specify permissions * and other attributes. diff --git a/src/main/org/apache/tools/ant/types/resources/TarResource.java b/src/main/org/apache/tools/ant/types/resources/TarResource.java index 9e137cc07..96db041dc 100644 --- a/src/main/org/apache/tools/ant/types/resources/TarResource.java +++ b/src/main/org/apache/tools/ant/types/resources/TarResource.java @@ -26,6 +26,7 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.tar.TarConstants; import org.apache.tools.tar.TarEntry; import org.apache.tools.tar.TarInputStream; @@ -39,6 +40,8 @@ public class TarResource extends ArchiveResource { private String groupName = ""; private long uid; private long gid; + private byte linkFlag = TarConstants.LF_NORMAL; + private String linkName = ""; /** * Default constructor. @@ -172,6 +175,22 @@ public class TarResource extends ArchiveResource { return (int) getLongGid(); } + /** + * @return the link "name" (=path) of this entry; an empty string if this is no link + * @since 1.10.10 + */ + public String getLinkName() { + return linkName; + } + + /** + * @return the link "flag" (=type) of this entry + * @since 1.10.10 + */ + public byte getLinkFlag() { + return linkFlag; + } + /** * fetches information from the named entry inside the archive. */ @@ -213,6 +232,8 @@ public class TarResource extends ArchiveResource { groupName = e.getGroupName(); uid = e.getLongUserId(); gid = e.getLongGroupId(); + linkName = e.getLinkName(); + linkFlag = e.getLinkFlag(); } } diff --git a/src/main/org/apache/tools/tar/TarEntry.java b/src/main/org/apache/tools/tar/TarEntry.java index d38a8ffd1..ab0212022 100644 --- a/src/main/org/apache/tools/tar/TarEntry.java +++ b/src/main/org/apache/tools/tar/TarEntry.java @@ -395,6 +395,24 @@ public class TarEntry implements TarConstants { this.mode = mode; } + /** + * Get this entry's link flag. + * + * @return This entry's link flag. + */ + public byte getLinkFlag() { + return linkFlag; + } + + /** + * Set this entry's link flag. + * + * @param link the link flag to use. + */ + public void setLinkFlag(byte linkFlag) { + this.linkFlag = linkFlag; + } + /** * Get this entry's link name. * diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/TarTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/TarTest.java index e37e2b95a..86a6d9beb 100644 --- a/src/tests/junit/org/apache/tools/ant/taskdefs/TarTest.java +++ b/src/tests/junit/org/apache/tools/ant/taskdefs/TarTest.java @@ -19,16 +19,21 @@ package org.apache.tools.ant.taskdefs; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildFileRule; import org.apache.tools.ant.FileUtilities; +import org.apache.tools.tar.TarEntry; +import org.apache.tools.tar.TarInputStream; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class TarTest { @@ -186,4 +191,27 @@ public class TarTest { public void testtestTarFilesetWithReference() { buildRule.executeTarget("testTarFilesetWithReference"); } + + @Test + public void testTarFilesetWithSymlinks() throws IOException { + buildRule.executeTarget("testTarFilesetWithSymlinks"); + final File f = new File(buildRule.getProject().getProperty("output"), "result.tar"); + final TarInputStream tis = new TarInputStream(new FileInputStream(f)); + try { + final TarEntry e1 = tis.getNextEntry(); + assertEquals("pre/dir/file", e1.getName()); + assertEquals("", e1.getLinkName()); + assertEquals(48, e1.getLinkFlag()); + + final TarEntry e2 = tis.getNextEntry(); + assertEquals("pre/sub/file", e2.getName()); + assertEquals("../dir/file", e2.getLinkName()); + assertEquals(50, e2.getLinkFlag()); + + assertNull(tis.getNextEntry()); + } + finally { + tis.close(); + } + } }