diff --git a/changelog.html b/changelog.html
index 1d9e566633..d364fd33fc 100644
--- a/changelog.html
+++ b/changelog.html
@@ -58,6 +58,9 @@ Upcoming changes
Deleting an external run did not immediately remove it from build list, leading to errors from log rotation.
(issue 19377)
+
+ When copying a directory from master to slave fails due to an error on the slave, properly report it.
+ (issue 9540)
Annotate the Advanced section if some fields are already customized.
(issue 3107)
diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java
index 7466d9d6af..7bba63d3f7 100644
--- a/core/src/main/java/hudson/FilePath.java
+++ b/core/src/main/java/hudson/FilePath.java
@@ -1931,13 +1931,19 @@ public final class FilePath implements Serializable {
}
}
});
- int r = writeToTar(new File(remote),fileMask,excludes,TarCompression.GZIP.compress(pipe.getOut()));
+ Future future2 = actAsync(new FileCallable() {
+ private static final long serialVersionUID = 1L;
+ @Override public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
+ return writeToTar(new File(remote),fileMask,excludes,TarCompression.GZIP.compress(pipe.getOut()));
+ }
+ });
try {
+ // JENKINS-9540 in case the reading side failed, report that error first
future.get();
+ return future2.get();
} catch (ExecutionException e) {
throw new IOException2(e);
}
- return r;
} else {
// remote -> local copy
final Pipe pipe = Pipe.createRemoteToLocal();
diff --git a/core/src/test/java/hudson/FilePathTest.java b/core/src/test/java/hudson/FilePathTest.java
index 04dca89a1a..762d0e7d96 100644
--- a/core/src/test/java/hudson/FilePathTest.java
+++ b/core/src/test/java/hudson/FilePathTest.java
@@ -206,6 +206,46 @@ public class FilePathTest extends ChannelTestCase {
}
}
+ @Bug(9540)
+ public void testErrorMessageInRemoteCopyRecursive() throws Exception {
+ File tmp = Util.createTempDir();
+ try {
+ File src = new File(tmp, "src");
+ File dst = new File(tmp, "dst");
+ FilePath from = new FilePath(src);
+ FilePath to = new FilePath(british, dst.getAbsolutePath());
+ for (int i = 0; i < 10000; i++) {
+ // TODO is there a simpler way to force the TarOutputStream to be flushed and the reader to start?
+ // Have not found a way to make the failure guaranteed.
+ OutputStream os = from.child("content" + i).write();
+ try {
+ for (int j = 0; j < 1024; j++) {
+ os.write('.');
+ }
+ } finally {
+ os.close();
+ }
+ }
+ FilePath toF = to.child("content0");
+ toF.write().close();
+ toF.chmod(0400);
+ try {
+ from.copyRecursiveTo(to);
+ // on Windows this may just succeed; OK, test did not prove anything then
+ } catch (IOException x) {
+ if (Functions.printThrowable(x).contains("content0")) {
+ // Fine, error message talks about permission denied.
+ } else {
+ throw x;
+ }
+ } finally {
+ toF.chmod(700);
+ }
+ } finally {
+ Util.deleteRecursive(tmp);
+ }
+ }
+
public void testArchiveBug4039() throws Exception {
File tmp = Util.createTempDir();
try {