Browse Source

Add support for Unix permissions to <zip> and friends, doing it the

Info-Zip way.

PR: 6492


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@273629 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 22 years ago
parent
commit
3255fa8ff4
10 changed files with 250 additions and 56 deletions
  1. +3
    -0
      WHATSNEW
  2. +26
    -6
      build.xml
  3. +67
    -9
      docs/manual/CoreTasks/zip.html
  4. +4
    -3
      src/main/org/apache/tools/ant/taskdefs/Ear.java
  5. +10
    -7
      src/main/org/apache/tools/ant/taskdefs/Jar.java
  6. +4
    -3
      src/main/org/apache/tools/ant/taskdefs/War.java
  7. +102
    -25
      src/main/org/apache/tools/ant/taskdefs/Zip.java
  8. +18
    -2
      src/main/org/apache/tools/ant/types/ZipFileSet.java
  9. +2
    -0
      src/main/org/apache/tools/zip/ZipEntry.java
  10. +14
    -1
      src/testcases/org/apache/tools/zip/ZipEntryTest.java

+ 3
- 0
WHATSNEW View File

@@ -132,6 +132,9 @@ Other changes:
* <tarfileset> has a new dirmode attribute to specify the permissions
for directories.

* <zip> can now store Unix permissions in a way that can be
reconstructed by Info-Zip's unzip command.

Changes from Ant 1.5.1Beta1 to 1.5.1
====================================



+ 26
- 6
build.xml View File

@@ -1183,9 +1183,21 @@
<antcall inheritAll="false" target="internal_dist">
<param name="dist.dir" value="${dist.name}"/>
</antcall>
<zip destfile="${dist.base}/bin/${dist.name}-bin.zip"
basedir="${dist.name}/.."
includes="${dist.name}/**"/>
<zip destfile="${dist.base}/bin/${dist.name}-bin.zip">
<zipfileset dir="${dist.name}/.." filemode="755">
<include name="${dist.name}/bin/ant"/>
<include name="${dist.name}/bin/antRun"/>
<include name="${dist.name}/bin/*.pl"/>
<include name="${dist.name}/bin/*.py"/>
</zipfileset>
<fileset dir="${dist.name}/..">
<include name="${dist.name}/**"/>
<exclude name="${dist.name}/bin/ant"/>
<exclude name="${dist.name}/bin/antRun"/>
<exclude name="${dist.name}/bin/*.pl"/>
<exclude name="${dist.name}/bin/*.py"/>
</fileset>
</zip>
<tar longfile="gnu"
destfile="${dist.base}/bin/${dist.name}-bin.tar">
<tarfileset dir="${dist.name}/.." mode="755" username="ant" group="ant">
@@ -1219,9 +1231,17 @@
<antcall inheritAll="false" target="src-dist">
<param name="src.dist.dir" value="${dist.name}"/>
</antcall>
<zip destfile="${dist.base}/src/${dist.name}-src.zip"
basedir="${dist.name}/.."
includes="${dist.name}/**"/>
<zip destfile="${dist.base}/src/${dist.name}-src.zip">
<zipfileset dir="${dist.name}/.." filemode="755">
<include name="${dist.name}/bootstrap.sh"/>
<include name="${dist.name}/build.sh"/>
</zipfileset>
<fileset dir="${dist.name}/..">
<include name="${dist.name}/**"/>
<exclude name="${dist.name}/bootstrap.sh"/>
<exclude name="${dist.name}/build.sh"/>
</fileset>
</zip>
<tar longfile="gnu"
destfile="${dist.base}/src/${dist.name}-src.tar" >
<tarfileset dir="${dist.name}/.." mode="755" username="ant" group="ant">


+ 67
- 9
docs/manual/CoreTasks/zip.html View File

@@ -52,6 +52,15 @@ but causes problems if you try to open them from within Java and your
filenames contain non US-ASCII characters. Use the encoding attribute
and set it to UTF8 to create zip files that can safely be read by
Java.</p>

<p>Starting with Ant 1.6, &lt;zip&gt; can store Unix permissions
inside the archive (see description of the filemode and dirmode
attributes for <a href="#zipfileset">&lt;zipfileset&gt;</a>).
Unfortunately there is no portable way to store these permissions.
Ant uses the algorithm used by <a href="http://www.info-zip.org">Info-Zip's</a>
implementation of the zip and unzip commands - these are the default
versions of zip and unzip for many Unix and Unix-like systems.</p>

<h3>Parameters</h3>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
@@ -144,15 +153,64 @@ Java.</p>
<p>The zip task supports any number of nested <a
href="../CoreTypes/fileset.html"><code>&lt;fileset&gt;</code></a> elements to specify
the files to be included in the archive.</p>
<h4>zipfileset</h4>
<p>A <code>&lt;zipfileset&gt;</code> has three additional attributes: <i>prefix</i>, <i>fullpath</i>, and <i>src</i>. The
<i>prefix</i> and <i>fullpath</i> attributes modify the location of the files when they are placed
inside the archive. If the <i>prefix</i> attribute is set, all files in the fileset are prefixed
with that path in the archive. If the <i>fullpath</i> attribute is set, the file described by the fileset is placed at that
exact location in the archive. (The <i>fullpath</i> attribute can only be set for filesets that represent a single file. The <i>prefix</i> and <i>fullpath</i> attributes cannot both be set on the same fileset.) The <i>src</i> attribute
may be used in place of the <i>dir</i> attribute to specify a zip file whose
contents will be extracted and included in the archive. As with directories, include and exclude patterns may be used to specify a subset of the zip file
for inclusion in the archive.</p>

<h4><a name="zipfileset">zipfileset</a></h4>

<p>A <code>&lt;zipfileset&gt;</code> is a special form of a
<code>&lt;fileset&gt;</code> that adds some extra functionality. It
supports all attributes of <code>&lt;fileset&gt;</code> in addition to
those listed below.</p>

<h3>Parameters</h3>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td valign="top"><b>Attribute</b></td>
<td valign="top"><b>Description</b></td>
<td valign="top" align="center"><b>Required</b></td>
</tr>
<tr>
<td valign="top">prefix</td>
<td valign="top">all files in the fileset are prefixed with that
path in the archive.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">fullpath</td>
<td valign="top">the file described by the fileset is placed at
that exact location in the archive.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">src</td>
<td valign="top">may be used in place of the <i>dir</i> attribute
to specify a zip file whose contents will be extracted and
included in the archive.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">filemode</td>
<td valign="top">A 3 digit octal string, specify the user, group
and other modes in the standard Unix fashion. Only applies to
plain files. Default is 644. <em>since Ant 1.6</em>.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">dirmode</td>
<td valign="top">A 3 digit octal string, specify the user, group
and other modes in the standard Unix fashion. Only applies to
directories. Default is 755. <em>since Ant 1.6</em>.</td>
<td align="center" valign="top">No</td>
</tr>
</table>

<p>The <i>fullpath</i> attribute can only be set for filesets that
represent a single file. The <i>prefix</i> and <i>fullpath</i>
attributes cannot both be set on the same fileset.</p>

<p>When using the <i>src</i> attribute, include and exclude patterns
may be used to specify a subset of the zip file for inclusion in the
archive as with the <i>dir</i> attribute.</p>

<h4>zipgroupfileset</h4>
<p>A <code>&lt;zipgroupfileset&gt;</code> allows for multiple zip files to be
merged into the archive. Each file found in this fileset is added to the archive


+ 4
- 3
src/main/org/apache/tools/ant/taskdefs/Ear.java View File

@@ -134,7 +134,8 @@ public class Ear extends Jar {
super.initZipOutputStream(zOut);
}

protected void zipFile(File file, ZipOutputStream zOut, String vPath)
protected void zipFile(File file, ZipOutputStream zOut, String vPath,
int mode)
throws IOException {
// If the file being added is META-INF/application.xml, we
// warn if it's not the one specified in the "appxml"
@@ -150,11 +151,11 @@ public class Ear extends Jar {
+ " be ignored (please use appxml attribute to "
+ archiveType + " task)", Project.MSG_WARN);
} else {
super.zipFile(file, zOut, vPath);
super.zipFile(file, zOut, vPath, mode);
descriptorAdded = true;
}
} else {
super.zipFile(file, zOut, vPath);
super.zipFile(file, zOut, vPath, mode);
}
}



+ 10
- 7
src/main/org/apache/tools/ant/taskdefs/Jar.java View File

@@ -314,7 +314,7 @@ public class Jar extends Zip {
Project.MSG_WARN);
}

zipDir(null, zOut, "META-INF/");
zipDir(null, zOut, "META-INF/", ZipFileSet.DEFAULT_DIR_MODE);
// time to write the manifest
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(baos);
@@ -324,7 +324,8 @@ public class Jar extends Zip {
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
super.zipFile(bais, zOut, "META-INF/MANIFEST.MF",
System.currentTimeMillis(), null);
System.currentTimeMillis(), null,
ZipFileSet.DEFAULT_FILE_MODE);
super.initZipOutputStream(zOut);
}

@@ -387,20 +388,22 @@ public class Jar extends Zip {
writer.flush();
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis(), null);
super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis(), null,
ZipFileSet.DEFAULT_FILE_MODE);
}

/**
* Overriden from Zip class to deal with manifests
*/
protected void zipFile(File file, ZipOutputStream zOut, String vPath)
protected void zipFile(File file, ZipOutputStream zOut, String vPath,
int mode)
throws IOException {
if ("META-INF/MANIFEST.MF".equalsIgnoreCase(vPath)) {
if (! doubleFilePass || (doubleFilePass && skipWriting)) {
filesetManifest(file, null);
}
} else {
super.zipFile(file, zOut, vPath);
super.zipFile(file, zOut, vPath, mode);
}
}

@@ -408,14 +411,14 @@ public class Jar extends Zip {
* Overriden from Zip class to deal with manifests
*/
protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath,
long lastModified, File file)
long lastModified, File file, int mode)
throws IOException {
if ("META-INF/MANIFEST.MF".equalsIgnoreCase(vPath)) {
if (! doubleFilePass || (doubleFilePass && skipWriting)) {
filesetManifest(file, is);
}
} else {
super.zipFile(is, zOut, vPath, lastModified, null);
super.zipFile(is, zOut, vPath, lastModified, null, mode);
}
}



+ 4
- 3
src/main/org/apache/tools/ant/taskdefs/War.java View File

@@ -172,7 +172,8 @@ public class War extends Jar {
/**
* add another file to the stream
*/
protected void zipFile(File file, ZipOutputStream zOut, String vPath)
protected void zipFile(File file, ZipOutputStream zOut, String vPath,
int mode)
throws IOException {
// If the file being added is WEB-INF/web.xml, we warn if it's
// not the one specified in the "webxml" attribute - or if
@@ -187,11 +188,11 @@ public class War extends Jar {
+ "(please use webxml attribute to "
+ archiveType + " task)", Project.MSG_WARN);
} else {
super.zipFile(file, zOut, vPath);
super.zipFile(file, zOut, vPath, mode);
descriptorAdded = true;
}
} else {
super.zipFile(file, zOut, vPath);
super.zipFile(file, zOut, vPath, mode);
}
}



+ 102
- 25
src/main/org/apache/tools/ant/taskdefs/Zip.java View File

