Skip to content
Browse files
[JENKINS-29956] Test Windows junctions before Java 7 symlink (#1787)
* Test Windows junctions before Java 7 symlink

Apparently Java 7 (and later) do not count Windows junctions as
symlinks.  When you drill down into the BasicFileAttributes structure,
reparse points are only counted under isOther.  So, since we already
have code that properly detects Windows Junctions, let's use that first
and then fallback to the Java 7 code.

* Add comment for change & link bug.

* Add test case

* Switch to Functions.isWindows().

* Use non-deprecated APIs for tmp file/dir mgmt

* Add better debugging.

* Allow spaces in junction name & dir name.

To do that, it was easier to use ProcessBuilder vs building up the correct
string in code to send to cmd.exe.

Also, I'll note that according to,
mklink is in internal cmd.exe command.  So, it must be invoked through
"cmd.exe /c" for it to work.

* Remove comment mentioning bug number.

* Remove debugging output, per Baptiste's request

* Add newline at end of file.

* Make sure that the junction was removed.

* Use assertEquals to show retcode of Process.waitFor.

* Fix compilation errors by importing functions
  • Loading branch information
dbroady1 authored and oleg-nenashev committed Aug 22, 2016
1 parent aa3188e commit f4edf91782720d2e1246ddb48554511912af1491
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 4 deletions.
@@ -492,17 +492,30 @@ private static String deleteFailExceptionMessage(File whatWeWereTryingToRemove,
//Taken from
public static boolean isSymlink(@Nonnull File file) throws IOException {
Boolean r = isSymlinkJava7(file);
if (r != null) {
return r;
* Windows Directory Junctions are effectively the same as Linux symlinks to directories.
* Unfortunately, the Java 7 NIO2 API function isSymbolicLink does not treat them as such.
* It thinks of them as normal directories. To use the NIO2 API & treat it like a symlink,
* you have to go through BasicFileAttributes and do the following check:
* isSymbolicLink() || isOther()
* The isOther() call will include Windows reparse points, of which a directory junction is.
* Since we already have a function that detects Windows junctions or symlinks and treats them
* both as symlinks, let's use that function and always call it before calling down to the
if (Functions.isWindows()) {
try {
return Kernel32Utils.isJunctionOrSymlink(file);
} catch (UnsupportedOperationException | LinkageError e) {
// fall through
Boolean r = isSymlinkJava7(file);
if (r != null) {
return r;
String name = file.getName();
if (name.equals(".") || name.equals(".."))
return false;
@@ -0,0 +1,48 @@
package hudson;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.Issue;

public class RemoveWindowsDirectoryJunctionTest {
public TemporaryFolder tmp = new TemporaryFolder();

public void windowsOnly() {

public void testJunctionIsRemovedButNotContents() throws Exception {
File subdir1 = tmp.newFolder("notJunction");
File f1 = new File(subdir1, "testfile1.txt");
assertTrue("Unable to create temporary file in notJunction directory", f1.createNewFile());
File j1 = makeJunction(tmp.getRoot(), subdir1);
assertFalse("Windows Junction should have been removed", j1.exists());
assertTrue("Contents of Windows Junction should not be removed", f1.exists());

private File makeJunction(File baseDir, File pointToDir) throws Exception {
File junc = new File(baseDir, "test Junction");
String cmd = "mklink /J \"" + junc.getPath() + "\" \"" + pointToDir.getPath() + "\"";
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", cmd);
Process p = pb.start();
assertEquals("Running mklink failed (cmd=" + cmd + ")", 0, p.waitFor());
return junc;

0 comments on commit f4edf91

Please sign in to comment.