Skip to content

add fallback for WinFSP directory#220

Merged
infeo merged 6 commits intodevelopfrom
feature/winfsp-fallback-dir
Mar 3, 2026
Merged

add fallback for WinFSP directory#220
infeo merged 6 commits intodevelopfrom
feature/winfsp-fallback-dir

Conversation

@infeo
Copy link
Copy Markdown
Member

@infeo infeo commented Mar 2, 2026

No description provided.

@infeo infeo self-assigned this Mar 2, 2026
@infeo infeo added this to the 6.0.1 milestone Mar 2, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 2, 2026

Warning

Rate limit exceeded

@infeo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between f5a192c and cfe6678.

📒 Files selected for processing (2)
  • src/main/java/org/cryptomator/frontend/fuse/mount/WinFspMountProvider.java
  • src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java

Walkthrough

The PR centralizes WinFsp discovery and DLL path selection by adding a new WinFspUtil class that locates and caches the WinFsp DLL path (selecting aarch64 vs x64), reads the install directory via the registry with a 3s timeout, and falls back to C:\Program Files (x86)\WinFsp\ if registry lookup fails. It removes the WinFspNotFoundException and adjusts existing callers (WinFspMountProvider, WinfspUtil) to use the new helper and simplified existence checks. Logging for registry success and fallback usage was added.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Add a pull request description explaining the motivation for adding the WinFsp fallback mechanism and any relevant implementation details.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'add fallback for WinFSP directory' directly reflects the main change: introducing a fallback mechanism for WinFsp directory resolution.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/winfsp-fallback-dir

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/java/org/cryptomator/frontend/fuse/mount/WinfspUtil.java (1)

32-37: Consider using updateAndGet for atomic initialization.

The current check-then-act pattern allows multiple threads to redundantly call readWinFspInstallDirFromRegistry(). While functionally correct (idempotent), this can be simplified:

♻️ Suggested atomic initialization pattern
 static String getWinFspInstallDir() {
-    if (cache.get() == null) {
-        cache.set(readWinFspInstallDirFromRegistry());
-    }
-    return cache.get();
+    return cache.updateAndGet(current -> current != null ? current : readWinFspInstallDirFromRegistry());
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/cryptomator/frontend/fuse/mount/WinfspUtil.java` around
lines 32 - 37, Replace the current check-then-act in getWinFspInstallDir with an
atomic update so multiple threads won't redundantly call
readWinFspInstallDirFromRegistry; use cache.updateAndGet(...) (or
cache.compareAndSet loop) to call readWinFspInstallDirFromRegistry only when
cache is null, returning the stored value, and keep references to the existing
symbols getWinFspInstallDir, cache, and readWinFspInstallDirFromRegistry to
locate and modify the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/cryptomator/frontend/fuse/mount/WinfspUtil.java`:
- Around line 59-61: The current isWinFspInstalled() uses
Files.exists(Path.of(getWinFspInstallDir())) which can return true for leftover
directories; update isWinFspInstalled() to verify presence of required WinFsp
binaries (e.g., check for the expected DLLs such as "winfsp-x64.dll" and/or
"winfsp.dll" inside getWinFspInstallDir()/bin) instead of just the directory,
and make isSupported() rely on that stronger check so it only advertises WinFsp
when those binaries are present.

---

Nitpick comments:
In `@src/main/java/org/cryptomator/frontend/fuse/mount/WinfspUtil.java`:
- Around line 32-37: Replace the current check-then-act in getWinFspInstallDir
with an atomic update so multiple threads won't redundantly call
readWinFspInstallDirFromRegistry; use cache.updateAndGet(...) (or
cache.compareAndSet loop) to call readWinFspInstallDirFromRegistry only when
cache is null, returning the stored value, and keep references to the existing
symbols getWinFspInstallDir, cache, and readWinFspInstallDirFromRegistry to
locate and modify the logic.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c04750c and 5aa0297.

📒 Files selected for processing (1)
  • src/main/java/org/cryptomator/frontend/fuse/mount/WinfspUtil.java

infeo added 3 commits March 3, 2026 12:02
* rename method getWinFspInstallDir ot getWinFspDLLPath
* resolve dll in util
* add doc
reducing risk by path manipulation
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java`:
- Around line 35-38: The DLL path is built by string concatenation which breaks
when getWinFspInstallDir() lacks a trailing separator; update WinFspUtil so
libPath is constructed using java.nio.file.Path joining (e.g., Path.of(...) or
Paths.get(...) and .resolve("bin").resolve(dllName)). Replace the current
installDir + "bin\\" + dllName expression with a Path-based join, then pass the
resulting toString() (or Path) into cache.set so the path is always correct
regardless of trailing separators.
- Around line 50-66: The reg invocation is brittle and the child Process `p`
isn't cleaned up on timeout; in WinFspUtil (the method that reads
REG_WINFSP_KEY/REG_WINFSP_VALUE and returns FALLBACK_PATH) change the
ProcessBuilder to call reg.exe by absolute path (e.g. System.getenv("windir") +
"\\\\System32\\\\reg.exe") instead of just "reg", keep `p` reference in a scope
visible to the catch handlers, and on TimeoutException (and other failure paths
where the process may still be running) call p.destroyForcibly() and wait for
termination before continuing; preserve the existing InterruptedException
handling (restore interrupt flag) and still return FALLBACK_PATH on failure.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa0297 and 4510766.

📒 Files selected for processing (2)
  • src/main/java/org/cryptomator/frontend/fuse/mount/WinFspMountProvider.java
  • src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java (2)

34-38: ⚠️ Potential issue | 🟠 Major

Build the DLL path with Path joining to avoid malformed paths.

Line 37 still depends on a trailing separator in installDir; registry values without it produce invalid paths (e.g., ...WinFspbin\...).

🔧 Proposed fix
 static String getWinFspDLLPath() {
 	if (cache.get() == null) {
 		var installDir = getWinFspInstallDir();
 		var dllName = (System.getProperty("os.arch").toLowerCase().contains("aarch64") ? "winfsp-a64.dll" : "winfsp-x64.dll");
-		var libPath = installDir + "bin\\" + dllName;
+		var libPath = Path.of(installDir, "bin", dllName).toString();
 		cache.set(libPath);
 	}
 	return cache.get();
 }
#!/bin/bash
# Verify unresolved string-concatenated DLL path construction.
rg -nP --type=java 'installDir\s*\+\s*"bin\\\\"\s*\+\s*dllName'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java` around
lines 34 - 38, The current construction of libPath in WinFspUtil relies on
string concatenation of installDir + "bin\\" + dllName and breaks if
getWinFspInstallDir() does not include a trailing separator; change this to
build the path using java.nio.file.Path/Paths (e.g., Paths.get(installDir,
"bin", dllName).toString()) so the OS-specific separators are handled correctly,
then store that resulting string into cache via cache.set(...), keeping the
existing cache.get() / cache.set(libPath) logic and the dllName selection
intact.

50-66: ⚠️ Potential issue | 🟠 Major

Harden reg.exe lookup and ensure child process cleanup on failure paths.

Line 51 hardcodes C:\Windows\System32\reg.exe (breaks non-standard Windows roots), and timeout/error paths do not forcibly terminate a possibly running child process.

🔧 Proposed fix
 static String getWinFspInstallDir() {
+	Process p = null;
 	try {
-		ProcessBuilder command = new ProcessBuilder("C:\\Windows\\System32\\reg.exe", "query", REG_WINFSP_KEY, "/v", REG_WINFSP_VALUE);
-		Process p = command.start();
+		var systemRoot = System.getenv().getOrDefault("SystemRoot", "C:\\Windows");
+		var regExe = Path.of(systemRoot, "System32", "reg.exe").toString();
+		ProcessBuilder command = new ProcessBuilder(regExe, "query", REG_WINFSP_KEY, "/v", REG_WINFSP_VALUE);
+		p = command.start();
 		ProcessHelper.waitForSuccess(p, 3, "`reg query`");
 		try (var reader = p.inputReader(StandardCharsets.UTF_8)) {
 			String result = reader.lines().filter(l -> l.contains(REG_WINFSP_VALUE)).findFirst().orElseThrow();
 			var installDir = result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim();
 			LOG.debug("Successfully read WinFsp directory {} from registry.", installDir);
 			return installDir;
 		}
 	} catch (TimeoutException | IOException | ProcessHelper.CommandFailedException | NoSuchElementException e) {
 		//no-op
 	} catch (InterruptedException e) {
 		Thread.currentThread().interrupt();
+	} finally {
+		if (p != null && p.isAlive()) {
+			p.destroyForcibly();
+		}
 	}
 	LOG.debug("Failed to read WinFsp directory from registry. Using fallback path {}", FALLBACK_PATH);
 	return FALLBACK_PATH;
 }
#!/bin/bash
# Verify hardcoded reg.exe path and missing process cleanup patterns.
rg -nP --type=java 'new\s+ProcessBuilder\("C:\\\\Windows\\\\System32\\\\reg\.exe"'
rg -nP --type=java 'destroyForcibly\s*\('
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java` around
lines 50 - 66, Replace the hardcoded "C:\\Windows\\System32\\reg.exe" in the
ProcessBuilder call with just "reg.exe" so the system path is respected (locate
the ProcessBuilder instantiation in WinFspUtil); additionally, ensure the child
Process referenced as p is always cleaned up on failure paths by capturing it in
a variable visible to the catch blocks and calling p.destroyForcibly() (and
optionally p.waitFor()) in the
TimeoutException/IOException/CommandFailedException catch and after
InterruptedException handling, making sure to re-set the interrupt for
InterruptedException and to still return FALLBACK_PATH when cleanup completes;
keep the existing use of ProcessHelper.waitForSuccess and the inputReader usage
but guarantee the process is forcibly terminated on all non-success paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java`:
- Around line 34-38: The current construction of libPath in WinFspUtil relies on
string concatenation of installDir + "bin\\" + dllName and breaks if
getWinFspInstallDir() does not include a trailing separator; change this to
build the path using java.nio.file.Path/Paths (e.g., Paths.get(installDir,
"bin", dllName).toString()) so the OS-specific separators are handled correctly,
then store that resulting string into cache via cache.set(...), keeping the
existing cache.get() / cache.set(libPath) logic and the dllName selection
intact.
- Around line 50-66: Replace the hardcoded "C:\\Windows\\System32\\reg.exe" in
the ProcessBuilder call with just "reg.exe" so the system path is respected
(locate the ProcessBuilder instantiation in WinFspUtil); additionally, ensure
the child Process referenced as p is always cleaned up on failure paths by
capturing it in a variable visible to the catch blocks and calling
p.destroyForcibly() (and optionally p.waitFor()) in the
TimeoutException/IOException/CommandFailedException catch and after
InterruptedException handling, making sure to re-set the interrupt for
InterruptedException and to still return FALLBACK_PATH when cleanup completes;
keep the existing use of ProcessHelper.waitForSuccess and the inputReader usage
but guarantee the process is forcibly terminated on all non-success paths.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4510766 and f5a192c.

📒 Files selected for processing (1)
  • src/main/java/org/cryptomator/frontend/fuse/mount/WinFspUtil.java

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Mar 3, 2026

@infeo infeo merged commit 5c76291 into develop Mar 3, 2026
12 checks passed
@infeo infeo deleted the feature/winfsp-fallback-dir branch March 3, 2026 11:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant