Skip to content

#1982: Modified fallback link creation on windows#2062

Open
oanding-blrng wants to merge 10 commits into
devonfw:mainfrom
oanding-blrng:fix/1982-relative-option-link-not-honered-fix
Open

#1982: Modified fallback link creation on windows#2062
oanding-blrng wants to merge 10 commits into
devonfw:mainfrom
oanding-blrng:fix/1982-relative-option-link-not-honered-fix

Conversation

@oanding-blrng

@oanding-blrng oanding-blrng commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

This PR fixes #1982

Implemented changes according to comments and hints in ticket #1982.

Behavior of ide ln on Windows without permissions:

Target Command Old Behavior New Behavior
Directory ln conf conf-link AccessDeniedException bubbled up to the user. Fallback with mklink /j <link> <target> with log information, that mklink junction had to used.
Directory ln -r conf conf-link AccessDeniedException bubbled up to the user. Fallback with mklink /j <link> <target> with log information, that mklink junction had to used.
Directory ln -s conf conf-link Fallback with mklink /j <link> <target> with log information about lack of permissions, use of mklink with junction and link to documenation. Fallback with mklink /j <link> <target> with log information, that mklink junction had to be used.
Directory ln -s -r conf conf-link Fallback with mklink /j <link> <target> with log information about lack of permissions, use of mklink with junction and link to documenation. Fallback with mklink /j <link> <target> with log information, that mklink junction had to be used + mention of wiki.
File ln test.txt test-link.txt Hardlink was created via Java. Hardlink was created via Java.
File ln -r test.txt test-link.txt Hardlink was created via Java. Hardlink was created via Java.
File ln -s test.txt test-link.txt Fallback with mklink /d <link> <target> as fallback returns with exit code 1 due to lack of permissions.
Creates hardlink via Java as second fallback.
Fallback with Java hardlink with log information, that hardlink as fallback was created.
File ln -s -r test.txt test-link.txt Fallback with mklink /d <link> <target> as fallback returns with exit code 1 due to lack of permissions.
Creates hardlink via Java as second fallback.
Fallback with Java hardlink with log information, that hardlink as fallback was created.

Behavior of ide ln on Windows with permissions:

Target Command Old Behavior New Behavior
Directory ln conf conf-link AccessDeniedException bubbled up to user. Fallback with mklink /j <link> <target> with log information, that mklink junction had to be used.
Directory ln -r conf conf-link AccessDeniedException bubbled up to user. Fallback with mklink /j <link> <target> with log information, that mklink junction had to be used + mention of wiki.
Directory ln -s conf conf-link Symlink was created via Java. Symlink was created via Java.
Directory ln -s -r conf conf-link Symlink was created via Java. Symlink was created via Java.
File ln test.txt test-link.txt Hardlink was created via Java. Hardlink was created via Java.
File ln -r test.txt test-link.txt Hardlink was created via Java. Hardlink was created via Java.
File ln -s test.txt test-link.txt Symlink was created via Java. Symlink was created via Java.
File ln -s -r test.txt test-link.txt Symlink was created via Java. Symlink was created via Java.

Implemented changes:

  • Modified method signature of link in FileAccess.java to return PathLinkType instead of void.
  • Modified implementation of link method in FileAccessImpl.java to return created PathLinkType. Also rewrote fallback on Windows machines to use correct fallback according to type (directory or file).
  • Modified LnCommandlet.java to check for created PathLinkType in case of set relative flag and log information due to lacking permissions.
  • Fixed bug, which caused bubbling up an AccessDeniedException up to the user.

Notes:

  • Even with permissions I wasn't able to create hardlink on directories via mklink /h <link> <target> because I would got "Zugriff verweigert". Creation of hardlinks on files would be possible, but this fallback is not needed, because Java is always able to create hardlinks. This is also the reason, why mklink /j is used as fallback.

Testing instructions

Please add conscise, understandable instructions on how a reviewer can test/verify the functionality of your contribution here:

User has not permissions:

  1. Make sure, you are on Windows and don't have the permissions mentioned in https://github.com/devonfw/IDEasy/blob/main/documentation/symlink.adoc in order to observe fallback behavior
  2. Create file C:\Users\user\projects\IDEasy\test.txt
  3. Create a run configuration for IDEasy
  4. Let the working directory point to C:\Users\user\projects\IDEasy
  5. Execute all eight commands mentioned in the table above and new behavior without permissions should be observed

User has persmissions:

  1. Make sure, you have permissions mentioned in https://github.com/devonfw/IDEasy/blob/main/documentation/symlink.adoc
  2. Execute all eight commands mentioned in the table above and new behavior with permissions should be observed

Checklist for this PR

Make sure everything is checked before merging this PR. For further info please also see
our DoD.

  • When running mvn clean test locally all tests pass and build is successful
  • PR title is of the form #«issue-id»: «brief summary» (e.g. #921: fixed setup.bat). If no issue ID exists, title only.
  • PR top-level comment summarizes what has been done and contains link to addressed issue(s)
  • PR and issue(s) have suitable labels
  • Issue is set to In Progress and assigned to you or there is no issue (might happen for very small PRs)
  • You followed all coding conventions
  • You have added the issue implemented by your PR in CHANGELOG.adoc unless issue is labeled
    with internal
  • You have formulated clear instructions on how to test your contribution under "Testing instructions"

* Modified method signature of link in FileAccess.java to return PathLinkType instead of void
* Modified implementation of link method in FileAccessImpl.java to return created PathLinkType. Also rewrote fallback on Windows machines to use correct fallback according to type (directory or file).
* Modified LnCommandlet.java to check for created PathLinkType in case of set relative flag and log information due to lacking permissions.
@github-project-automation github-project-automation Bot moved this to 🆕 New in IDEasy board Jun 22, 2026
@oanding-blrng oanding-blrng self-assigned this Jun 22, 2026
@oanding-blrng oanding-blrng added windows specific for Microsoft Windows OS link symlinks and windows junctions labels Jun 22, 2026
@coveralls

coveralls commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 29091073904

Coverage decreased (-0.06%) to 72.17%

Details

  • Coverage decreased (-0.06%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 163 coverage regressions across 4 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

163 previously-covered lines in 4 files lost coverage.

File Lines Losing Coverage Coverage
com/devonfw/tools/ide/io/FileAccessImpl.java 155 67.83%
com/devonfw/tools/ide/commandlet/LnCommandlet.java 5 71.43%
com/devonfw/tools/ide/version/VersionSegment.java 2 90.55%
com/devonfw/tools/ide/tool/ide/IdeToolCommandlet.java 1 78.33%

Coverage Stats

Coverage Status
Relevant Lines: 16715
Covered Lines: 12572
Line Coverage: 75.21%
Relevant Branches: 7464
Covered Branches: 4878
Branch Coverage: 65.35%
Branches in Coverage %: Yes
Coverage Strength: 3.18 hits per line

💛 - Coveralls

@oanding-blrng oanding-blrng changed the title #1982: Modified fallback link creation an windows #1982: Modified fallback link creation onwindows Jun 22, 2026
@oanding-blrng oanding-blrng changed the title #1982: Modified fallback link creation onwindows #1982: Modified fallback link creation on windows Jun 22, 2026
@oanding-blrng oanding-blrng moved this from 🆕 New to 🏗 In progress in IDEasy board Jun 22, 2026
…s to use junctions.

* Modified mklinkOnWindows() to set option for mklink to /j if dealing with hard links, because option /h doesn't work because of missing permissions.
* Moved adding args and running process for mklink into try block to avoid bubbling up exception in runMklink().
@oanding-blrng oanding-blrng marked this pull request as ready for review June 24, 2026 07:35
@oanding-blrng oanding-blrng moved this from 🏗 In progress to Team Review in IDEasy board Jun 24, 2026
@tineff96 tineff96 self-assigned this Jun 24, 2026
@tineff96 tineff96 removed their assignment Jul 2, 2026
…ption-link-not-honered-fix

# Conflicts:
#	CHANGELOG.adoc
@Caylipp

Caylipp commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Good job, you implemented a Windows fallback logic and fixed the issue 1982.

I tested the commands with permissions and also ran mvn clean test successfully.

The Implementation also looks good to me.

@Caylipp Caylipp moved this from Team Review to 👀 In review in IDEasy board Jul 6, 2026

@hohwille hohwille left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oanding-blrng thanks for your PR. You implemented the story well and perfectly tested and documented various combinations and scenarios. Awesome job 👍

There is one small misunderstanding regarding the log message in the LnCommandlet and the existing log message for the fallback. Also I added some other small review comments for improvement. When resolved ready for merge.

"Due to lack of permissions, Microsoft's mklink with junction had to be used to create a Symlink. See\n"
+ "https://github.com/devonfw/IDEasy/blob/main/documentation/symlink.adoc for further details. Error was: "
+ e.getMessage());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message should still stay here.
We might want to adopt it or log different things depending if we have a directory or not.

Comment on lines +96 to +98
LOG.info(
"Due to lack of permissions, Microsoft's mklink with junction had to be used to create a Symlink. See\n"
+ "https://github.com/devonfw/IDEasy/blob/main/documentation/symlink.adoc for further details.");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
LOG.info(
"Due to lack of permissions, Microsoft's mklink with junction had to be used to create a Symlink. See\n"
+ "https://github.com/devonfw/IDEasy/blob/main/documentation/symlink.adoc for further details.");
LOG.warn("Windows junction had to be used as fallback that only supports absolute paths. Therefore we cannot create a relative link as requested.");

option = directoryTarget ? "/j" : "/d";
} else {
// Manual override of option in case of HARD_LINK, because of missing permissions for `mklink /h ...`
option = "/j";

@hohwille hohwille Jul 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C:\>mklink /?
Creates a symbolic link.

MKLINK [[/D] | [/H] | [/J]] Link Target

        /D      Creates a directory symbolic link.  Default is a file
                symbolic link.
        /H      Creates a hard link instead of a symbolic link.
        /J      Creates a Directory Junction.
        Link    Specifies the new symbolic link name.
        Target  Specifies the path (relative or absolute) that the new link
                refers to.

If /h (aka /H) was not supported at all, it would not make to keep it in PathLinkType.HARD_LINK.

As @shodiBoy1 recently found out, we already use a hardlink as fallback if mklink did not work.
However this does not even have to happen via mklink at all but can directly happen via Files.createLink.
Maybe it does not even make sense any more to pass PathLinkType to mklinkOnWindows since we only ever want to create a junction here. Then we could also remove PathLinkType.mklinkOption with its getter...
In the end mklink /h ... is the same as Files.createLink and mklink /d ... is more or less the same as Files.createSymbolicLink for a directory but that will fail again with the same permission error. So the only relevant option left for us is always /j.

throw new IllegalStateException("" + type);
}
} catch (FileSystemException e) {
} catch (FileSystemException | RuntimeException e) {

@hohwille hohwille Jul 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO we should not catch RuntimeException.
If for whatever reason we e.g. provide a type that is neither SYMBOLIC_LINK nor HARD_LINK (so maybe null or we added a 3rd type but forgot to handle it here) then we throw an IllegalStateException.
Catching that (or a NullPointerException) and assuming we have a permission issue and therefore should retry the link creation with a fallback strategy does not make any sense to me.

Suggested change
} catch (FileSystemException | RuntimeException e) {
} catch (FileSystemException e) {

Comment thread cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java Outdated
Comment thread cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java Outdated
Comment thread CHANGELOG.adoc
Comment thread CHANGELOG.adoc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

link symlinks and windows junctions windows specific for Microsoft Windows OS

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

relative option for link not honored

6 participants