@@ -388,7 +388,9 @@ public class Zip extends MatchingTask {

// Add the implicit fileset to the archive.
if (baseDir != null) {
addFiles(getDirectoryScanner(baseDir), zOut, "", "");
addFiles(getDirectoryScanner(baseDir), zOut, "", "",
ZipFileSet.DEFAULT_DIR_MODE,
ZipFileSet.DEFAULT_FILE_MODE);
}
// Add the explicit filesets to the archive.
addFiles(filesets, zOut);
@@ -473,10 +475,28 @@ public class Zip extends MatchingTask {
* prependig the given prefix to each filename.
*
* <p>Ensure parent directories have been added as well.
*
* @deprecated use six-arg version instead.
*/
protected void addFiles(FileScanner scanner, ZipOutputStream zOut,
String prefix, String fullpath)
throws IOException {
addFiles(scanner, zOut, prefix, fullpath, ZipFileSet.DEFAULT_DIR_MODE,
ZipFileSet.DEFAULT_FILE_MODE);
}

/**
* Add all files of the given FileScanner to the ZipOutputStream
* prependig the given prefix to each filename.
*
* <p>Ensure parent directories have been added as well.
*
* @since Ant 1.6
*/
protected void addFiles(FileScanner scanner, ZipOutputStream zOut,
String prefix, String fullpath, int dirMode,
int fileMode)
throws IOException {

if (prefix.length() > 0 && fullpath.length() > 0) {
throw new BuildException("Both prefix and fullpath attributes must"
@@ -500,7 +520,7 @@ public class Zip extends MatchingTask {
if (!name.endsWith("/")) {
name += "/";
}
addParentDirs(thisBaseDir, name, zOut, prefix);
addParentDirs(thisBaseDir, name, zOut, prefix, dirMode);
}

// files that matched include patterns
@@ -514,13 +534,13 @@ public class Zip extends MatchingTask {
File f = new File(thisBaseDir, files[i]);
if (fullpath.length() > 0) {
// Add this file at the specified location.
addParentDirs(null, fullpath, zOut, "");
zipFile(f, zOut, fullpath);
addParentDirs(null, fullpath, zOut, "", dirMode);
zipFile(f, zOut, fullpath, fileMode);
} else {
// Add this file with the specified prefix.
String name = files[i].replace(File.separatorChar, '/');
addParentDirs(thisBaseDir, name, zOut, prefix);
zipFile(f, zOut, prefix + name);
addParentDirs(thisBaseDir, name, zOut, prefix, dirMode);
zipFile(f, zOut, prefix + name, fileMode);
}
}
}
@@ -550,13 +570,16 @@ public class Zip extends MatchingTask {
String vPath = entry.getName();
if (zipScanner.match(vPath)) {
if (fullpath.length() > 0) {
addParentDirs(null, fullpath, zOut, "");
zipFile(in, zOut, fullpath, entry.getTime(), zipSrc);
addParentDirs(null, fullpath, zOut, "",
fs.getDirMode());
zipFile(in, zOut, fullpath, entry.getTime(), zipSrc,
fs.getFileMode());
} else {
addParentDirs(null, vPath, zOut, prefix);
addParentDirs(null, vPath, zOut, prefix,
fs.getDirMode());
if (!entry.isDirectory()) {
zipFile(in, zOut, prefix + vPath, entry.getTime(),
zipSrc);
zipSrc, fs.getFileMode());
}
}
}
@@ -701,8 +724,20 @@ public class Zip extends MatchingTask {
return result;
}

/**
* @deprecated use four-arg version instead.
*/
protected void zipDir(File dir, ZipOutputStream zOut, String vPath)
throws IOException {
zipDir(dir, zOut, vPath, ZipFileSet.DEFAULT_DIR_MODE);
}

/**
* @since Ant 1.6
*/
protected void zipDir(File dir, ZipOutputStream zOut, String vPath,
int mode)
throws IOException {
if (addedDirs.get(vPath) != null) {
// don't add directories we've already added.
// no warning if we try, it is harmless in and of itself
@@ -723,17 +758,28 @@ public class Zip extends MatchingTask {
ze.setMethod (ZipEntry.STORED);
// This is faintly ridiculous:
ze.setCrc (EMPTY_CRC);

// this is 040775 | MS-DOS directory flag in reverse byte order
ze.setExternalAttributes(0x41FD0010L);
ze.setUnixMode(mode);

zOut.putNextEntry (ze);
}
}

/**
* @deprecated use six-arg version instead.
*/
protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath,
long lastModified, File file)
throws IOException {
zipFile(in, zOut, vPath, lastModified, file,
ZipFileSet.DEFAULT_FILE_MODE);
}

/**
* @since Ant 1.6
*/
protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath,
long lastModified, File file, int mode)
throws IOException {
if (entries.contains(vPath)) {

if (duplicate.equals("preserve")) {
@@ -759,14 +805,15 @@ public class Zip extends MatchingTask {
ze.setTime(lastModified);

/*
* XXX ZipOutputStream.putEntry expects the ZipEntry to know its
* size and the CRC sum before you start writing the data when using
* STORED mode.
* ZipOutputStream.putNextEntry expects the ZipEntry to
* know its size and the CRC sum before you start writing
* the data when using STORED mode.
*
* This forces us to process the data twice.
*
* I couldn't find any documentation on this, just found out by try
* and error.
* In DEFLATED mode, it will take advantage of a Zip
* Version 2 feature where size can be stored after the
* data (as the data itself signals end of data).
*/
if (!doCompress) {
long size = 0;
@@ -800,6 +847,7 @@ public class Zip extends MatchingTask {
ze.setCrc(cal.getValue());
}

ze.setUnixMode(mode);
zOut.putNextEntry(ze);

byte[] buffer = new byte[8 * 1024];
@@ -814,8 +862,20 @@ public class Zip extends MatchingTask {
addedFiles.addElement(vPath);
}

/**
* @deprecated use six-arg version instead.
*/
protected void zipFile(File file, ZipOutputStream zOut, String vPath)
throws IOException {
zipFile(file, zOut, vPath, ZipFileSet.DEFAULT_FILE_MODE);
}

/**
* @since Ant 1.6
*/
protected void zipFile(File file, ZipOutputStream zOut, String vPath,
int mode)
throws IOException {
if (file.equals(zipFile)) {
throw new BuildException("A zip file cannot include itself",
getLocation());
@@ -823,18 +883,31 @@ public class Zip extends MatchingTask {

FileInputStream fIn = new FileInputStream(file);
try {
zipFile(fIn, zOut, vPath, file.lastModified(), null);
zipFile(fIn, zOut, vPath, file.lastModified(), null, mode);
} finally {
fIn.close();
}
}

/**
* Ensure all parent dirs of a given entry have been added.
* @deprecated use five-arg version instead.
*/
protected void addParentDirs(File baseDir, String entry,
ZipOutputStream zOut, String prefix)
throws IOException {
addParentDirs(baseDir, entry, zOut, prefix,
ZipFileSet.DEFAULT_DIR_MODE);
}

/**
* Ensure all parent dirs of a given entry have been added.
*
* @since Ant 1.6
*/
protected void addParentDirs(File baseDir, String entry,
ZipOutputStream zOut, String prefix,
int dirMode)
throws IOException {
if (!doFilesonly) {
Stack directories = new Stack();
int slashPos = entry.length();
@@ -855,7 +928,7 @@ public class Zip extends MatchingTask {
} else {
f = new File(dir);
}
zipDir(f, zOut, prefix + dir);
zipDir(f, zOut, prefix + dir, dirMode);
}
}
}
@@ -874,10 +947,14 @@ public class Zip extends MatchingTask {

String prefix = "";
String fullpath = "";
int fileMode = ZipFileSet.DEFAULT_FILE_MODE;
int dirMode = ZipFileSet.DEFAULT_DIR_MODE;
if (fs instanceof ZipFileSet) {
ZipFileSet zfs = (ZipFileSet) fs;
prefix = zfs.getPrefix();
fullpath = zfs.getFullpath();
fileMode = zfs.getFileMode();
dirMode = zfs.getDirMode();
}

if (prefix.length() > 0
@@ -889,10 +966,10 @@ public class Zip extends MatchingTask {
// Need to manually add either fullpath's parent directory, or
// the prefix directory, to the archive.
if (prefix.length() > 0) {
addParentDirs(null, prefix, zOut, "");
zipDir(null, zOut, prefix);
addParentDirs(null, prefix, zOut, "", dirMode);
zipDir(null, zOut, prefix, dirMode);
} else if (fullpath.length() > 0) {
addParentDirs(null, fullpath, zOut, "");
addParentDirs(null, fullpath, zOut, "", dirMode);
}

if (fs instanceof ZipFileSet
@@ -900,7 +977,7 @@ public class Zip extends MatchingTask {
addZipEntries((ZipFileSet) fs, ds, zOut, prefix, fullpath);
} else {
// Add the fileset.
addFiles(ds, zOut, prefix, fullpath);
addFiles(ds, zOut, prefix, fullpath, dirMode, fileMode);
}
}
}


+ 18
- 2
src/main/org/apache/tools/ant/types/ZipFileSet.java View File

@@ -80,12 +80,28 @@ import org.apache.tools.zip.UnixStat;
*/
public class ZipFileSet extends FileSet {

/**
* Default value for the dirmode attribute.
*
* @since Ant 1.6
*/
public static final int DEFAULT_DIR_MODE =
UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;

/**
* Default value for the filemode attribute.
*
* @since Ant 1.6
*/
public static final int DEFAULT_FILE_MODE =
UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;

private File srcFile = null;
private String prefix = "";
private String fullpath = "";
private boolean hasDir = false;
private int fileMode = UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
private int dirMode = UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;
private int fileMode = DEFAULT_FILE_MODE;
private int dirMode = DEFAULT_DIR_MODE;

public ZipFileSet() {
super();


+ 2
- 0
src/main/org/apache/tools/zip/ZipEntry.java View File

@@ -198,6 +198,8 @@ public class ZipEntry extends java.util.zip.ZipEntry {
*/
public void setUnixMode(int mode) {
setExternalAttributes((mode << 16)
// MS-DOS read-only attribute
| ((mode & 0200) == 0 ? 1 : 0)
// MS-DOS directory flag
| (isDirectory() ? 0x10 : 0));
platform = PLATFORM_UNIX;


+ 14
- 1
src/testcases/org/apache/tools/zip/ZipEntryTest.java View File

@@ -131,12 +131,25 @@ public class ZipEntryTest extends TestCase {
(ze.getExternalAttributes() >> 16) & 0xFFFF);
assertEquals(0, ze.getExternalAttributes() & 0xFFFF);

ze.setUnixMode(0444);
assertEquals(3, ze.getPlatform());
assertEquals(0444,
(ze.getExternalAttributes() >> 16) & 0xFFFF);
assertEquals(1, ze.getExternalAttributes() & 0xFFFF);

ze = new ZipEntry("foo/");
assertEquals(0, ze.getPlatform());
ze.setUnixMode(0777);
assertEquals(3, ze.getPlatform());
assertEquals(0777,
(ze.getExternalAttributes() >> 16) & 0xFFFF);
assertEquals(0x10, ze.getExternalAttributes() & 0xFFFF);

ze.setUnixMode(0577);
assertEquals(3, ze.getPlatform());
assertEquals(0577,
(ze.getExternalAttributes() >> 16) & 0xFFFF);
assertEquals(0x10, ze.getExternalAttributes() & 0xFFFF);
assertEquals(0x11, ze.getExternalAttributes() & 0xFFFF);
}

}

Loading…
Cancel
Save