Skip to content

Commit

Permalink
devonfw#139: always use toRealPath in symlink
Browse files Browse the repository at this point in the history
  • Loading branch information
MattesMrzik committed Dec 12, 2023
1 parent b3994c8 commit 7683f5b
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 14 deletions.
46 changes: 34 additions & 12 deletions cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -339,19 +339,35 @@ private void deleteLinkIfExists(Path path) throws IOException {
* @param relative the {@code relative} flag.
* @return the adapted {@link Path}.
*/
private Path adaptPath(Path source, Path targetLink, boolean relative) {
private Path adaptPath(Path source, Path targetLink, boolean relative) throws IOException {

if (relative && source.isAbsolute()) {
source = targetLink.getParent().relativize(source);
// to make relative links like this work: dir/link -> dir
return (source.toString().isEmpty()) ? Paths.get(".") : source;
}
if (!relative && !source.isAbsolute()) {
if (source.isAbsolute()) {
try {
source = targetLink.resolveSibling(source).toRealPath(LinkOption.NOFOLLOW_LINKS);
source = source.toRealPath(LinkOption.NOFOLLOW_LINKS); // to transform ../d1/../d2 to ../d2
} catch (IOException e) {
throw new IllegalStateException(
"Failed to create fallback symlink from " + source + " with target link " + targetLink, e);
throw new IOException("source.toRealPath() failed for source " + source, e);
}
if (relative) {
source = targetLink.getParent().relativize(source);
// to make relative links like this work: dir/link -> dir
source = (source.toString().isEmpty()) ? Paths.get(".") : source;
}
} else { // source is relative
if (relative) {
// even though the source is already relative, toRealPath should be called to transform paths like
// this ../d1/../d2 to ../d2
source = targetLink.getParent()
.relativize(targetLink.resolveSibling(source).toRealPath(LinkOption.NOFOLLOW_LINKS));
source = (source.toString().isEmpty()) ? Paths.get(".") : source;
} else { // !relative
try {
source = targetLink.resolveSibling(source).toRealPath(LinkOption.NOFOLLOW_LINKS);
} catch (IOException e) {
throw new IOException(
"targetLink.resolveSibling(source).toRealPath(LinkOption.NOFOLLOW_LINKS) failed for source " + source
+ " and target link " + targetLink,
e);
}
}
}
return source;
Expand All @@ -362,7 +378,7 @@ private void createWindowsJunction(Path source, Path targetLink) {
Path fallbackPath = null;
if (!source.isAbsolute()) {
try {
fallbackPath = targetLink.resolveSibling(source).toRealPath();
fallbackPath = targetLink.resolveSibling(source).toRealPath(LinkOption.NOFOLLOW_LINKS);
} catch (IOException ioe) {
throw new IllegalStateException("Failed to create fallback symlink at " + fallbackPath, ioe);
}
Expand All @@ -387,7 +403,13 @@ private void createWindowsJunction(Path source, Path targetLink) {
@Override
public void symlink(Path source, Path targetLink, boolean relative) {

Path adaptedSource = adaptPath(source, targetLink, relative);
Path adaptedSource = null;
try {
adaptedSource = adaptPath(source, targetLink, relative);
} catch (IOException e) {
throw new IllegalStateException("Failed to adapt source for source (" + source + ") target (" + targetLink
+ ") and relative (" + relative + ")", e);
}
this.context.trace("Creating {} symbolic link {} pointing to {}", adaptedSource.isAbsolute() ? "" : "relative",
targetLink, adaptedSource);

Expand Down
35 changes: 33 additions & 2 deletions cli/src/test/java/com/devonfw/tools/ide/io/FileAccessImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,37 @@ public void testSymlinkWindowsJunctionsCanNotPointToFiles(@TempDir Path tempDir)
assertThat(e1).hasMessageContaining("These junctions can only point to directories or other junctions");
}

@Test
public void testSymlinkShortcutPaths(@TempDir Path tempDir) {

// arrange
IdeContext context = IdeTestContextMock.get();
FileAccess fileAccess = new FileAccessImpl(context);
Path dir = tempDir.resolve("parent");
createDirs(fileAccess, dir);
fileAccess.mkdirs(dir.resolve("d3"));
boolean readLinks = !windowsJunctionsAreUsed(context, tempDir);

// act
fileAccess.symlink(dir.resolve("d3/../d1"), dir.resolve("link1"), false);
fileAccess.symlink(Path.of("d3/../d1"), dir.resolve("link2"), false);
fileAccess.symlink(dir.resolve("d3/../d1"), dir.resolve("link3"), true);
fileAccess.symlink(Path.of("d3/../d1"), dir.resolve("link4"), true);
fileAccess.delete(dir.resolve("d3"));

// assert
assertSymlinkToRealPath(dir.resolve("link1"), dir.resolve("d1"));
assertSymlinkToRealPath(dir.resolve("link2"), dir.resolve("d1"));
assertSymlinkToRealPath(dir.resolve("link3"), dir.resolve("d1"));
assertSymlinkToRealPath(dir.resolve("link4"), dir.resolve("d1"));
if (readLinks) {
assertSymlinkRead(dir.resolve("link1"), dir.resolve("d1"));
assertSymlinkRead(dir.resolve("link2"), dir.resolve("d1"));
assertSymlinkRead(dir.resolve("link3"), dir.resolve("d1"));
assertSymlinkRead(dir.resolve("link4"), dir.resolve("d1"));
}
}

private void createDirs(FileAccess fileAccess, Path dir) {

fileAccess.mkdirs(dir.resolve("d1/d11/d111/d1111"));
Expand All @@ -235,7 +266,7 @@ private void createSymlinksByPassingRelativeSource(FileAccess fa, Path dir, bool
fa.symlink(Path.of("."), dir.resolve("d1/d11/link_to_d11"), relative);
fa.symlink(Path.of("d111"), dir.resolve("d1/d11/link_to_d111"), relative);
fa.symlink(Path.of("d111/d1111"), dir.resolve("d1/d11/link_to_d1111"), relative);
fa.symlink(Path.of("../nonExistingDir/../../d2"), dir.resolve("d1/d11/link_to_d2"), relative);
fa.symlink(Path.of("../../d1/../d2"), dir.resolve("d1/d11/link_to_d2"), relative);
fa.symlink(Path.of("../../d2/d22"), dir.resolve("d1/d11/link_to_d22"), relative);
fa.symlink(Path.of("../../d2/d22/d222"), dir.resolve("d1/d11/link_to_d222"), relative);

Expand All @@ -253,7 +284,7 @@ private void createSymlinks(FileAccess fa, Path dir, boolean relative) {
fa.symlink(dir.resolve("d1/d11"), dir.resolve("d1/d11/link_to_d11"), relative);
fa.symlink(dir.resolve("d1/d11/d111"), dir.resolve("d1/d11/link_to_d111"), relative);
fa.symlink(dir.resolve("d1/d11/d111/d1111"), dir.resolve("d1/d11/link_to_d1111"), relative);
fa.symlink(dir.resolve("nonExistingDir/../d2"), dir.resolve("d1/d11/link_to_d2"), relative);
fa.symlink(dir.resolve("d1/../d2"), dir.resolve("d1/d11/link_to_d2"), relative);
fa.symlink(dir.resolve("d2/d22"), dir.resolve("d1/d11/link_to_d22"), relative);
fa.symlink(dir.resolve("d2/d22/d222"), dir.resolve("d1/d11/link_to_d222"), relative);

Expand Down

0 comments on commit 7683f5b

Please sign in to comment.