diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 9cc93b7ab6c65..66d2ea9aae03c 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -132,6 +132,7 @@ import org.jenkinsci.remoting.RoleSensitive; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.Function; import org.kohsuke.stapler.Stapler; import static hudson.FilePath.TarCompression.GZIP; @@ -3262,9 +3263,14 @@ public Boolean invoke(@Nonnull File parentFile, @Nonnull VirtualChannel channel) Path parentAbsolutePath = Util.fileToPath(parentFile.getAbsoluteFile()); Path parentRealPath; try { - parentRealPath = parentAbsolutePath.toRealPath(); + if (Functions.isWindows()) { + parentRealPath = this.windowsToRealPath(parentAbsolutePath); + } else { + parentRealPath = parentAbsolutePath.toRealPath(); + } } - catch(NoSuchFileException e) { + catch (NoSuchFileException e) { + LOGGER.log(Level.FINE, String.format("Cannot find the real path to the parentFile: %s", parentAbsolutePath), e); return false; } @@ -3296,10 +3302,11 @@ public Boolean invoke(@Nonnull File parentFile, @Nonnull VirtualChannel channel) try{ Path child = currentFileAbsolutePath.toRealPath(); if (!child.startsWith(parentRealPath)) { + LOGGER.log(Level.FINE, "Child [{0}] does not start with parent [{1}] => not descendant", new Object[]{ child, parentRealPath }); return false; } } catch (NoSuchFileException e) { - // nonexistent file + // nonexistent file / Windows Server 2016 + MSFT docker // in case this folder / file will be copied somewhere else, // it becomes the responsibility of that system to check the isDescendant with the existing links // we are not taking the parentRealPath to avoid possible problem @@ -3322,6 +3329,21 @@ public Boolean invoke(@Nonnull File parentFile, @Nonnull VirtualChannel channel) } return current; } + + private @Nonnull Path windowsToRealPath(@Nonnull Path path) throws IOException { + try { + return path.toRealPath(); + } + catch (IOException e) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, String.format("relaxedToRealPath cannot use the regular toRealPath on %s, trying with toRealPath(LinkOption.NOFOLLOW_LINKS)", path), e); + } + } + + // that's required for specific environment like Windows Server 2016, running MSFT docker + // where the root is a + return path.toRealPath(LinkOption.NOFOLLOW_LINKS); + } } private static final SoloFilePathFilter UNRESTRICTED = SoloFilePathFilter.wrap(FilePathFilter.UNRESTRICTED); diff --git a/core/src/main/java/jenkins/util/VirtualFile.java b/core/src/main/java/jenkins/util/VirtualFile.java index a79c06a2f2627..0cce3d1a65b71 100644 --- a/core/src/main/java/jenkins/util/VirtualFile.java +++ b/core/src/main/java/jenkins/util/VirtualFile.java @@ -544,7 +544,11 @@ public Collection list(String includes, String excludes, boolean useDefa private boolean isIllegalSymlink() { try { - return !this.isDescendant(""); + String myPath = f.toPath().toRealPath().toString(); + String rootPath = root.toPath().toRealPath().toString(); + if (!myPath.equals(rootPath) && !myPath.startsWith(rootPath + File.separatorChar)) { + return true; + } } catch (IOException x) { Logger.getLogger(VirtualFile.class.getName()).log(Level.FINE, "could not determine symlink status of " + f, x); } catch (InvalidPathException x2) {