提交 d184d53b 编写于 作者: K Kohsuke Kawaguchi

[JENKINS-9118]

Tar/untar now correctly supports symlinks.
上级 48daab0f
......@@ -55,6 +55,8 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=bug>
tar/untar now correctly handles symlinks.
<li class=bug>
Fixed a bug in the auto-overwrite of bundled plugins on Windows.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-12514">issue 12514</a>)
......
......@@ -46,8 +46,9 @@ import hudson.util.IOException2;
import hudson.util.HeadBufferingStream;
import hudson.util.FormValidation;
import hudson.util.IOUtils;
import static hudson.Util.*;
import static hudson.util.jna.GNUCLibrary.LIBC;
import static hudson.Util.fixEmpty;
import static hudson.FilePath.TarCompression.GZIP;
import hudson.org.apache.tools.tar.TarInputStream;
import hudson.util.io.Archiver;
......@@ -75,6 +76,7 @@ import java.io.OutputStream;
import java.io.Serializable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
......@@ -331,6 +333,15 @@ public final class FilePath implements Serializable {
zip(os,(FileFilter)null);
}
public void zip(FilePath dst) throws IOException, InterruptedException {
OutputStream os = dst.write();
try {
zip(os);
} finally {
os.close();
}
}
/**
* Creates a zip file from this directory by using the specified filter,
* and sends the result to the given output stream.
......@@ -529,6 +540,39 @@ public final class FilePath implements Serializable {
}));
}
/**
* Creates a symlink to the specified target.
*
* @param target
* The file that the symlink should point to.
* @param listener
* If symlink creation requires a help of an external process, the error will be reported here.
* @since 1.456
*/
public void symlinkTo(final String target, final TaskListener listener) throws IOException, InterruptedException {
act(new FileCallable<Void>() {
public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
Util.createSymlink(f.getParentFile(),target,f.getName(),listener);
return null;
}
});
}
/**
* Resolves symlink, if the given file is a symlink. Otherwise return null.
* <p>
* If the resolution fails, report an error.
*
* @since 1.456
*/
public String readLink() throws IOException, InterruptedException {
return act(new FileCallable<String>() {
public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
return Util.resolveSymlink(f);
}
});
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
......@@ -1756,7 +1800,13 @@ public final class FilePath implements Serializable {
File parent = f.getParentFile();
if (parent != null) parent.mkdirs();
IOUtils.copy(t,f);
byte linkFlag = (Byte) LINKFLAG_FIELD.get(te);
if (linkFlag==TarEntry.LF_SYMLINK) {
new FilePath(f).symlinkTo(te.getLinkName(), TaskListener.NULL);
} else {
IOUtils.copy(t,f);
}
f.setLastModified(te.getModTime().getTime());
int mode = te.getMode()&0777;
if(mode!=0 && !Functions.isWindows()) // be defensive
......@@ -1765,6 +1815,11 @@ public final class FilePath implements Serializable {
}
} catch(IOException e) {
throw new IOException2("Failed to extract "+name,e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // process this later
throw new IOException2("Failed to extract "+name,e);
} catch (IllegalAccessException e) {
throw new IOException2("Failed to extract "+name,e);
} finally {
t.close();
}
......@@ -2124,4 +2179,18 @@ public final class FilePath implements Serializable {
return o1.length()-o2.length();
}
};
private static final Field LINKFLAG_FIELD = getTarEntryLinkFlagField();
private static Field getTarEntryLinkFlagField() {
try {
Field f = TarEntry.class.getDeclaredField("linkFlag");
f.setAccessible(true);
return f;
} catch (SecurityException e) {
throw new AssertionError(e);
} catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
}
}
......@@ -1022,6 +1022,14 @@ public class Util {
}
}
/**
* @deprecated as of 1.456
* Use {@link #resolveSymlink(File)}
*/
public static String resolveSymlink(File link, TaskListener listener) throws InterruptedException, IOException {
return resolveSymlink(link);
}
/**
* Resolves symlink, if the given file is a symlink. Otherwise return null.
* <p>
......@@ -1030,7 +1038,7 @@ public class Util {
* @param listener
* If we rely on an external command to resolve symlink, this is it.
*/
public static String resolveSymlink(File link, TaskListener listener) throws InterruptedException, IOException {
public static String resolveSymlink(File link) throws InterruptedException, IOException {
if(Functions.isWindows()) return null;
String filename = link.getAbsolutePath();
......
......@@ -101,6 +101,18 @@ public abstract class DirScanner implements Serializable {
DirectoryScanner ds = fs.getDirectoryScanner(new org.apache.tools.ant.Project());
for( String f : ds.getIncludedFiles()) {
File file = new File(dir, f);
if (visitor.understandsSymlink()) {
try {
String target = Util.resolveSymlink(file,TaskListener.NULL);
if (target!=null) {
visitor.visitSymlink(file,target,f);
continue;
}
} catch (InterruptedException e) {
throw (IOException)new InterruptedIOException().initCause(e);
}
}
visitor.visit(file,f);
}
}
......
......@@ -23,6 +23,8 @@
*/
package hudson;
import hudson.FilePath.TarCompression;
import hudson.model.TaskListener;
import hudson.remoting.LocalChannel;
import hudson.remoting.VirtualChannel;
import hudson.util.IOException2;
......@@ -356,4 +358,25 @@ public class FilePathTest extends ChannelTestCase {
}
}
public void testSymlinkInTar() throws Exception {
if (Functions.isWindows()) return; // can't test on Windows
FilePath tmp = new FilePath(Util.createTempDir());
try {
FilePath in = tmp.child("in");
in.mkdirs();
in.child("a").touch(0);
in.child("b").symlinkTo("a", TaskListener.NULL);
FilePath tar = tmp.child("test.tar");
in.tar(tar.write(), "**/*");
FilePath dst = in.child("dst");
tar.untar(dst, TarCompression.NONE);
assertEquals("a",dst.child("b").readLink());
} finally {
tmp.deleteRecursive();
}
}
}
......@@ -136,7 +136,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.8.0</version>
<version>1.8.3</version>
</dependency>
<dependency>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册