From ff115e2da8435f07db0faaff94f02a5d66206349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:44:26 +0000 Subject: [PATCH 01/78] Bump the java-test-dependencies group with 1 update (#56) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 06c3600..bcdf3c4 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 2.16.1 - 5.10.1 + 5.10.2 5.10.0 From 06ec348cc4b8669e39bb10d5509563d1cc13df50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:44:35 +0000 Subject: [PATCH 02/78] Bump the maven-build-plugins group with 1 update (#57) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bcdf3c4..02b2010 100644 --- a/pom.xml +++ b/pom.xml @@ -281,7 +281,7 @@ org.codehaus.mojo exec-maven-plugin - 3.1.1 + 3.2.0 From 0b7981d13457dc810c9ab8d54b5cb856b5e1f58e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:44:55 +0000 Subject: [PATCH 03/78] Bump the java-production-dependencies group with 3 updates (#58) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 02b2010..615417d 100644 --- a/pom.xml +++ b/pom.xml @@ -37,8 +37,8 @@ 17 - 1.3.0 - 2.0.11 + 1.3.1 + 2.0.12 2.16.1 From afe160b8bb00a270c153e014ffac2c202325015a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:23:00 +0000 Subject: [PATCH 04/78] Bump the github-actions group with 1 update (#59) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1dd8715..7519b13 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: run: mvn -B clean test - name: Codesign DLL on release if: startsWith(github.ref, 'refs/tags/') - uses: skymatic/code-sign-action@v2 + uses: skymatic/code-sign-action@v3 with: certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }} password: ${{ secrets.WIN_CODESIGN_P12_PW }} From 2fc55311618912fee6d211b197d868a0a73f7219 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:14:31 +0000 Subject: [PATCH 05/78] Bump the java-production-dependencies group with 2 updates (#62) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 615417d..fe02809 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 1.3.1 2.0.12 - 2.16.1 + 2.17.0 5.10.2 From d215a89c12fcec2dcede92db5afddb87ae801ac0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:14:54 +0000 Subject: [PATCH 06/78] Bump the java-test-dependencies group with 1 update (#60) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fe02809..5c07243 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ 5.10.2 - 5.10.0 + 5.11.0 9.0.9 From bb2a48310ae748a51f356dd5551a5967f8e1ad29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:17:09 +0000 Subject: [PATCH 07/78] Bump the maven-build-plugins group with 3 updates (#61) --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5c07243..e902ce8 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ 5.11.0 - 9.0.9 + 9.1.0 1.6.13 @@ -122,7 +122,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.13.0 -h @@ -311,7 +311,7 @@ maven-gpg-plugin - 3.1.0 + 3.2.2 sign-artifacts From 2bd905a5df9d29b84188a4796acc0d60305ab44f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:28:17 +0000 Subject: [PATCH 08/78] Bump the github-actions group with 1 update (#63) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7519b13..bd6f60c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: name: artifacts path: target/*.jar - name: Create Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: prerelease: true From 5c8d1594075bef49588c9484dd9feae3b1f48d37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 09:29:24 +0000 Subject: [PATCH 09/78] Bump the java-production-dependencies group across 1 directory with 4 updates (#67) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e902ce8..46cd3bd 100644 --- a/pom.xml +++ b/pom.xml @@ -38,8 +38,8 @@ 1.3.1 - 2.0.12 - 2.17.0 + 2.0.13 + 2.17.1 5.10.2 From 9dca255d021c8c608ed91cbe4eb2b5e42df8874f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 09:30:24 +0000 Subject: [PATCH 10/78] Bump the maven-build-plugins group across 1 directory with 8 updates (#68) --- pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 46cd3bd..36fd446 100644 --- a/pom.xml +++ b/pom.xml @@ -46,8 +46,8 @@ 5.11.0 - 9.1.0 - 1.6.13 + 9.2.0 + 1.7.0 @@ -134,7 +134,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.4.1 + 3.5.0 check-preconditions @@ -184,7 +184,7 @@ maven-source-plugin - 3.3.0 + 3.3.1 attach-sources @@ -196,7 +196,7 @@ maven-javadoc-plugin - 3.6.3 + 3.7.0 attach-javadocs @@ -281,7 +281,7 @@ org.codehaus.mojo exec-maven-plugin - 3.2.0 + 3.3.0 @@ -311,7 +311,7 @@ maven-gpg-plugin - 3.2.2 + 3.2.4 sign-artifacts @@ -372,7 +372,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.1 + 3.1.2 From 7cb6895d254a2aeef8d9c78edacb909501463dbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 09:31:22 +0000 Subject: [PATCH 11/78] Bump org.mockito:mockito-core in the java-test-dependencies group (#66) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36fd446..c08c17d 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ 5.10.2 - 5.11.0 + 5.12.0 9.2.0 From f2920a38bf9a49d13089cd55ef61786b7b2d2d5e Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Fri, 7 Jun 2024 14:11:04 +0000 Subject: [PATCH 12/78] Hardening the CI in relation to PRs --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd6f60c..a85c28c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,8 @@ name: Build on: - [push] + push: + pull_request_target: + types: [labeled] defaults: run: @@ -47,4 +49,4 @@ jobs: with: prerelease: true token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} - generate_release_notes: true \ No newline at end of file + generate_release_notes: true From 27ee602d6bc9757bac10a7aa74bc9f2722bd0aac Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 12 Jun 2024 15:57:06 +0200 Subject: [PATCH 13/78] fix bug in pom jdk version specified in property not used in compile phase --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c08c17d..95937a0 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ -h ${project.basedir}/src/main/headers - 17 + ${project.jdk.version} From 1d43fc8937cb093e8f3569eee30a8cf416e25615 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 12 Jun 2024 15:57:50 +0200 Subject: [PATCH 14/78] prepare project to use ffi api in JDK 22 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 95937a0..789766f 100644 --- a/pom.xml +++ b/pom.xml @@ -34,10 +34,10 @@ UTF-8 - 17 + 22 - 1.3.1 + 1.4.0-sidebar 2.0.13 2.17.1 From 4c9a6c81851a2cd2368089f2a0b9460eda51b0ee Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 12 Jun 2024 15:58:37 +0200 Subject: [PATCH 15/78] add required windows c api classes generated with jextract --- .../windows/capi/common/Windows_h.java | 203 +++++++++ .../windows/capi/ktmw32/ktmw32_ex_h.java | 254 +++++++++++ .../windows/capi/winreg/winreg_h.java | 398 ++++++++++++++++++ 3 files changed, 855 insertions(+) create mode 100644 src/main/java/org/cryptomator/windows/capi/common/Windows_h.java create mode 100644 src/main/java/org/cryptomator/windows/capi/ktmw32/ktmw32_ex_h.java create mode 100644 src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java diff --git a/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java new file mode 100644 index 0000000..91cca57 --- /dev/null +++ b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java @@ -0,0 +1,203 @@ +// Generated by jextract + +package org.cryptomator.windows.capi.common; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class Windows_h { + + Windows_h() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MemorySegment findOrThrow(String symbol) { + return SYMBOL_LOOKUP.find(symbol) + .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: " + symbol)); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("Kernel32"), LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); + public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT; + public static final ValueLayout.OfDouble C_LONG_DOUBLE = ValueLayout.JAVA_DOUBLE; + /** + * {@snippet lang=c : + * typedef void *HANDLE + * } + */ + public static final AddressLayout HANDLE = Windows_h.C_POINTER; + + private static class CloseHandle { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + Windows_h.C_INT, + Windows_h.C_POINTER + ); + + public static final MemorySegment ADDR = Windows_h.findOrThrow("CloseHandle"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * BOOL CloseHandle(HANDLE hObject) + * } + */ + public static FunctionDescriptor CloseHandle$descriptor() { + return CloseHandle.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * BOOL CloseHandle(HANDLE hObject) + * } + */ + public static MethodHandle CloseHandle$handle() { + return CloseHandle.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * BOOL CloseHandle(HANDLE hObject) + * } + */ + public static MemorySegment CloseHandle$address() { + return CloseHandle.ADDR; + } + + /** + * {@snippet lang=c : + * BOOL CloseHandle(HANDLE hObject) + * } + */ + public static int CloseHandle(MemorySegment hObject) { + var mh$ = CloseHandle.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CloseHandle", hObject); + } + return (int)mh$.invokeExact(hObject); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class GetLastError { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + Windows_h.C_LONG ); + + public static final MemorySegment ADDR = Windows_h.findOrThrow("GetLastError"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * DWORD GetLastError() + * } + */ + public static FunctionDescriptor GetLastError$descriptor() { + return GetLastError.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * DWORD GetLastError() + * } + */ + public static MethodHandle GetLastError$handle() { + return GetLastError.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * DWORD GetLastError() + * } + */ + public static MemorySegment GetLastError$address() { + return GetLastError.ADDR; + } + + /** + * {@snippet lang=c : + * DWORD GetLastError() + * } + */ + public static int GetLastError() { + var mh$ = GetLastError.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("GetLastError"); + } + return (int)mh$.invokeExact(); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + private static final MemorySegment INVALID_HANDLE_VALUE = MemorySegment.ofAddress(-1L); + /** + * {@snippet lang=c : + * #define INVALID_HANDLE_VALUE (void*) -1 + * } + */ + public static MemorySegment INVALID_HANDLE_VALUE() { + return INVALID_HANDLE_VALUE; + } +} + diff --git a/src/main/java/org/cryptomator/windows/capi/ktmw32/ktmw32_ex_h.java b/src/main/java/org/cryptomator/windows/capi/ktmw32/ktmw32_ex_h.java new file mode 100644 index 0000000..14cb1f3 --- /dev/null +++ b/src/main/java/org/cryptomator/windows/capi/ktmw32/ktmw32_ex_h.java @@ -0,0 +1,254 @@ +// Generated by jextract + +package org.cryptomator.windows.capi.ktmw32; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class ktmw32_ex_h { + + ktmw32_ex_h() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MemorySegment findOrThrow(String symbol) { + return SYMBOL_LOOKUP.find(symbol) + .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: " + symbol)); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("KtmW32"), LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); + public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT; + public static final ValueLayout.OfDouble C_LONG_DOUBLE = ValueLayout.JAVA_DOUBLE; + + private static class CreateTransaction { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + ktmw32_ex_h.C_POINTER, + ktmw32_ex_h.C_POINTER, + ktmw32_ex_h.C_POINTER, + ktmw32_ex_h.C_LONG, + ktmw32_ex_h.C_LONG, + ktmw32_ex_h.C_LONG, + ktmw32_ex_h.C_LONG, + ktmw32_ex_h.C_POINTER + ); + + public static final MemorySegment ADDR = ktmw32_ex_h.findOrThrow("CreateTransaction"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * HANDLE CreateTransaction(LPSECURITY_ATTRIBUTES lpTransactionAttributes, LPGUID UOW, DWORD CreateOptions, DWORD IsolationLevel, DWORD IsolationFlags, DWORD Timeout, LPWSTR Description) + * } + */ + public static FunctionDescriptor CreateTransaction$descriptor() { + return CreateTransaction.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * HANDLE CreateTransaction(LPSECURITY_ATTRIBUTES lpTransactionAttributes, LPGUID UOW, DWORD CreateOptions, DWORD IsolationLevel, DWORD IsolationFlags, DWORD Timeout, LPWSTR Description) + * } + */ + public static MethodHandle CreateTransaction$handle() { + return CreateTransaction.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * HANDLE CreateTransaction(LPSECURITY_ATTRIBUTES lpTransactionAttributes, LPGUID UOW, DWORD CreateOptions, DWORD IsolationLevel, DWORD IsolationFlags, DWORD Timeout, LPWSTR Description) + * } + */ + public static MemorySegment CreateTransaction$address() { + return CreateTransaction.ADDR; + } + + /** + * {@snippet lang=c : + * HANDLE CreateTransaction(LPSECURITY_ATTRIBUTES lpTransactionAttributes, LPGUID UOW, DWORD CreateOptions, DWORD IsolationLevel, DWORD IsolationFlags, DWORD Timeout, LPWSTR Description) + * } + */ + public static MemorySegment CreateTransaction(MemorySegment lpTransactionAttributes, MemorySegment UOW, int CreateOptions, int IsolationLevel, int IsolationFlags, int Timeout, MemorySegment Description) { + var mh$ = CreateTransaction.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CreateTransaction", lpTransactionAttributes, UOW, CreateOptions, IsolationLevel, IsolationFlags, Timeout, Description); + } + return (MemorySegment)mh$.invokeExact(lpTransactionAttributes, UOW, CreateOptions, IsolationLevel, IsolationFlags, Timeout, Description); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class CommitTransaction { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + ktmw32_ex_h.C_INT, + ktmw32_ex_h.C_POINTER + ); + + public static final MemorySegment ADDR = ktmw32_ex_h.findOrThrow("CommitTransaction"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * BOOL CommitTransaction(HANDLE TransactionHandle) + * } + */ + public static FunctionDescriptor CommitTransaction$descriptor() { + return CommitTransaction.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * BOOL CommitTransaction(HANDLE TransactionHandle) + * } + */ + public static MethodHandle CommitTransaction$handle() { + return CommitTransaction.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * BOOL CommitTransaction(HANDLE TransactionHandle) + * } + */ + public static MemorySegment CommitTransaction$address() { + return CommitTransaction.ADDR; + } + + /** + * {@snippet lang=c : + * BOOL CommitTransaction(HANDLE TransactionHandle) + * } + */ + public static int CommitTransaction(MemorySegment TransactionHandle) { + var mh$ = CommitTransaction.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CommitTransaction", TransactionHandle); + } + return (int)mh$.invokeExact(TransactionHandle); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class RollbackTransaction { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + ktmw32_ex_h.C_INT, + ktmw32_ex_h.C_POINTER + ); + + public static final MemorySegment ADDR = ktmw32_ex_h.findOrThrow("RollbackTransaction"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * BOOL RollbackTransaction(HANDLE TransactionHandle) + * } + */ + public static FunctionDescriptor RollbackTransaction$descriptor() { + return RollbackTransaction.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * BOOL RollbackTransaction(HANDLE TransactionHandle) + * } + */ + public static MethodHandle RollbackTransaction$handle() { + return RollbackTransaction.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * BOOL RollbackTransaction(HANDLE TransactionHandle) + * } + */ + public static MemorySegment RollbackTransaction$address() { + return RollbackTransaction.ADDR; + } + + /** + * {@snippet lang=c : + * BOOL RollbackTransaction(HANDLE TransactionHandle) + * } + */ + public static int RollbackTransaction(MemorySegment TransactionHandle) { + var mh$ = RollbackTransaction.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RollbackTransaction", TransactionHandle); + } + return (int)mh$.invokeExact(TransactionHandle); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } +} + diff --git a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java new file mode 100644 index 0000000..6daf186 --- /dev/null +++ b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java @@ -0,0 +1,398 @@ +// Generated by jextract + +package org.cryptomator.windows.capi.winreg; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class winreg_h { + + winreg_h() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MemorySegment findOrThrow(String symbol) { + return SYMBOL_LOOKUP.find(symbol) + .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: " + symbol)); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("Advapi32"), LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); + public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT; + public static final ValueLayout.OfDouble C_LONG_DOUBLE = ValueLayout.JAVA_DOUBLE; + + private static class RegCloseKey { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegCloseKey"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegCloseKey(HKEY hKey) + * } + */ + public static FunctionDescriptor RegCloseKey$descriptor() { + return RegCloseKey.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegCloseKey(HKEY hKey) + * } + */ + public static MethodHandle RegCloseKey$handle() { + return RegCloseKey.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegCloseKey(HKEY hKey) + * } + */ + public static MemorySegment RegCloseKey$address() { + return RegCloseKey.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegCloseKey(HKEY hKey) + * } + */ + public static int RegCloseKey(MemorySegment hKey) { + var mh$ = RegCloseKey.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegCloseKey", hKey); + } + return (int)mh$.invokeExact(hKey); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class RegCreateKeyTransactedW { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_LONG, + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegCreateKeyTransactedW"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegCreateKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, REGSAM samDesired, const LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static FunctionDescriptor RegCreateKeyTransactedW$descriptor() { + return RegCreateKeyTransactedW.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegCreateKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, REGSAM samDesired, const LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static MethodHandle RegCreateKeyTransactedW$handle() { + return RegCreateKeyTransactedW.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegCreateKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, REGSAM samDesired, const LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static MemorySegment RegCreateKeyTransactedW$address() { + return RegCreateKeyTransactedW.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegCreateKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, REGSAM samDesired, const LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static int RegCreateKeyTransactedW(MemorySegment hKey, MemorySegment lpSubKey, int Reserved, MemorySegment lpClass, int dwOptions, int samDesired, MemorySegment lpSecurityAttributes, MemorySegment phkResult, MemorySegment lpdwDisposition, MemorySegment hTransaction, MemorySegment pExtendedParemeter) { + var mh$ = RegCreateKeyTransactedW.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegCreateKeyTransactedW", hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition, hTransaction, pExtendedParemeter); + } + return (int)mh$.invokeExact(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition, hTransaction, pExtendedParemeter); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class RegSetValueExW { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_LONG, + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_LONG + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegSetValueExW"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * } + */ + public static FunctionDescriptor RegSetValueExW$descriptor() { + return RegSetValueExW.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * } + */ + public static MethodHandle RegSetValueExW$handle() { + return RegSetValueExW.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * } + */ + public static MemorySegment RegSetValueExW$address() { + return RegSetValueExW.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * } + */ + public static int RegSetValueExW(MemorySegment hKey, MemorySegment lpValueName, int Reserved, int dwType, MemorySegment lpData, int cbData) { + var mh$ = RegSetValueExW.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegSetValueExW", hKey, lpValueName, Reserved, dwType, lpData, cbData); + } + return (int)mh$.invokeExact(hKey, lpValueName, Reserved, dwType, lpData, cbData); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + private static final int KEY_READ = (int)131097L; + /** + * {@snippet lang=c : + * #define KEY_READ 131097 + * } + */ + public static int KEY_READ() { + return KEY_READ; + } + private static final int KEY_WRITE = (int)131078L; + /** + * {@snippet lang=c : + * #define KEY_WRITE 131078 + * } + */ + public static int KEY_WRITE() { + return KEY_WRITE; + } + private static final int REG_OPTION_NON_VOLATILE = (int)0L; + /** + * {@snippet lang=c : + * #define REG_OPTION_NON_VOLATILE 0 + * } + */ + public static int REG_OPTION_NON_VOLATILE() { + return REG_OPTION_NON_VOLATILE; + } + private static final int REG_OPTION_VOLATILE = (int)1L; + /** + * {@snippet lang=c : + * #define REG_OPTION_VOLATILE 1 + * } + */ + public static int REG_OPTION_VOLATILE() { + return REG_OPTION_VOLATILE; + } + private static final int REG_NONE = (int)0L; + /** + * {@snippet lang=c : + * #define REG_NONE 0 + * } + */ + public static int REG_NONE() { + return REG_NONE; + } + private static final int REG_SZ = (int)1L; + /** + * {@snippet lang=c : + * #define REG_SZ 1 + * } + */ + public static int REG_SZ() { + return REG_SZ; + } + private static final int REG_EXPAND_SZ = (int)2L; + /** + * {@snippet lang=c : + * #define REG_EXPAND_SZ 2 + * } + */ + public static int REG_EXPAND_SZ() { + return REG_EXPAND_SZ; + } + private static final int REG_BINARY = (int)3L; + /** + * {@snippet lang=c : + * #define REG_BINARY 3 + * } + */ + public static int REG_BINARY() { + return REG_BINARY; + } + private static final int REG_DWORD = (int)4L; + /** + * {@snippet lang=c : + * #define REG_DWORD 4 + * } + */ + public static int REG_DWORD() { + return REG_DWORD; + } + private static final int REG_MULTI_SZ = (int)7L; + /** + * {@snippet lang=c : + * #define REG_MULTI_SZ 7 + * } + */ + public static int REG_MULTI_SZ() { + return REG_MULTI_SZ; + } + private static final int REG_QWORD = (int)11L; + /** + * {@snippet lang=c : + * #define REG_QWORD 11 + * } + */ + public static int REG_QWORD() { + return REG_QWORD; + } + private static final MemorySegment HKEY_CLASSES_ROOT = MemorySegment.ofAddress(-2147483648L); + /** + * {@snippet lang=c : + * #define HKEY_CLASSES_ROOT (void*) -2147483648 + * } + */ + public static MemorySegment HKEY_CLASSES_ROOT() { + return HKEY_CLASSES_ROOT; + } + private static final MemorySegment HKEY_CURRENT_USER = MemorySegment.ofAddress(-2147483647L); + /** + * {@snippet lang=c : + * #define HKEY_CURRENT_USER (void*) -2147483647 + * } + */ + public static MemorySegment HKEY_CURRENT_USER() { + return HKEY_CURRENT_USER; + } + private static final MemorySegment HKEY_LOCAL_MACHINE = MemorySegment.ofAddress(-2147483646L); + /** + * {@snippet lang=c : + * #define HKEY_LOCAL_MACHINE (void*) -2147483646 + * } + */ + public static MemorySegment HKEY_LOCAL_MACHINE() { + return HKEY_LOCAL_MACHINE; + } + private static final MemorySegment HKEY_USERS = MemorySegment.ofAddress(-2147483645L); + /** + * {@snippet lang=c : + * #define HKEY_USERS (void*) -2147483645 + * } + */ + public static MemorySegment HKEY_USERS() { + return HKEY_USERS; + } +} + From ed0ac5867a12e3fb2c270a5dc2a68957d4a30312 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 13 Jun 2024 17:15:35 +0200 Subject: [PATCH 16/78] setting base for accessing registry --- .../windows/capi/common/Windows_h.java | 18 ++ .../windows/capi/winreg/winreg_h.java | 282 +++++++++++++++++- .../windows/common/WindowsRegistry.java | 251 ++++++++++++++++ .../windows/common/WindowsRegistryIT.java | 153 ++++++++++ 4 files changed, 688 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/cryptomator/windows/common/WindowsRegistry.java create mode 100644 src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java diff --git a/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java index 91cca57..c4b9c0d 100644 --- a/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java +++ b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java @@ -199,5 +199,23 @@ public static int GetLastError() { public static MemorySegment INVALID_HANDLE_VALUE() { return INVALID_HANDLE_VALUE; } + private static final int ERROR_SUCCESS = (int)0L; + /** + * {@snippet lang=c : + * #define ERROR_SUCCESS 0 + * } + */ + public static int ERROR_SUCCESS() { + return ERROR_SUCCESS; + } + private static final int ERROR_MORE_DATA = (int)234L; + /** + * {@snippet lang=c : + * #define ERROR_MORE_DATA 234 + * } + */ + public static int ERROR_MORE_DATA() { + return ERROR_MORE_DATA; + } } diff --git a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java index 6daf186..9522b6c 100644 --- a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java +++ b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java @@ -197,7 +197,7 @@ public static int RegCreateKeyTransactedW(MemorySegment hKey, MemorySegment lpSu } } - private static class RegSetValueExW { + private static class RegDeleteKeyTransactedW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( winreg_h.C_LONG, winreg_h.C_POINTER, @@ -205,10 +205,260 @@ private static class RegSetValueExW { winreg_h.C_LONG, winreg_h.C_LONG, winreg_h.C_POINTER, + winreg_h.C_POINTER + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegDeleteKeyTransactedW"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegDeleteKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved, HANDLE hTransaction, PVOID pExtendedParameter) + * } + */ + public static FunctionDescriptor RegDeleteKeyTransactedW$descriptor() { + return RegDeleteKeyTransactedW.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegDeleteKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved, HANDLE hTransaction, PVOID pExtendedParameter) + * } + */ + public static MethodHandle RegDeleteKeyTransactedW$handle() { + return RegDeleteKeyTransactedW.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegDeleteKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved, HANDLE hTransaction, PVOID pExtendedParameter) + * } + */ + public static MemorySegment RegDeleteKeyTransactedW$address() { + return RegDeleteKeyTransactedW.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegDeleteKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved, HANDLE hTransaction, PVOID pExtendedParameter) + * } + */ + public static int RegDeleteKeyTransactedW(MemorySegment hKey, MemorySegment lpSubKey, int samDesired, int Reserved, MemorySegment hTransaction, MemorySegment pExtendedParameter) { + var mh$ = RegDeleteKeyTransactedW.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegDeleteKeyTransactedW", hKey, lpSubKey, samDesired, Reserved, hTransaction, pExtendedParameter); + } + return (int)mh$.invokeExact(hKey, lpSubKey, samDesired, Reserved, hTransaction, pExtendedParameter); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class RegOpenKeyTransactedW { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_LONG, + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegOpenKeyTransactedW"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegOpenKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static FunctionDescriptor RegOpenKeyTransactedW$descriptor() { + return RegOpenKeyTransactedW.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegOpenKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static MethodHandle RegOpenKeyTransactedW$handle() { + return RegOpenKeyTransactedW.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegOpenKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static MemorySegment RegOpenKeyTransactedW$address() { + return RegOpenKeyTransactedW.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegOpenKeyTransactedW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult, HANDLE hTransaction, PVOID pExtendedParemeter) + * } + */ + public static int RegOpenKeyTransactedW(MemorySegment hKey, MemorySegment lpSubKey, int ulOptions, int samDesired, MemorySegment phkResult, MemorySegment hTransaction, MemorySegment pExtendedParemeter) { + var mh$ = RegOpenKeyTransactedW.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegOpenKeyTransactedW", hKey, lpSubKey, ulOptions, samDesired, phkResult, hTransaction, pExtendedParemeter); + } + return (int)mh$.invokeExact(hKey, lpSubKey, ulOptions, samDesired, phkResult, hTransaction, pExtendedParemeter); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class RegSetKeyValueW { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_LONG, + winreg_h.C_POINTER, winreg_h.C_LONG ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegSetValueExW"); + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegSetKeyValueW"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegSetKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName, DWORD dwType, LPCVOID lpData, DWORD cbData) + * } + */ + public static FunctionDescriptor RegSetKeyValueW$descriptor() { + return RegSetKeyValueW.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegSetKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName, DWORD dwType, LPCVOID lpData, DWORD cbData) + * } + */ + public static MethodHandle RegSetKeyValueW$handle() { + return RegSetKeyValueW.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegSetKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName, DWORD dwType, LPCVOID lpData, DWORD cbData) + * } + */ + public static MemorySegment RegSetKeyValueW$address() { + return RegSetKeyValueW.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegSetKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName, DWORD dwType, LPCVOID lpData, DWORD cbData) + * } + */ + public static int RegSetKeyValueW(MemorySegment hKey, MemorySegment lpSubKey, MemorySegment lpValueName, int dwType, MemorySegment lpData, int cbData) { + var mh$ = RegSetKeyValueW.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegSetKeyValueW", hKey, lpSubKey, lpValueName, dwType, lpData, cbData); + } + return (int)mh$.invokeExact(hKey, lpSubKey, lpValueName, dwType, lpData, cbData); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class RegDeleteTreeW { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegDeleteTreeW"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegDeleteTreeW(HKEY hKey, LPCWSTR lpSubKey) + * } + */ + public static FunctionDescriptor RegDeleteTreeW$descriptor() { + return RegDeleteTreeW.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegDeleteTreeW(HKEY hKey, LPCWSTR lpSubKey) + * } + */ + public static MethodHandle RegDeleteTreeW$handle() { + return RegDeleteTreeW.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegDeleteTreeW(HKEY hKey, LPCWSTR lpSubKey) + * } + */ + public static MemorySegment RegDeleteTreeW$address() { + return RegDeleteTreeW.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegDeleteTreeW(HKEY hKey, LPCWSTR lpSubKey) + * } + */ + public static int RegDeleteTreeW(MemorySegment hKey, MemorySegment lpSubKey) { + var mh$ = RegDeleteTreeW.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegDeleteTreeW", hKey, lpSubKey); + } + return (int)mh$.invokeExact(hKey, lpSubKey); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class RegGetValueW { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegGetValueW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -216,45 +466,45 @@ private static class RegSetValueExW { /** * Function descriptor for: * {@snippet lang=c : - * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * LSTATUS RegGetValueW(HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpValue, DWORD dwFlags, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) * } */ - public static FunctionDescriptor RegSetValueExW$descriptor() { - return RegSetValueExW.DESC; + public static FunctionDescriptor RegGetValueW$descriptor() { + return RegGetValueW.DESC; } /** * Downcall method handle for: * {@snippet lang=c : - * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * LSTATUS RegGetValueW(HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpValue, DWORD dwFlags, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) * } */ - public static MethodHandle RegSetValueExW$handle() { - return RegSetValueExW.HANDLE; + public static MethodHandle RegGetValueW$handle() { + return RegGetValueW.HANDLE; } /** * Address for: * {@snippet lang=c : - * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * LSTATUS RegGetValueW(HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpValue, DWORD dwFlags, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) * } */ - public static MemorySegment RegSetValueExW$address() { - return RegSetValueExW.ADDR; + public static MemorySegment RegGetValueW$address() { + return RegGetValueW.ADDR; } /** * {@snippet lang=c : - * LSTATUS RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) + * LSTATUS RegGetValueW(HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpValue, DWORD dwFlags, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) * } */ - public static int RegSetValueExW(MemorySegment hKey, MemorySegment lpValueName, int Reserved, int dwType, MemorySegment lpData, int cbData) { - var mh$ = RegSetValueExW.HANDLE; + public static int RegGetValueW(MemorySegment hkey, MemorySegment lpSubKey, MemorySegment lpValue, int dwFlags, MemorySegment pdwType, MemorySegment pvData, MemorySegment pcbData) { + var mh$ = RegGetValueW.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("RegSetValueExW", hKey, lpValueName, Reserved, dwType, lpData, cbData); + traceDowncall("RegGetValueW", hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData); } - return (int)mh$.invokeExact(hKey, lpValueName, Reserved, dwType, lpData, cbData); + return (int)mh$.invokeExact(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData); } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java new file mode 100644 index 0000000..926017e --- /dev/null +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -0,0 +1,251 @@ +package org.cryptomator.windows.common; + +import org.cryptomator.windows.capi.common.Windows_h; +import org.cryptomator.windows.capi.ktmw32.ktmw32_ex_h; +import org.cryptomator.windows.capi.winreg.winreg_h; + +import java.lang.foreign.AddressLayout; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static java.lang.foreign.MemorySegment.NULL; +import static org.cryptomator.windows.capi.common.Windows_h.ERROR_MORE_DATA; +import static org.cryptomator.windows.capi.common.Windows_h.ERROR_SUCCESS; +import static org.cryptomator.windows.capi.common.Windows_h.GetLastError; +import static org.cryptomator.windows.capi.common.Windows_h.INVALID_HANDLE_VALUE; +import static org.cryptomator.windows.capi.winreg.winreg_h.*; + +public class WindowsRegistry { + + public static final long MAX_DATA_SIZE = (1L << 32) - 1L; //unsinged integer + + public static RegistryTransaction startTransaction() { + var transactionHandle = ktmw32_ex_h.CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL); + if (transactionHandle.address() == INVALID_HANDLE_VALUE().address()) { + //GetLastError() + int error = Windows_h.GetLastError(); + //TODO: get error message directly? https://learn.microsoft.com/en-us/windows/win32/Debug/retrieving-the-last-error-code + throw new RuntimeException("Native code returned win32 error code " + error); + } + return new RegistryTransaction(transactionHandle); + } + + public static class RegistryTransaction implements AutoCloseable { + + private MemorySegment transactionHandle; + private volatile boolean isCommited = false; + private volatile boolean isClosed = false; + + RegistryTransaction(MemorySegment handle) { + this.transactionHandle = handle; + } + + public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolatile) { + var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); + try (var arena = Arena.ofConfined()) { + var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); + int result = winreg_h.RegCreateKeyTransactedW( + key.getHandle(), + lpSubkey, + 0, + NULL, + isVolatile? REG_OPTION_VOLATILE() : REG_OPTION_NON_VOLATILE(), + KEY_READ() | KEY_WRITE(), + NULL, + pointerToResultKey, + NULL, + transactionHandle, + NULL + ); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Creating Key failed with error code " + result); + } + return new RegistryKey(pointerToResultKey.get(C_POINTER,0), key.getPath() + "\\" + subkey); + } + } + + public RegistryKey openRegKey(RegistryKey key, String subkey) { + var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); + try (var arena = Arena.ofConfined()) { + var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); + int result = winreg_h.RegOpenKeyTransactedW( + key.getHandle(), + lpSubkey, + 0, + KEY_READ() | KEY_WRITE(), + pointerToResultKey, + transactionHandle, + NULL + ); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Opening key failed with error code " + result); + } + return new RegistryKey(pointerToResultKey.get(C_POINTER,0), key.getPath() + "\\" + subkey); + } + } + + public void deleteRegKey(RegistryKey key, String subkey) { + try (var arena = Arena.ofConfined()) { + var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); + int result = winreg_h.RegDeleteKeyTransactedW( + key.getHandle(), + lpSubkey, + 0x0100, //KEY_WOW64_64KEY + 0, + transactionHandle, + NULL + ); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Opening key failed with error code " + result); + } + } + } + + + public synchronized void commit() { + if(isClosed) { + throw new IllegalStateException("Transaction already closed"); + } + int result = ktmw32_ex_h.CommitTransaction(transactionHandle); + if (result == 0) { + int error = Windows_h.GetLastError(); + throw new RuntimeException("Native code returned win32 error code " + error); + } + isCommited = true; + closeInternal(); + } + + public synchronized void rollback() { + if(isClosed) { + throw new IllegalStateException("Transaction already closed"); + } + int result = ktmw32_ex_h.RollbackTransaction(transactionHandle); + if (result == 0) { + int error = Windows_h.GetLastError(); + throw new RuntimeException("Native code returned win32 error code " + error); + } + closeInternal(); + } + + ; + + @Override + public synchronized void close() throws RuntimeException { + if(!isCommited) { + try { + rollback(); + } catch (RuntimeException e) { + System.err.printf("Failed to rollback uncommited transaction on close. Exception message: %s%n", e.getMessage()); + } + } + closeInternal(); + } + + private synchronized void closeInternal() { + if (!isClosed) { + int result = Windows_h.CloseHandle(transactionHandle); + if (result == 0) { + int error = Windows_h.GetLastError(); + throw new RuntimeException("Native code returned win32 error code " + error); + } + transactionHandle = null; + isClosed = true; + } + } + } + + public static class RegistryKey implements AutoCloseable { + + public static final RegistryKey HKEY_CURRENT_USER = new RegistryKey(winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); + public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryKey(winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); + public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryKey(winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); + public static final RegistryKey HKEY_USERS = new RegistryKey(winreg_h.HKEY_USERS(), "HKEY_USERS"); + + protected final String path; + private MemorySegment handle; + private volatile boolean isClosed = false; + + RegistryKey(MemorySegment handle, String path) { + this.handle = handle; + this.path = path; + } + + public void setStringValue(String name, String data) throws RuntimeException { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); + if (lpValueData.byteSize() > MAX_DATA_SIZE) { + throw new IllegalArgumentException("Data encoded as UTF 16 LE must be smaller than " + MAX_DATA_SIZE + "bytes."); + } + int result = winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, winreg_h.REG_SZ(), lpValueData, (int) lpValueData.byteSize()); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(name, path, result)); + } + } + } + + public String getStringValue(String name) throws RuntimeException { + return getStringValue(name, 256); + } + + private String getStringValue(String name, int bufferSize) throws RuntimeException { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var lpData = Arena.ofAuto().allocate(bufferSize); + var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, bufferSize); + + //dwFlag is set to RRF_RT_REG_SZ + int result = winreg_h.RegGetValueW(handle, NULL, lpValueName, 0x00000002, NULL, lpData, lpDataSize); + if (result == ERROR_MORE_DATA()) { + getStringValue(name,lpDataSize.get(ValueLayout.JAVA_INT,0)); //TODO: we are allocating and allocating and allocating memorey due to the recursion! + } else if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Getting value %s for key %s failed with error code %d".formatted(name, path, result)); + } + return lpData.getString(0,StandardCharsets.UTF_16LE); + } + } + + public void deleteSubtree(String subkey) { + if(subkey == null || subkey.isBlank()) { + throw new IllegalArgumentException("Subkey must not be empty"); + } + deleteValuesAndSubtree(subkey); + } + + public void deleteValuesAndSubtree(String subkey) { + try (var arena = Arena.ofConfined()) { + var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); + int result = winreg_h.RegDeleteTreeW(handle, lpSubkey); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Deleting Key failed with error code " + result); + } + } + } + + @Override + public synchronized void close() { + if (!isClosed) { + int result = winreg_h.RegCloseKey(handle); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Closing key %s failed with error code %d.".formatted(path,result)); + } + handle = NULL; + isClosed = true; + } + } + + MemorySegment getHandle() { + return handle; + } + + public String getPath() { + return path; + } + } + + + +} \ No newline at end of file diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java new file mode 100644 index 0000000..e8b6143 --- /dev/null +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -0,0 +1,153 @@ +package org.cryptomator.windows.common; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class WindowsRegistryIT { + + @Test + @DisplayName("Open not exisitig key fails") + @Order(1) + public void testOpenNotExisting() { + Assertions.assertThrows(RuntimeException.class, () -> { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist")) { + + } + }); + } + + @Test + @DisplayName("Create and no commit leads to rollback") + @Order(1) + public void testCreateNotExistingRollback() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { + } + + Assertions.assertThrows(RuntimeException.class, () -> { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + } + }); + } + + @Test + @DisplayName("Creating, commit, open succeeds") + @Order(2) + public void testCreateNotExistingCommit() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { + t.commit(); + } + + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + } + } + + @Test + @DisplayName("Open, setValue, rollback") + @Order(3) + public void testOpenSetValueRollback() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.setStringValue("itTest", "In Progress"); + } + + Assertions.assertThrows(RuntimeException.class, () -> { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.getStringValue("itTest"); + } + }); + } + + @Test + @DisplayName("Open, setValue, commit") + @Order(4) + public void testOpenSetValueCommit() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.setStringValue("itTest", "In Progress"); + t.commit(); + } + + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.getStringValue("itTest"); + } + } + + @Test + @DisplayName("Open, deleteTree, rollback") + @Order(5) + public void testOpenDeleteTreeRollback() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.deleteValuesAndSubtree(""); + t.rollback(); + } + + Assertions.assertDoesNotThrow(() -> { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.getStringValue("itTest"); + } + }); + } + + @Test + @DisplayName("Open, deleteValuesAndSubtrees, commit") + @Order(6) + public void testOpenDeleteTreeCommit() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); + var subk = t.createRegKey(k, "subkey", true)) { + k.deleteValuesAndSubtree(""); + t.commit(); + } + + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + + Assertions.assertThrows(RuntimeException.class, () -> t.openRegKey(k, "subkey")); + Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("itTest")); + } + } + + @Test + @DisplayName("Delete, rollback") + @Order(7) + public void testDeleteRollback() { + try (var t = WindowsRegistry.startTransaction()) { + t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); + t.rollback(); + } + + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + } + } + + @Test + @DisplayName("Delete, commit") + @Order(8) + public void testDeleteCommit() { + try (var t = WindowsRegistry.startTransaction()) { + t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); + t.commit(); + } + + Assertions.assertThrows(RuntimeException.class, () -> { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + } + }); + } +} From 37516f6ffa46ffdcf9ca5801e05d762e9e3eee43 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 13 Jun 2024 22:16:54 +0200 Subject: [PATCH 17/78] fix buffer allocation problem --- .../windows/common/WindowsRegistry.java | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 926017e..bfe737e 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -188,26 +188,41 @@ public void setStringValue(String name, String data) throws RuntimeException { } public String getStringValue(String name) throws RuntimeException { - return getStringValue(name, 256); + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + return getStringValue(lpValueName, 256L); + } } - private String getStringValue(String name, int bufferSize) throws RuntimeException { + private String getStringValue(MemorySegment lpValueName, long bufferSize) throws RuntimeException { try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var lpData = Arena.ofAuto().allocate(bufferSize); - var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, bufferSize); - - //dwFlag is set to RRF_RT_REG_SZ - int result = winreg_h.RegGetValueW(handle, NULL, lpValueName, 0x00000002, NULL, lpData, lpDataSize); - if (result == ERROR_MORE_DATA()) { - getStringValue(name,lpDataSize.get(ValueLayout.JAVA_INT,0)); //TODO: we are allocating and allocating and allocating memorey due to the recursion! - } else if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Getting value %s for key %s failed with error code %d".formatted(name, path, result)); + var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, (int) bufferSize); + + try (var dataArena = Arena.ofConfined()) { + var lpData = dataArena.allocate(bufferSize); + + int result = winreg_h.RegGetValueW(handle, NULL, lpValueName, RRF_RT_REG_SZ(), NULL, lpData, lpDataSize); + if (result == ERROR_MORE_DATA()) { + throw new BufferTooSmallException(); + } else if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Getting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); + } + return lpData.getString(0, StandardCharsets.UTF_16LE); + + } catch (BufferTooSmallException _) { + if (bufferSize <= MAX_DATA_SIZE) { + return getStringValue(lpValueName, (bufferSize << 1) - 1); + } else { + throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size reached".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path)); + } } - return lpData.getString(0,StandardCharsets.UTF_16LE); } } + private static class BufferTooSmallException extends RuntimeException { + + } + public void deleteSubtree(String subkey) { if(subkey == null || subkey.isBlank()) { throw new IllegalArgumentException("Subkey must not be empty"); From 7d1c9fb4a756190a989b2626eefb667b11e73564 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 13 Jun 2024 22:19:41 +0200 Subject: [PATCH 18/78] remove explicit rollback call in integration test --- .../java/org/cryptomator/windows/common/WindowsRegistryIT.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index e8b6143..ee89db4 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -91,7 +91,6 @@ public void testOpenDeleteTreeRollback() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.deleteValuesAndSubtree(""); - t.rollback(); } Assertions.assertDoesNotThrow(() -> { @@ -127,7 +126,6 @@ public void testOpenDeleteTreeCommit() { public void testDeleteRollback() { try (var t = WindowsRegistry.startTransaction()) { t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); - t.rollback(); } try (var t = WindowsRegistry.startTransaction(); From cbbed9fba338fed8e9da8f2b3a991ed2767431a3 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 13 Jun 2024 22:20:15 +0200 Subject: [PATCH 19/78] format code --- .../windows/common/WindowsRegistry.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index bfe737e..0f3651d 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -9,12 +9,10 @@ import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.charset.StandardCharsets; -import java.util.Objects; import static java.lang.foreign.MemorySegment.NULL; import static org.cryptomator.windows.capi.common.Windows_h.ERROR_MORE_DATA; import static org.cryptomator.windows.capi.common.Windows_h.ERROR_SUCCESS; -import static org.cryptomator.windows.capi.common.Windows_h.GetLastError; import static org.cryptomator.windows.capi.common.Windows_h.INVALID_HANDLE_VALUE; import static org.cryptomator.windows.capi.winreg.winreg_h.*; @@ -52,7 +50,7 @@ public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolati lpSubkey, 0, NULL, - isVolatile? REG_OPTION_VOLATILE() : REG_OPTION_NON_VOLATILE(), + isVolatile ? REG_OPTION_VOLATILE() : REG_OPTION_NON_VOLATILE(), KEY_READ() | KEY_WRITE(), NULL, pointerToResultKey, @@ -63,7 +61,7 @@ isVolatile? REG_OPTION_VOLATILE() : REG_OPTION_NON_VOLATILE(), if (result != ERROR_SUCCESS()) { throw new RuntimeException("Creating Key failed with error code " + result); } - return new RegistryKey(pointerToResultKey.get(C_POINTER,0), key.getPath() + "\\" + subkey); + return new RegistryKey(pointerToResultKey.get(C_POINTER, 0), key.getPath() + "\\" + subkey); } } @@ -83,7 +81,7 @@ public RegistryKey openRegKey(RegistryKey key, String subkey) { if (result != ERROR_SUCCESS()) { throw new RuntimeException("Opening key failed with error code " + result); } - return new RegistryKey(pointerToResultKey.get(C_POINTER,0), key.getPath() + "\\" + subkey); + return new RegistryKey(pointerToResultKey.get(C_POINTER, 0), key.getPath() + "\\" + subkey); } } @@ -106,7 +104,7 @@ public void deleteRegKey(RegistryKey key, String subkey) { public synchronized void commit() { - if(isClosed) { + if (isClosed) { throw new IllegalStateException("Transaction already closed"); } int result = ktmw32_ex_h.CommitTransaction(transactionHandle); @@ -119,7 +117,7 @@ public synchronized void commit() { } public synchronized void rollback() { - if(isClosed) { + if (isClosed) { throw new IllegalStateException("Transaction already closed"); } int result = ktmw32_ex_h.RollbackTransaction(transactionHandle); @@ -134,7 +132,7 @@ public synchronized void rollback() { @Override public synchronized void close() throws RuntimeException { - if(!isCommited) { + if (!isCommited) { try { rollback(); } catch (RuntimeException e) { @@ -224,7 +222,7 @@ private static class BufferTooSmallException extends RuntimeException { } public void deleteSubtree(String subkey) { - if(subkey == null || subkey.isBlank()) { + if (subkey == null || subkey.isBlank()) { throw new IllegalArgumentException("Subkey must not be empty"); } deleteValuesAndSubtree(subkey); @@ -245,7 +243,7 @@ public synchronized void close() { if (!isClosed) { int result = winreg_h.RegCloseKey(handle); if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Closing key %s failed with error code %d.".formatted(path,result)); + throw new RuntimeException("Closing key %s failed with error code %d.".formatted(path, result)); } handle = NULL; isClosed = true; @@ -262,5 +260,4 @@ public String getPath() { } - } \ No newline at end of file From 654f4e92ba52ca4f75e2297627205605b24b174a Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 13 Jun 2024 22:54:01 +0200 Subject: [PATCH 20/78] add missing assertion to integration test --- .../java/org/cryptomator/windows/common/WindowsRegistryIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index ee89db4..5361984 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -80,7 +80,8 @@ public void testOpenSetValueCommit() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.getStringValue("itTest"); + var data = k.getStringValue("itTest"); + Assertions.assertEquals("In Progress", data); } } From 8254eb12cd5adb3c4a0c6653416ddf9834c53dc7 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 13 Jun 2024 22:55:16 +0200 Subject: [PATCH 21/78] generalize getValue method --- .../windows/common/WindowsRegistry.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 0f3651d..6f89be0 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -188,30 +188,35 @@ public void setStringValue(String name, String data) throws RuntimeException { public String getStringValue(String name) throws RuntimeException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - return getStringValue(lpValueName, 256L); + var data = getValue(lpValueName, RRF_RT_REG_SZ(), 256L); + return data.getString(0, StandardCharsets.UTF_16LE); } } - private String getStringValue(MemorySegment lpValueName, long bufferSize) throws RuntimeException { + private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed) throws RuntimeException { + long bufferSize = seed - 1; try (var arena = Arena.ofConfined()) { var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, (int) bufferSize); try (var dataArena = Arena.ofConfined()) { var lpData = dataArena.allocate(bufferSize); - int result = winreg_h.RegGetValueW(handle, NULL, lpValueName, RRF_RT_REG_SZ(), NULL, lpData, lpDataSize); + int result = winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); if (result == ERROR_MORE_DATA()) { throw new BufferTooSmallException(); } else if (result != ERROR_SUCCESS()) { throw new RuntimeException("Getting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); } - return lpData.getString(0, StandardCharsets.UTF_16LE); + + var returnBuffer = Arena.ofAuto().allocate(Integer.toUnsignedLong(lpDataSize.get(ValueLayout.JAVA_INT, 0))); + MemorySegment.copy(lpData, 0L, returnBuffer, 0L, returnBuffer.byteSize()); + return returnBuffer; } catch (BufferTooSmallException _) { if (bufferSize <= MAX_DATA_SIZE) { - return getStringValue(lpValueName, (bufferSize << 1) - 1); + return getValue(lpValueName, dwFlags, seed << 1); } else { - throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size reached".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path)); + throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); } } } From 20fd52e68c724f5725d6fdc4d335815e7c6213ed Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 13 Jun 2024 23:12:46 +0200 Subject: [PATCH 22/78] enable setting DWORDS as data for registry key values --- .../windows/capi/winreg/winreg_h.java | 72 +++++++++++++++++++ .../windows/common/WindowsRegistry.java | 20 ++++++ 2 files changed, 92 insertions(+) diff --git a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java index 9522b6c..ad82bd9 100644 --- a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java +++ b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java @@ -70,6 +70,78 @@ static MemoryLayout align(MemoryLayout layout, long align) { .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT; public static final ValueLayout.OfDouble C_LONG_DOUBLE = ValueLayout.JAVA_DOUBLE; + private static final int RRF_RT_REG_SZ = (int)2L; + /** + * {@snippet lang=c : + * #define RRF_RT_REG_SZ 2 + * } + */ + public static int RRF_RT_REG_SZ() { + return RRF_RT_REG_SZ; + } + private static final int RRF_RT_REG_EXPAND_SZ = (int)4L; + /** + * {@snippet lang=c : + * #define RRF_RT_REG_EXPAND_SZ 4 + * } + */ + public static int RRF_RT_REG_EXPAND_SZ() { + return RRF_RT_REG_EXPAND_SZ; + } + private static final int RRF_RT_REG_BINARY = (int)8L; + /** + * {@snippet lang=c : + * #define RRF_RT_REG_BINARY 8 + * } + */ + public static int RRF_RT_REG_BINARY() { + return RRF_RT_REG_BINARY; + } + private static final int RRF_RT_REG_DWORD = (int)16L; + /** + * {@snippet lang=c : + * #define RRF_RT_REG_DWORD 16 + * } + */ + public static int RRF_RT_REG_DWORD() { + return RRF_RT_REG_DWORD; + } + private static final int RRF_RT_REG_MULTI_SZ = (int)32L; + /** + * {@snippet lang=c : + * #define RRF_RT_REG_MULTI_SZ 32 + * } + */ + public static int RRF_RT_REG_MULTI_SZ() { + return RRF_RT_REG_MULTI_SZ; + } + private static final int RRF_RT_REG_QWORD = (int)64L; + /** + * {@snippet lang=c : + * #define RRF_RT_REG_QWORD 64 + * } + */ + public static int RRF_RT_REG_QWORD() { + return RRF_RT_REG_QWORD; + } + private static final int RRF_RT_ANY = (int)65535L; + /** + * {@snippet lang=c : + * #define RRF_RT_ANY 65535 + * } + */ + public static int RRF_RT_ANY() { + return RRF_RT_ANY; + } + private static final int RRF_NOEXPAND = (int)268435456L; + /** + * {@snippet lang=c : + * #define RRF_NOEXPAND 268435456 + * } + */ + public static int RRF_NOEXPAND() { + return RRF_NOEXPAND; + } private static class RegCloseKey { public static final FunctionDescriptor DESC = FunctionDescriptor.of( diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 6f89be0..84a9a69 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -226,6 +226,26 @@ private static class BufferTooSmallException extends RuntimeException { } + public void setDwordValue(String name, int data) { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var lpValueData = arena.allocateFrom(ValueLayout.JAVA_INT, data); + int result = winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, winreg_h.REG_DWORD(), lpValueData, (int) lpValueData.byteSize()); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(name, path, result)); + } + } + } + + public int getDwordValue(String name) { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var data = getValue(lpValueName, RRF_RT_REG_DWORD(), 2); + return data.get(ValueLayout.JAVA_INT,0); + } + } + + public void deleteSubtree(String subkey) { if (subkey == null || subkey.isBlank()) { throw new IllegalArgumentException("Subkey must not be empty"); From badfe8e9de2b475943f4ba6071970e32d066cb90 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 14 Jun 2024 15:15:33 +0200 Subject: [PATCH 23/78] generalize RegistryKeys setValue functions --- .../windows/common/WindowsRegistry.java | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 84a9a69..c379227 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -171,25 +171,21 @@ public static class RegistryKey implements AutoCloseable { this.path = path; } - public void setStringValue(String name, String data) throws RuntimeException { + //-- GetValue functions -- + + public String getStringValue(String name) throws RuntimeException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); - if (lpValueData.byteSize() > MAX_DATA_SIZE) { - throw new IllegalArgumentException("Data encoded as UTF 16 LE must be smaller than " + MAX_DATA_SIZE + "bytes."); - } - int result = winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, winreg_h.REG_SZ(), lpValueData, (int) lpValueData.byteSize()); - if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(name, path, result)); - } + var data = getValue(lpValueName, RRF_RT_REG_SZ(), 256L); + return data.getString(0, StandardCharsets.UTF_16LE); } } - public String getStringValue(String name) throws RuntimeException { + public int getDwordValue(String name) { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var data = getValue(lpValueName, RRF_RT_REG_SZ(), 256L); - return data.getString(0, StandardCharsets.UTF_16LE); + var data = getValue(lpValueName, RRF_RT_REG_DWORD(), 5L); + return data.get(ValueLayout.JAVA_INT,0); } } @@ -226,25 +222,34 @@ private static class BufferTooSmallException extends RuntimeException { } - public void setDwordValue(String name, int data) { + //-- SetValue functions -- + + public void setStringValue(String name, String data) throws RuntimeException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var lpValueData = arena.allocateFrom(ValueLayout.JAVA_INT, data); - int result = winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, winreg_h.REG_DWORD(), lpValueData, (int) lpValueData.byteSize()); - if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(name, path, result)); - } + var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); + setValue(lpValueName, lpValueData, winreg_h.REG_SZ()); } } - public int getDwordValue(String name) { + public void setDwordValue(String name, int data) { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var data = getValue(lpValueName, RRF_RT_REG_DWORD(), 2); - return data.get(ValueLayout.JAVA_INT,0); + var lpValueData = arena.allocateFrom(ValueLayout.JAVA_INT, data); + setValue(lpValueName, lpValueData, winreg_h.REG_DWORD()); } } + private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags) { + if (data.byteSize() > MAX_DATA_SIZE) { + throw new IllegalArgumentException("Data must be smaller than " + MAX_DATA_SIZE + "bytes."); + } + + int result = winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, dwFlags, data , (int) data.byteSize()); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); + } + } public void deleteSubtree(String subkey) { if (subkey == null || subkey.isBlank()) { From 28ae7370616a19478ca7c6f9fbe78f92953ce9d8 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 14 Jun 2024 16:44:19 +0200 Subject: [PATCH 24/78] allow to set expandable strings --- .../cryptomator/windows/common/WindowsRegistry.java | 8 ++++---- .../windows/common/WindowsRegistryIT.java | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index c379227..340d732 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -173,10 +173,10 @@ public static class RegistryKey implements AutoCloseable { //-- GetValue functions -- - public String getStringValue(String name) throws RuntimeException { + public String getStringValue(String name, boolean isExpandable) throws RuntimeException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var data = getValue(lpValueName, RRF_RT_REG_SZ(), 256L); + var data = getValue(lpValueName, isExpandable? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ(), 256L); return data.getString(0, StandardCharsets.UTF_16LE); } } @@ -224,11 +224,11 @@ private static class BufferTooSmallException extends RuntimeException { //-- SetValue functions -- - public void setStringValue(String name, String data) throws RuntimeException { + public void setStringValue(String name, String data, boolean isExpandable) throws RuntimeException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); - setValue(lpValueName, lpValueData, winreg_h.REG_SZ()); + setValue(lpValueName, lpValueData, isExpandable? REG_EXPAND_SZ() : winreg_h.REG_SZ()); } } diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index 5361984..377e5dd 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -57,13 +57,14 @@ public void testCreateNotExistingCommit() { public void testOpenSetValueRollback() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.setStringValue("itTest", "In Progress"); + k.setStringValue("itTest", "In Progress", false); } + //TODO: be more specific in the assertion Assertions.assertThrows(RuntimeException.class, () -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.getStringValue("itTest"); + k.getStringValue("itTest", false); } }); } @@ -74,13 +75,13 @@ public void testOpenSetValueRollback() { public void testOpenSetValueCommit() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.setStringValue("itTest", "In Progress"); + k.setStringValue("itTest", "In Progress", false); t.commit(); } try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - var data = k.getStringValue("itTest"); + var data = k.getStringValue("itTest", false); Assertions.assertEquals("In Progress", data); } } @@ -97,7 +98,7 @@ public void testOpenDeleteTreeRollback() { Assertions.assertDoesNotThrow(() -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.getStringValue("itTest"); + k.getStringValue("itTest", false); } }); } @@ -117,7 +118,7 @@ public void testOpenDeleteTreeCommit() { var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { Assertions.assertThrows(RuntimeException.class, () -> t.openRegKey(k, "subkey")); - Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("itTest")); + Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("itTest", false)); } } From f52258abc209b3754fd207d2136650671a5316a1 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 14 Jun 2024 16:46:23 +0200 Subject: [PATCH 25/78] implement adding explorer sidebar integration --- .../ExplorerSidebarService.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java new file mode 100644 index 0000000..3b99a7e --- /dev/null +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -0,0 +1,81 @@ +package org.cryptomator.windows.filemanagersidebar; + +import org.cryptomator.integrations.filemanagersidebar.SidebarService; +import org.cryptomator.windows.common.WindowsRegistry; + +import java.nio.file.Path; +import java.util.UUID; + +/** + * Implementation of the FileManagerSidebarService for Windows Explorer + *

+ * Based on a Microsoft docs example. + */ +public class ExplorerSidebarService implements SidebarService { + + @Override + public SidebarEntry add(Path mountpoint) { + var entryName = "Vault " + mountpoint.getFileName().toString(); + var clsid = UUID.randomUUID().toString(); + System.out.println(clsid); + //1. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d "MyCloudStorageApp" /f + try (var t = WindowsRegistry.startTransaction()) { + try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid), true)) { + + baseKey.setStringValue("", entryName, false); + + //2. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\DefaultIcon /ve /t REG_EXPAND_SZ /d %%SystemRoot%%\system32\imageres.dll,-1043 /f + try (var iconKey = t.createRegKey(baseKey, "DefaultIcon", true)) { + iconKey.setStringValue("", "C:\\Program Files\\Cryptomator\\Cryptomator.exe", false); + } + + //3. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /v System.IsPinnedToNameSpaceTree /t REG_DWORD /d 0x1 /f + baseKey.setDwordValue("System.IsPinnedToNameSpaceTree", 0x1); + + //4. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /v SortOrderIndex /t REG_DWORD /d 0x42 /f + baseKey.setDwordValue("SortOrderIndex", 0x41); + + //5. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\InProcServer32 /ve /t REG_EXPAND_SZ /d /f + try (var inProcServer32Key = t.createRegKey(baseKey, "InProcServer32", true)) { + inProcServer32Key.setStringValue("", "%systemroot%\\system32\\shell32.dll", true); + } + + //6. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\Instance /v CLSID /t REG_SZ /d {0E5AAE11-A475-4c5b-AB00-C66DE400274E} /f + try (var instanceKey = t.createRegKey(baseKey, "Instance", true)) { + instanceKey.setStringValue("CLSID", "{0E5AAE11-A475-4c5b-AB00-C66DE400274E}", false); + + //7. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\Instance\InitPropertyBag /v Attributes /t REG_DWORD /d 0x411 /f + // Attributes are READ_ONLY, DIRECTORY, REPARSE_POINT + try (var initPropertyBagKey = t.createRegKey(instanceKey, "InitPropertyBag", true)) { + initPropertyBagKey.setDwordValue("Attributes", 0x411); + + //8. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\Instance\InitPropertyBag /v TargetFolderPath /t REG_EXPAND_SZ /d %%USERPROFILE%%\MyCloudStorageApp /f + initPropertyBagKey.setStringValue("TargetFolderPath", mountpoint.toString(), false); + } + } + + //9. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\ShellFolder /v FolderValueFlags /t REG_DWORD /d 0x28 /f + try (var shellFolderKey = t.createRegKey(baseKey, "ShellFolder", true)) { + shellFolderKey.setDwordValue("FolderValueFlags", 0x28); + + //10. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\ShellFolder /v Attributes /t REG_DWORD /d 0xF080004D /f + shellFolderKey.setDwordValue("Attributes", 0xF080004D); + } + } + + //11. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d MyCloudStorageApp /f + var nameSpaceSubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\{%s}".formatted(clsid); + try (var nameSpaceKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, nameSpaceSubKey, true)) { + nameSpaceKey.setStringValue("", entryName, false); + } + + //12. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel /v {0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /t REG_DWORD /d 0x1 /f + try (var newStartPanelKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", true)) { + newStartPanelKey.setDwordValue("{%s}".formatted(clsid), 0x1); + } + t.commit(); + } + return null; + } + +} From 50755f31b55f73bd532ce15623ba22819db5ff36 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 14 Jun 2024 17:55:09 +0200 Subject: [PATCH 26/78] add feature to delete single values from registry keys --- .../windows/capi/winreg/winreg_h.java | 60 +++++++++++++++++ .../windows/common/WindowsRegistry.java | 20 +++++- .../windows/common/WindowsRegistryIT.java | 64 +++++++++++++++---- 3 files changed, 129 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java index ad82bd9..80e254c 100644 --- a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java +++ b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java @@ -396,6 +396,66 @@ public static int RegOpenKeyTransactedW(MemorySegment hKey, MemorySegment lpSubK } } + private static class RegDeleteKeyValueW { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + winreg_h.C_LONG, + winreg_h.C_POINTER, + winreg_h.C_POINTER, + winreg_h.C_POINTER + ); + + public static final MemorySegment ADDR = winreg_h.findOrThrow("RegDeleteKeyValueW"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * LSTATUS RegDeleteKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName) + * } + */ + public static FunctionDescriptor RegDeleteKeyValueW$descriptor() { + return RegDeleteKeyValueW.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * LSTATUS RegDeleteKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName) + * } + */ + public static MethodHandle RegDeleteKeyValueW$handle() { + return RegDeleteKeyValueW.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * LSTATUS RegDeleteKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName) + * } + */ + public static MemorySegment RegDeleteKeyValueW$address() { + return RegDeleteKeyValueW.ADDR; + } + + /** + * {@snippet lang=c : + * LSTATUS RegDeleteKeyValueW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpValueName) + * } + */ + public static int RegDeleteKeyValueW(MemorySegment hKey, MemorySegment lpSubKey, MemorySegment lpValueName) { + var mh$ = RegDeleteKeyValueW.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("RegDeleteKeyValueW", hKey, lpSubKey, lpValueName); + } + return (int)mh$.invokeExact(hKey, lpSubKey, lpValueName); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + private static class RegSetKeyValueW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( winreg_h.C_LONG, diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 340d732..2e6f071 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -251,14 +251,30 @@ private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags } } + //-- delete operations + + public void deleteValue(String valueName) { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(valueName, StandardCharsets.UTF_16LE); + int result = winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Deleting Key failed with error code " + result); + } + } + } + public void deleteSubtree(String subkey) { if (subkey == null || subkey.isBlank()) { throw new IllegalArgumentException("Subkey must not be empty"); } - deleteValuesAndSubtree(subkey); + deleteValuesAndSubtrees(subkey); + } + + public void deleteAllValuesAndSubtrees() { + deleteValuesAndSubtrees(""); } - public void deleteValuesAndSubtree(String subkey) { + private void deleteValuesAndSubtrees(String subkey) { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); int result = winreg_h.RegDeleteTreeW(handle, lpSubkey); diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index 377e5dd..cba1b0a 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -1,6 +1,7 @@ package org.cryptomator.windows.common; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; @@ -57,14 +58,14 @@ public void testCreateNotExistingCommit() { public void testOpenSetValueRollback() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.setStringValue("itTest", "In Progress", false); + k.setStringValue("exampleStringValue", "In Progress", false); } //TODO: be more specific in the assertion Assertions.assertThrows(RuntimeException.class, () -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.getStringValue("itTest", false); + k.getStringValue("exampleStringValue", false); } }); } @@ -75,42 +76,79 @@ public void testOpenSetValueRollback() { public void testOpenSetValueCommit() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.setStringValue("itTest", "In Progress", false); + k.setStringValue("exampleStringValue", "In Progress", false); + k.setDwordValue("exampleDwordValue", 0x42); t.commit(); } try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - var data = k.getStringValue("itTest", false); - Assertions.assertEquals("In Progress", data); + var stringData = k.getStringValue("exampleStringValue", false); + var binaryData = k.getDwordValue("exampleDwordValue"); + Assertions.assertEquals("In Progress", stringData); + Assertions.assertEquals(0x42, binaryData); } } @Test - @DisplayName("Open, deleteTree, rollback") + @DisplayName("Open, deleteValue, rollback") @Order(5) + public void testOpenDeleteValueRollback() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.deleteValue("exampleDwordValue"); + } + + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var data = k.getDwordValue("exampleDwordValue"); + Assertions.assertEquals(0x42, data); + } + } + + @Test + @DisplayName("Open, deleteValue, commit") + @Order(6) + public void testOpenDeleteValueCommit() { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + k.deleteValue("exampleDwordValue"); + t.commit(); + } + + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + Assertions.assertThrows(RuntimeException.class, () -> { + k.getDwordValue("exampleDwordValue"); + }); + } + } + + @Test + @DisplayName("Open, deleteValuesAndSubtrees, rollback") + @Order(7) public void testOpenDeleteTreeRollback() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.deleteValuesAndSubtree(""); + k.deleteAllValuesAndSubtrees(); } Assertions.assertDoesNotThrow(() -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.getStringValue("itTest", false); + k.getStringValue("exampleStringValue", false); } }); } @Test @DisplayName("Open, deleteValuesAndSubtrees, commit") - @Order(6) + @Order(8) public void testOpenDeleteTreeCommit() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); var subk = t.createRegKey(k, "subkey", true)) { - k.deleteValuesAndSubtree(""); + k.deleteAllValuesAndSubtrees(); t.commit(); } @@ -118,13 +156,13 @@ public void testOpenDeleteTreeCommit() { var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { Assertions.assertThrows(RuntimeException.class, () -> t.openRegKey(k, "subkey")); - Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("itTest", false)); + Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("exampleStringValue", false)); } } @Test @DisplayName("Delete, rollback") - @Order(7) + @Order(9) public void testDeleteRollback() { try (var t = WindowsRegistry.startTransaction()) { t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); @@ -137,7 +175,7 @@ public void testDeleteRollback() { @Test @DisplayName("Delete, commit") - @Order(8) + @Order(10) public void testDeleteCommit() { try (var t = WindowsRegistry.startTransaction()) { t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); From eff5fff13fb7e77321d2138d5bc021f84513d788 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 14 Jun 2024 17:55:56 +0200 Subject: [PATCH 27/78] implement removal of sidebar entry --- .../ExplorerSidebarService.java | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index 3b99a7e..21ae008 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -15,12 +15,12 @@ public class ExplorerSidebarService implements SidebarService { @Override public SidebarEntry add(Path mountpoint) { - var entryName = "Vault " + mountpoint.getFileName().toString(); - var clsid = UUID.randomUUID().toString(); + var entryName = "Vault - " + mountpoint.getFileName().toString(); + var clsid = "{" + UUID.randomUUID() + "}"; System.out.println(clsid); //1. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d "MyCloudStorageApp" /f try (var t = WindowsRegistry.startTransaction()) { - try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid), true)) { + try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true)) { baseKey.setStringValue("", entryName, false); @@ -64,18 +64,42 @@ public SidebarEntry add(Path mountpoint) { } //11. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d MyCloudStorageApp /f - var nameSpaceSubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\{%s}".formatted(clsid); + var nameSpaceSubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; try (var nameSpaceKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, nameSpaceSubKey, true)) { nameSpaceKey.setStringValue("", entryName, false); } //12. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel /v {0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /t REG_DWORD /d 0x1 /f try (var newStartPanelKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", true)) { - newStartPanelKey.setDwordValue("{%s}".formatted(clsid), 0x1); + newStartPanelKey.setDwordValue(clsid, 0x1); } t.commit(); } - return null; + return new ExplorerSidebarEntry(clsid); + } + + record ExplorerSidebarEntry(String clsid) implements SidebarEntry { + + @Override + public void remove() { + try (var t = WindowsRegistry.startTransaction()) { + //undo step 11. + var nameSpaceSubkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; + t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, nameSpaceSubkey); + + //undo step 12. + try (var nameSpaceKey = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel")) { + nameSpaceKey.deleteValue(clsid); + } + + //undo everything else + try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid), true)) { + baseKey.deleteAllValuesAndSubtrees(); + } + t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid)); + t.commit(); + } + } } } From 07c6f56110f1cfc709a17745bee86ca198fd4184 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 14 Jun 2024 17:56:14 +0200 Subject: [PATCH 28/78] add integration test for sidebar service --- .../ExplorerSidebarServiceIT.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java diff --git a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java new file mode 100644 index 0000000..f484dfa --- /dev/null +++ b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java @@ -0,0 +1,32 @@ +package org.cryptomator.filemanagersidebar; + +import org.cryptomator.windows.filemanagersidebar.ExplorerSidebarService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; + +public class ExplorerSidebarServiceIT { + + @TempDir + Path base; + + @Test + @DisplayName("Integrates a temp dir for 20s into the file explorer sidebar") + public void testAddCLSIDTree() throws IOException, InterruptedException { + var p = base.resolve("integration-win-testVault"); + Files.createDirectory(p); + Files.createFile(p.resolve("firstLevel.file")); + var sub = Files.createDirectory(p.resolve("subdir")); + Files.createFile(sub.resolve("secondLevel.file")); + var entry = new ExplorerSidebarService().add(p); + + Thread.sleep(Duration.ofSeconds(20)); + + entry.remove(); + } +} From 233976758d0e70164bde83debd376804611b93c4 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 14 Jun 2024 18:24:19 +0200 Subject: [PATCH 29/78] add service annotations and expose service --- pom.xml | 2 +- src/main/java/module-info.java | 3 +++ .../windows/filemanagersidebar/ExplorerSidebarService.java | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 789766f..d1ded5a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.cryptomator integrations-win - 1.3.0-SNAPSHOT + 1.3.0-sidebar Cryptomator Integrations for Windows Provides optional Windows services used by Cryptomator diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 97af1f7..ce2e1b8 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,8 +1,10 @@ import org.cryptomator.integrations.autostart.AutoStartProvider; +import org.cryptomator.integrations.filemanagersidebar.SidebarService; import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.integrations.uiappearance.UiAppearanceProvider; import org.cryptomator.windows.autostart.WindowsAutoStart; +import org.cryptomator.windows.filemanagersidebar.ExplorerSidebarService; import org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess; import org.cryptomator.windows.revealpath.ExplorerRevealPathService; import org.cryptomator.windows.uiappearance.WinUiAppearanceProvider; @@ -19,4 +21,5 @@ provides KeychainAccessProvider with WindowsProtectedKeychainAccess; provides UiAppearanceProvider with WinUiAppearanceProvider; provides RevealPathService with ExplorerRevealPathService; + provides SidebarService with ExplorerSidebarService; } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index 21ae008..e12b25f 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -1,5 +1,7 @@ package org.cryptomator.windows.filemanagersidebar; +import org.cryptomator.integrations.common.OperatingSystem; +import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.filemanagersidebar.SidebarService; import org.cryptomator.windows.common.WindowsRegistry; @@ -11,6 +13,8 @@ *

* Based on a Microsoft docs example. */ +@Priority(100) +@OperatingSystem(OperatingSystem.Value.WINDOWS) public class ExplorerSidebarService implements SidebarService { @Override From c854fe6baf8756b6183a99fc6e236c78fb5f9fce Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 17 Jun 2024 12:35:44 +0200 Subject: [PATCH 30/78] rename method --- .../filemanagersidebar/ExplorerSidebarServiceIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java index f484dfa..3f39017 100644 --- a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java +++ b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java @@ -17,7 +17,7 @@ public class ExplorerSidebarServiceIT { @Test @DisplayName("Integrates a temp dir for 20s into the file explorer sidebar") - public void testAddCLSIDTree() throws IOException, InterruptedException { + public void testExplorerSidebarIntegration() throws IOException, InterruptedException { var p = base.resolve("integration-win-testVault"); Files.createDirectory(p); Files.createFile(p.resolve("firstLevel.file")); From 60849a6600b091f6465034dae531e71f1bde90b2 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 17 Jun 2024 13:43:27 +0200 Subject: [PATCH 31/78] tabbifying pom --- pom.xml | 458 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 236 insertions(+), 222 deletions(-) diff --git a/pom.xml b/pom.xml index 95937a0..758df39 100644 --- a/pom.xml +++ b/pom.xml @@ -1,74 +1,74 @@ - 4.0.0 - org.cryptomator - integrations-win - 1.3.0-SNAPSHOT + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + org.cryptomator + integrations-win + 1.3.0-SNAPSHOT - Cryptomator Integrations for Windows - Provides optional Windows services used by Cryptomator - https://github.com/cryptomator/integrations-win - - scm:git:git@github.com:cryptomator/integrations-win.git - scm:git:git@github.com:cryptomator/integrations-win.git - git@github.com:cryptomator/integrations-win.git - - - - Sebastian Stenzel - sebastian.stenzel@skymatic.de - +1 - Skymatic GmbH - http://skymatic.de - - - Armin Schrenk - armin.schrenk@skymatic.de - +1 - Skymatic GmbH - http://skymatic.de - - + Cryptomator Integrations for Windows + Provides optional Windows services used by Cryptomator + https://github.com/cryptomator/integrations-win + + scm:git:git@github.com:cryptomator/integrations-win.git + scm:git:git@github.com:cryptomator/integrations-win.git + git@github.com:cryptomator/integrations-win.git + + + + Sebastian Stenzel + sebastian.stenzel@skymatic.de + +1 + Skymatic GmbH + http://skymatic.de + + + Armin Schrenk + armin.schrenk@skymatic.de + +1 + Skymatic GmbH + http://skymatic.de + + - - UTF-8 + + UTF-8 17 - - 1.3.1 - 2.0.13 + + 1.3.1 + 2.0.13 2.17.1 - + 5.10.2 5.12.0 9.2.0 1.7.0 - + - - - GNU Affero General Public License (AGPL) version 3.0 - https://www.gnu.org/licenses/agpl.txt - repo - - + + + GNU Affero General Public License (AGPL) version 3.0 + https://www.gnu.org/licenses/agpl.txt + repo + + - - - org.cryptomator - integrations-api - ${api.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - + + + org.cryptomator + integrations-api + ${api.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + com.fasterxml.jackson.core jackson-databind @@ -80,58 +80,58 @@ ${jackson.version} - - - org.slf4j - slf4j-simple - ${slf4j.version} - test - - - org.junit.jupiter - junit-jupiter - ${junit.jupiter.version} - test - - - org.mockito - mockito-core - ${mockito.version} - test - - + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + - - - - org.apache.maven.plugins - maven-clean-plugin - 3.3.2 - - - - ${project.basedir}/src/main/headers - false - - *.h - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - - -h - ${project.basedir}/src/main/headers - - ${project.jdk.version} - - - + + + + org.apache.maven.plugins + maven-clean-plugin + 3.3.2 + + + + ${project.basedir}/src/main/headers + false + + *.h + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + -h + ${project.basedir}/src/main/headers + + ${project.jdk.version} + + + org.apache.maven.plugins maven-enforcer-plugin 3.5.0 @@ -152,129 +152,143 @@ - - maven-resources-plugin - 3.3.1 - - - copy-dlls - process-classes - - copy-resources - - - ${project.build.outputDirectory} - - - ${project.basedir}/src/main/resources - false - - *.dll - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.5 - - - maven-source-plugin - 3.3.1 - - - attach-sources - - jar-no-fork - - - - - - maven-javadoc-plugin - 3.7.0 - - - attach-javadocs - - jar - - - - - true - ${project.jdk.version} - - - - apiNote - a - API Note: - - - implSpec - a - Implementation Requirements: - - - implNote - a - Implementation Note: - - param - return - throws - since - version - serialData - see - - - - - + + maven-resources-plugin + 3.3.1 + + + copy-dlls + process-classes + + copy-resources + + + ${project.build.outputDirectory} + + + ${project.basedir}/src/main/resources + false + + *.dll + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + + maven-javadoc-plugin + 3.7.0 + + + attach-javadocs + + jar + + + + + true + ${project.jdk.version} + + + + apiNote + a + API Note: + + + implSpec + a + Implementation Requirements: + + + implNote + a + Implementation Note: + + + param + + + return + + + throws + + + since + + + version + + + serialData + + + see + + + + + + - - - dependency-check - - - - org.owasp - dependency-check-maven - ${dependency-check.version} - - 24 - 0 - true - true - suppression.xml - false - ${env.NVD_API_KEY} - - - - - check - - validate - - - - - - + + + dependency-check + + + + org.owasp + dependency-check-maven + ${dependency-check.version} + + 24 + 0 + true + true + suppression.xml + false + ${env.NVD_API_KEY} + + + + + check + + validate + + + + + + compile-native !skipNativeCompile - + From f8aef6cd119400dd49e4228c2d6d28dd9062342a Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 17 Jun 2024 13:46:06 +0200 Subject: [PATCH 32/78] move revel service test to integration testing --- ...ealPathServiceTest.java => ExplorerRevealPathServiceIT.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/org/cryptomator/windows/revealpath/{ExplorerRevealPathServiceTest.java => ExplorerRevealPathServiceIT.java} (95%) diff --git a/src/test/java/org/cryptomator/windows/revealpath/ExplorerRevealPathServiceTest.java b/src/test/java/org/cryptomator/windows/revealpath/ExplorerRevealPathServiceIT.java similarity index 95% rename from src/test/java/org/cryptomator/windows/revealpath/ExplorerRevealPathServiceTest.java rename to src/test/java/org/cryptomator/windows/revealpath/ExplorerRevealPathServiceIT.java index eb77dd7..84d01a0 100644 --- a/src/test/java/org/cryptomator/windows/revealpath/ExplorerRevealPathServiceTest.java +++ b/src/test/java/org/cryptomator/windows/revealpath/ExplorerRevealPathServiceIT.java @@ -9,7 +9,7 @@ import java.nio.file.Files; import java.nio.file.Path; -public class ExplorerRevealPathServiceTest { +public class ExplorerRevealPathServiceIT { ExplorerRevealPathService service = new ExplorerRevealPathService(); From 1180ef8fa81d2da461b6cdba3320a66c8ad75bac Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 17 Jun 2024 13:50:57 +0200 Subject: [PATCH 33/78] setting IDE project JDK to 22 --- .idea/misc.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index f2fb311..ea62868 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + @@ -7,5 +8,5 @@ - + \ No newline at end of file From 81f5a8f46b47b446a120d284e6c1986b2a9f6b48 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 17 Jun 2024 13:53:12 +0200 Subject: [PATCH 34/78] adding jextract-maven-plugin --- pom.xml | 81 +++++++++++++++++++ .../windows/capi/winreg/winreg_h.java | 9 --- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 0da0d30..277e79a 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,11 @@ 9.2.0 1.7.0 + 0.4.1 + + + C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt + C:\Users\Arbeit\Programs\jextract-22\bin\jextract.bat @@ -391,5 +396,81 @@ + + + jextract + + false + + + + + io.github.coffeelibs + jextract-maven-plugin + ${jextract-maven.version} + + ${jextract.executable} + ${win.ucrtHeaderPath} + ${project.build.sourceDirectory} + + + + jextract-winreg + + sources + + + org.cryptomator.windows.capi.winreg + ${win.ucrtHeaderPath}/../um/winreg.h + winreg_h + + Advapi32 + + + _AMD64_ + UNICODE + _WIN32_WINNT=0x0A00 + + + RegCreateKeyTransactedW + RegOpenKeyTransactedW + RegDeleteKeyTransactedW + RegCloseKey + RegGetValueW + RegSetKeyValueW + RegDeleteKeyValueW + RegDeleteTreeW + + + HKEY_CLASSES_ROOT + HKEY_LOCAL_MACHINE + HKEY_USERS + HKEY_CURRENT_USER + REG_OPTION_VOLATILE + REG_OPTION_NON_VOLATILE + KEY_READ + KEY_WRITE + REG_BINARY + REG_DWORD + REG_QWORD + REG_SZ + REG_EXPAND_SZ + REG_MULTI_SZ + RRF_RT_ANY + RRF_RT_REG_BINARY + RRF_RT_REG_DWORD + RRF_RT_REG_QWORD + RRF_RT_REG_SZ + RRF_RT_REG_EXPAND_SZ + RRF_RT_REG_MULTI_SZ + RRF_NOEXPAND + + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java index 80e254c..13c7a21 100644 --- a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java +++ b/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java @@ -677,15 +677,6 @@ public static int REG_OPTION_NON_VOLATILE() { public static int REG_OPTION_VOLATILE() { return REG_OPTION_VOLATILE; } - private static final int REG_NONE = (int)0L; - /** - * {@snippet lang=c : - * #define REG_NONE 0 - * } - */ - public static int REG_NONE() { - return REG_NONE; - } private static final int REG_SZ = (int)1L; /** * {@snippet lang=c : From c59d5d28ef34a12694ae99495ee7f14076d6300c Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 17 Jun 2024 17:29:29 +0200 Subject: [PATCH 35/78] add jextract-maven-plugin to pom and renamed jextract'ed classes --- pom.xml | 64 +++++++-- ...indows_keychain_WinDataProtection_Native.h | 29 ----- ...indows_uiappearance_WinAppearance_Native.h | 29 ----- .../windows/capi/common/Windows_h.java | 6 - .../{ktmw32_ex_h.java => Ktmw32_h.java} | 43 +++--- .../winreg/{winreg_h.java => Winreg_h.java} | 122 +++++++++--------- .../windows/common/WindowsRegistry.java | 40 +++--- src/main/resources/ktmw32_helper.h | 2 + 8 files changed, 164 insertions(+), 171 deletions(-) delete mode 100644 src/main/headers/org_cryptomator_windows_keychain_WinDataProtection_Native.h delete mode 100644 src/main/headers/org_cryptomator_windows_uiappearance_WinAppearance_Native.h rename src/main/java/org/cryptomator/windows/capi/ktmw32/{ktmw32_ex_h.java => Ktmw32_h.java} (89%) rename src/main/java/org/cryptomator/windows/capi/winreg/{winreg_h.java => Winreg_h.java} (91%) create mode 100644 src/main/resources/ktmw32_helper.h diff --git a/pom.xml b/pom.xml index 277e79a..0a44335 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ 0.4.1 - C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt + C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um C:\Users\Arbeit\Programs\jextract-22\bin\jextract.bat @@ -410,8 +410,13 @@ ${jextract-maven.version} ${jextract.executable} - ${win.ucrtHeaderPath} + ${win.umHeaderPath} ${project.build.sourceDirectory} + + _AMD64_ + UNICODE + _WIN32_WINNT=0x0A00 + @@ -421,16 +426,11 @@ org.cryptomator.windows.capi.winreg - ${win.ucrtHeaderPath}/../um/winreg.h - winreg_h + ${win.umHeaderPath}/winreg.h + Winreg_h Advapi32 - - _AMD64_ - UNICODE - _WIN32_WINNT=0x0A00 - RegCreateKeyTransactedW RegOpenKeyTransactedW @@ -467,6 +467,52 @@ + + jextract-ktmw32 + + sources + + + org.cryptomator.windows.capi.ktmw32 + ${project.basedir}/src/main/resources/ktmw32_helper.h + Ktmw32_h + + KtmW32 + + + CreateTransaction + CommitTransaction + RollbackTransaction + + + INVALID_HANDLE_VALUE + + + + + jextract-common + + sources + + + org.cryptomator.windows.capi.common + ${win.umHeaderPath}/Windows.h + Windows_h + + Kernel32 + + + GetLastError + CloseHandle + + + ERROR_SUCCESS + INVALID_HANDLE_VALUE + ERROR_SUCCESS + ERROR_MORE_DATA + + + diff --git a/src/main/headers/org_cryptomator_windows_keychain_WinDataProtection_Native.h b/src/main/headers/org_cryptomator_windows_keychain_WinDataProtection_Native.h deleted file mode 100644 index 8ca5b8f..0000000 --- a/src/main/headers/org_cryptomator_windows_keychain_WinDataProtection_Native.h +++ /dev/null @@ -1,29 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_cryptomator_windows_keychain_WinDataProtection_Native */ - -#ifndef _Included_org_cryptomator_windows_keychain_WinDataProtection_Native -#define _Included_org_cryptomator_windows_keychain_WinDataProtection_Native -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_cryptomator_windows_keychain_WinDataProtection_Native - * Method: protect - * Signature: ([B[B)[B - */ -JNIEXPORT jbyteArray JNICALL Java_org_cryptomator_windows_keychain_WinDataProtection_00024Native_protect - (JNIEnv *, jobject, jbyteArray, jbyteArray); - -/* - * Class: org_cryptomator_windows_keychain_WinDataProtection_Native - * Method: unprotect - * Signature: ([B[B)[B - */ -JNIEXPORT jbyteArray JNICALL Java_org_cryptomator_windows_keychain_WinDataProtection_00024Native_unprotect - (JNIEnv *, jobject, jbyteArray, jbyteArray); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/main/headers/org_cryptomator_windows_uiappearance_WinAppearance_Native.h b/src/main/headers/org_cryptomator_windows_uiappearance_WinAppearance_Native.h deleted file mode 100644 index 4bf01ab..0000000 --- a/src/main/headers/org_cryptomator_windows_uiappearance_WinAppearance_Native.h +++ /dev/null @@ -1,29 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_cryptomator_windows_uiappearance_WinAppearance_Native */ - -#ifndef _Included_org_cryptomator_windows_uiappearance_WinAppearance_Native -#define _Included_org_cryptomator_windows_uiappearance_WinAppearance_Native -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_cryptomator_windows_uiappearance_WinAppearance_Native - * Method: getCurrentTheme - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_cryptomator_windows_uiappearance_WinAppearance_00024Native_getCurrentTheme - (JNIEnv *, jobject); - -/* - * Class: org_cryptomator_windows_uiappearance_WinAppearance_Native - * Method: waitForNextThemeChange - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_org_cryptomator_windows_uiappearance_WinAppearance_00024Native_waitForNextThemeChange - (JNIEnv *, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java index c4b9c0d..21c163c 100644 --- a/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java +++ b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java @@ -70,12 +70,6 @@ static MemoryLayout align(MemoryLayout layout, long align) { .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT; public static final ValueLayout.OfDouble C_LONG_DOUBLE = ValueLayout.JAVA_DOUBLE; - /** - * {@snippet lang=c : - * typedef void *HANDLE - * } - */ - public static final AddressLayout HANDLE = Windows_h.C_POINTER; private static class CloseHandle { public static final FunctionDescriptor DESC = FunctionDescriptor.of( diff --git a/src/main/java/org/cryptomator/windows/capi/ktmw32/ktmw32_ex_h.java b/src/main/java/org/cryptomator/windows/capi/ktmw32/Ktmw32_h.java similarity index 89% rename from src/main/java/org/cryptomator/windows/capi/ktmw32/ktmw32_ex_h.java rename to src/main/java/org/cryptomator/windows/capi/ktmw32/Ktmw32_h.java index 14cb1f3..e011f0e 100644 --- a/src/main/java/org/cryptomator/windows/capi/ktmw32/ktmw32_ex_h.java +++ b/src/main/java/org/cryptomator/windows/capi/ktmw32/Ktmw32_h.java @@ -12,9 +12,9 @@ import static java.lang.foreign.ValueLayout.*; import static java.lang.foreign.MemoryLayout.PathElement.*; -public class ktmw32_ex_h { +public class Ktmw32_h { - ktmw32_ex_h() { + Ktmw32_h() { // Should not be called directly } @@ -73,17 +73,17 @@ static MemoryLayout align(MemoryLayout layout, long align) { private static class CreateTransaction { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - ktmw32_ex_h.C_POINTER, - ktmw32_ex_h.C_POINTER, - ktmw32_ex_h.C_POINTER, - ktmw32_ex_h.C_LONG, - ktmw32_ex_h.C_LONG, - ktmw32_ex_h.C_LONG, - ktmw32_ex_h.C_LONG, - ktmw32_ex_h.C_POINTER + Ktmw32_h.C_POINTER, + Ktmw32_h.C_POINTER, + Ktmw32_h.C_POINTER, + Ktmw32_h.C_LONG, + Ktmw32_h.C_LONG, + Ktmw32_h.C_LONG, + Ktmw32_h.C_LONG, + Ktmw32_h.C_POINTER ); - public static final MemorySegment ADDR = ktmw32_ex_h.findOrThrow("CreateTransaction"); + public static final MemorySegment ADDR = Ktmw32_h.findOrThrow("CreateTransaction"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -137,11 +137,11 @@ public static MemorySegment CreateTransaction(MemorySegment lpTransactionAttribu private static class CommitTransaction { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - ktmw32_ex_h.C_INT, - ktmw32_ex_h.C_POINTER + Ktmw32_h.C_INT, + Ktmw32_h.C_POINTER ); - public static final MemorySegment ADDR = ktmw32_ex_h.findOrThrow("CommitTransaction"); + public static final MemorySegment ADDR = Ktmw32_h.findOrThrow("CommitTransaction"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -195,11 +195,11 @@ public static int CommitTransaction(MemorySegment TransactionHandle) { private static class RollbackTransaction { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - ktmw32_ex_h.C_INT, - ktmw32_ex_h.C_POINTER + Ktmw32_h.C_INT, + Ktmw32_h.C_POINTER ); - public static final MemorySegment ADDR = ktmw32_ex_h.findOrThrow("RollbackTransaction"); + public static final MemorySegment ADDR = Ktmw32_h.findOrThrow("RollbackTransaction"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -250,5 +250,14 @@ public static int RollbackTransaction(MemorySegment TransactionHandle) { throw new AssertionError("should not reach here", ex$); } } + private static final MemorySegment INVALID_HANDLE_VALUE = MemorySegment.ofAddress(-1L); + /** + * {@snippet lang=c : + * #define INVALID_HANDLE_VALUE (void*) -1 + * } + */ + public static MemorySegment INVALID_HANDLE_VALUE() { + return INVALID_HANDLE_VALUE; + } } diff --git a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java b/src/main/java/org/cryptomator/windows/capi/winreg/Winreg_h.java similarity index 91% rename from src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java rename to src/main/java/org/cryptomator/windows/capi/winreg/Winreg_h.java index 13c7a21..2974f41 100644 --- a/src/main/java/org/cryptomator/windows/capi/winreg/winreg_h.java +++ b/src/main/java/org/cryptomator/windows/capi/winreg/Winreg_h.java @@ -12,9 +12,9 @@ import static java.lang.foreign.ValueLayout.*; import static java.lang.foreign.MemoryLayout.PathElement.*; -public class winreg_h { +public class Winreg_h { - winreg_h() { + Winreg_h() { // Should not be called directly } @@ -145,11 +145,11 @@ public static int RRF_NOEXPAND() { private static class RegCloseKey { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER + Winreg_h.C_LONG, + Winreg_h.C_POINTER ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegCloseKey"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegCloseKey"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -203,21 +203,21 @@ public static int RegCloseKey(MemorySegment hKey) { private static class RegCreateKeyTransactedW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_LONG, - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_LONG, + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegCreateKeyTransactedW"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegCreateKeyTransactedW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -271,16 +271,16 @@ public static int RegCreateKeyTransactedW(MemorySegment hKey, MemorySegment lpSu private static class RegDeleteKeyTransactedW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_LONG, - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_LONG, + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegDeleteKeyTransactedW"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegDeleteKeyTransactedW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -334,17 +334,17 @@ public static int RegDeleteKeyTransactedW(MemorySegment hKey, MemorySegment lpSu private static class RegOpenKeyTransactedW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_LONG, - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_LONG, + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegOpenKeyTransactedW"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegOpenKeyTransactedW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -398,13 +398,13 @@ public static int RegOpenKeyTransactedW(MemorySegment hKey, MemorySegment lpSubK private static class RegDeleteKeyValueW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegDeleteKeyValueW"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegDeleteKeyValueW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -458,16 +458,16 @@ public static int RegDeleteKeyValueW(MemorySegment hKey, MemorySegment lpSubKey, private static class RegSetKeyValueW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_LONG + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_LONG ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegSetKeyValueW"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegSetKeyValueW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -521,12 +521,12 @@ public static int RegSetKeyValueW(MemorySegment hKey, MemorySegment lpSubKey, Me private static class RegDeleteTreeW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegDeleteTreeW"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegDeleteTreeW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } @@ -580,17 +580,17 @@ public static int RegDeleteTreeW(MemorySegment hKey, MemorySegment lpSubKey) { private static class RegGetValueW { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_LONG, - winreg_h.C_POINTER, - winreg_h.C_POINTER, - winreg_h.C_POINTER + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_LONG, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER, + Winreg_h.C_POINTER ); - public static final MemorySegment ADDR = winreg_h.findOrThrow("RegGetValueW"); + public static final MemorySegment ADDR = Winreg_h.findOrThrow("RegGetValueW"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 2e6f071..272ae14 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -1,8 +1,8 @@ package org.cryptomator.windows.common; import org.cryptomator.windows.capi.common.Windows_h; -import org.cryptomator.windows.capi.ktmw32.ktmw32_ex_h; -import org.cryptomator.windows.capi.winreg.winreg_h; +import org.cryptomator.windows.capi.ktmw32.Ktmw32_h; +import org.cryptomator.windows.capi.winreg.Winreg_h; import java.lang.foreign.AddressLayout; import java.lang.foreign.Arena; @@ -14,14 +14,14 @@ import static org.cryptomator.windows.capi.common.Windows_h.ERROR_MORE_DATA; import static org.cryptomator.windows.capi.common.Windows_h.ERROR_SUCCESS; import static org.cryptomator.windows.capi.common.Windows_h.INVALID_HANDLE_VALUE; -import static org.cryptomator.windows.capi.winreg.winreg_h.*; +import static org.cryptomator.windows.capi.winreg.Winreg_h.*; public class WindowsRegistry { public static final long MAX_DATA_SIZE = (1L << 32) - 1L; //unsinged integer public static RegistryTransaction startTransaction() { - var transactionHandle = ktmw32_ex_h.CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL); + var transactionHandle = Ktmw32_h.CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL); if (transactionHandle.address() == INVALID_HANDLE_VALUE().address()) { //GetLastError() int error = Windows_h.GetLastError(); @@ -45,7 +45,7 @@ public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolati var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); - int result = winreg_h.RegCreateKeyTransactedW( + int result = Winreg_h.RegCreateKeyTransactedW( key.getHandle(), lpSubkey, 0, @@ -69,7 +69,7 @@ public RegistryKey openRegKey(RegistryKey key, String subkey) { var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); - int result = winreg_h.RegOpenKeyTransactedW( + int result = Winreg_h.RegOpenKeyTransactedW( key.getHandle(), lpSubkey, 0, @@ -88,7 +88,7 @@ public RegistryKey openRegKey(RegistryKey key, String subkey) { public void deleteRegKey(RegistryKey key, String subkey) { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); - int result = winreg_h.RegDeleteKeyTransactedW( + int result = Winreg_h.RegDeleteKeyTransactedW( key.getHandle(), lpSubkey, 0x0100, //KEY_WOW64_64KEY @@ -107,7 +107,7 @@ public synchronized void commit() { if (isClosed) { throw new IllegalStateException("Transaction already closed"); } - int result = ktmw32_ex_h.CommitTransaction(transactionHandle); + int result = Ktmw32_h.CommitTransaction(transactionHandle); if (result == 0) { int error = Windows_h.GetLastError(); throw new RuntimeException("Native code returned win32 error code " + error); @@ -120,7 +120,7 @@ public synchronized void rollback() { if (isClosed) { throw new IllegalStateException("Transaction already closed"); } - int result = ktmw32_ex_h.RollbackTransaction(transactionHandle); + int result = Ktmw32_h.RollbackTransaction(transactionHandle); if (result == 0) { int error = Windows_h.GetLastError(); throw new RuntimeException("Native code returned win32 error code " + error); @@ -157,10 +157,10 @@ private synchronized void closeInternal() { public static class RegistryKey implements AutoCloseable { - public static final RegistryKey HKEY_CURRENT_USER = new RegistryKey(winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); - public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryKey(winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); - public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryKey(winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); - public static final RegistryKey HKEY_USERS = new RegistryKey(winreg_h.HKEY_USERS(), "HKEY_USERS"); + public static final RegistryKey HKEY_CURRENT_USER = new RegistryKey(Winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); + public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryKey(Winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); + public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryKey(Winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); + public static final RegistryKey HKEY_USERS = new RegistryKey(Winreg_h.HKEY_USERS(), "HKEY_USERS"); protected final String path; private MemorySegment handle; @@ -197,7 +197,7 @@ private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed try (var dataArena = Arena.ofConfined()) { var lpData = dataArena.allocate(bufferSize); - int result = winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); + int result = Winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); if (result == ERROR_MORE_DATA()) { throw new BufferTooSmallException(); } else if (result != ERROR_SUCCESS()) { @@ -228,7 +228,7 @@ public void setStringValue(String name, String data, boolean isExpandable) throw try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); - setValue(lpValueName, lpValueData, isExpandable? REG_EXPAND_SZ() : winreg_h.REG_SZ()); + setValue(lpValueName, lpValueData, isExpandable? REG_EXPAND_SZ() : Winreg_h.REG_SZ()); } } @@ -236,7 +236,7 @@ public void setDwordValue(String name, int data) { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); var lpValueData = arena.allocateFrom(ValueLayout.JAVA_INT, data); - setValue(lpValueName, lpValueData, winreg_h.REG_DWORD()); + setValue(lpValueName, lpValueData, Winreg_h.REG_DWORD()); } } @@ -245,7 +245,7 @@ private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags throw new IllegalArgumentException("Data must be smaller than " + MAX_DATA_SIZE + "bytes."); } - int result = winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, dwFlags, data , (int) data.byteSize()); + int result = Winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, dwFlags, data , (int) data.byteSize()); if (result != ERROR_SUCCESS()) { throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); } @@ -256,7 +256,7 @@ private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags public void deleteValue(String valueName) { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(valueName, StandardCharsets.UTF_16LE); - int result = winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); + int result = Winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); if (result != ERROR_SUCCESS()) { throw new RuntimeException("Deleting Key failed with error code " + result); } @@ -277,7 +277,7 @@ public void deleteAllValuesAndSubtrees() { private void deleteValuesAndSubtrees(String subkey) { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); - int result = winreg_h.RegDeleteTreeW(handle, lpSubkey); + int result = Winreg_h.RegDeleteTreeW(handle, lpSubkey); if (result != ERROR_SUCCESS()) { throw new RuntimeException("Deleting Key failed with error code " + result); } @@ -287,7 +287,7 @@ private void deleteValuesAndSubtrees(String subkey) { @Override public synchronized void close() { if (!isClosed) { - int result = winreg_h.RegCloseKey(handle); + int result = Winreg_h.RegCloseKey(handle); if (result != ERROR_SUCCESS()) { throw new RuntimeException("Closing key %s failed with error code %d.".formatted(path, result)); } diff --git a/src/main/resources/ktmw32_helper.h b/src/main/resources/ktmw32_helper.h new file mode 100644 index 0000000..8072afc --- /dev/null +++ b/src/main/resources/ktmw32_helper.h @@ -0,0 +1,2 @@ +#include +#include From 16cc44ca4e96626c58caeee2431dbf8f595fbcb8 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 12:29:03 +0200 Subject: [PATCH 36/78] add logging --- .../filemanagersidebar/ExplorerSidebarService.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index e12b25f..bd75112 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -4,6 +4,8 @@ import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.filemanagersidebar.SidebarService; import org.cryptomator.windows.common.WindowsRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.file.Path; import java.util.UUID; @@ -17,11 +19,13 @@ @OperatingSystem(OperatingSystem.Value.WINDOWS) public class ExplorerSidebarService implements SidebarService { + private static final Logger LOG = LoggerFactory.getLogger(ExplorerSidebarService.class); + @Override public SidebarEntry add(Path mountpoint) { var entryName = "Vault - " + mountpoint.getFileName().toString(); var clsid = "{" + UUID.randomUUID() + "}"; - System.out.println(clsid); + LOG.debug("Creating sidebar entry with CLSID {}", clsid); //1. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d "MyCloudStorageApp" /f try (var t = WindowsRegistry.startTransaction()) { try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true)) { @@ -65,17 +69,20 @@ public SidebarEntry add(Path mountpoint) { //10. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\ShellFolder /v Attributes /t REG_DWORD /d 0xF080004D /f shellFolderKey.setDwordValue("Attributes", 0xF080004D); } + LOG.trace("Created RegKey {} and subkeys, including Values", baseKey); } //11. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d MyCloudStorageApp /f var nameSpaceSubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; try (var nameSpaceKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, nameSpaceSubKey, true)) { nameSpaceKey.setStringValue("", entryName, false); + LOG.trace("Created RegKey {} and setting default value", nameSpaceKey); } //12. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel /v {0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /t REG_DWORD /d 0x1 /f try (var newStartPanelKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", true)) { newStartPanelKey.setDwordValue(clsid, 0x1); + LOG.trace("Set value {} for RegKey {}", clsid, newStartPanelKey); } t.commit(); } @@ -86,18 +93,22 @@ record ExplorerSidebarEntry(String clsid) implements SidebarEntry { @Override public void remove() { + LOG.debug("Removing sidebar entry with CLSID {}", clsid); try (var t = WindowsRegistry.startTransaction()) { //undo step 11. var nameSpaceSubkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, nameSpaceSubkey); + LOG.trace("Removing RegKey {}", nameSpaceSubkey); //undo step 12. try (var nameSpaceKey = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel")) { + LOG.trace("Removing Value {} of RegKey {}", clsid, nameSpaceKey); nameSpaceKey.deleteValue(clsid); } //undo everything else try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid), true)) { + LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey); baseKey.deleteAllValuesAndSubtrees(); } t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid)); From bf70fe99f4a0b12cdee92155f05cac0949a1802f Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 12:31:53 +0200 Subject: [PATCH 37/78] refactor registry key to own file --- .../windows/common/RegistryKey.java | 165 ++++++++++++++++++ .../windows/common/WindowsRegistry.java | 154 ---------------- .../ExplorerSidebarService.java | 16 +- .../windows/common/WindowsRegistryIT.java | 43 +++-- 4 files changed, 194 insertions(+), 184 deletions(-) create mode 100644 src/main/java/org/cryptomator/windows/common/RegistryKey.java diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java new file mode 100644 index 0000000..1a61ce5 --- /dev/null +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -0,0 +1,165 @@ +package org.cryptomator.windows.common; + +import org.cryptomator.windows.capi.winreg.Winreg_h; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.nio.charset.StandardCharsets; + +import static java.lang.foreign.MemorySegment.NULL; +import static org.cryptomator.windows.capi.common.Windows_h.ERROR_MORE_DATA; +import static org.cryptomator.windows.capi.common.Windows_h.ERROR_SUCCESS; +import static org.cryptomator.windows.capi.winreg.Winreg_h.REG_EXPAND_SZ; +import static org.cryptomator.windows.capi.winreg.Winreg_h.RRF_RT_REG_DWORD; +import static org.cryptomator.windows.capi.winreg.Winreg_h.RRF_RT_REG_EXPAND_SZ; +import static org.cryptomator.windows.capi.winreg.Winreg_h.RRF_RT_REG_SZ; + +public class RegistryKey implements AutoCloseable { + + public static final RegistryKey HKEY_CURRENT_USER = new RegistryKey(Winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); + public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryKey(Winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); + public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryKey(Winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); + public static final RegistryKey HKEY_USERS = new RegistryKey(Winreg_h.HKEY_USERS(), "HKEY_USERS"); + + protected final String path; + private MemorySegment handle; + private volatile boolean isClosed = false; + + RegistryKey(MemorySegment handle, String path) { + this.handle = handle; + this.path = path; + } + + //-- GetValue functions -- + + public String getStringValue(String name, boolean isExpandable) throws RuntimeException { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var data = getValue(lpValueName, isExpandable ? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ(), 256L); + return data.getString(0, StandardCharsets.UTF_16LE); + } + } + + public int getDwordValue(String name) { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var data = getValue(lpValueName, RRF_RT_REG_DWORD(), 5L); + return data.get(ValueLayout.JAVA_INT, 0); + } + } + + private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed) throws RuntimeException { + long bufferSize = seed - 1; + try (var arena = Arena.ofConfined()) { + var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, (int) bufferSize); + + try (var dataArena = Arena.ofConfined()) { + var lpData = dataArena.allocate(bufferSize); + + int result = Winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); + if (result == ERROR_MORE_DATA()) { + throw new BufferTooSmallException(); + } else if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Getting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); + } + + var returnBuffer = Arena.ofAuto().allocate(Integer.toUnsignedLong(lpDataSize.get(ValueLayout.JAVA_INT, 0))); + MemorySegment.copy(lpData, 0L, returnBuffer, 0L, returnBuffer.byteSize()); + return returnBuffer; + } catch (BufferTooSmallException _) { + if (bufferSize <= WindowsRegistry.MAX_DATA_SIZE) { + return getValue(lpValueName, dwFlags, seed << 1); + } else { + throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); + } + } + } + } + + private static class BufferTooSmallException extends RuntimeException { + + } + + //-- SetValue functions -- + + public void setStringValue(String name, String data, boolean isExpandable) throws RuntimeException { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); + setValue(lpValueName, lpValueData, isExpandable ? REG_EXPAND_SZ() : Winreg_h.REG_SZ()); + } + } + + public void setDwordValue(String name, int data) { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var lpValueData = arena.allocateFrom(ValueLayout.JAVA_INT, data); + setValue(lpValueName, lpValueData, Winreg_h.REG_DWORD()); + } + } + + private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags) { + if (data.byteSize() > WindowsRegistry.MAX_DATA_SIZE) { + throw new IllegalArgumentException("Data must be smaller than " + WindowsRegistry.MAX_DATA_SIZE + "bytes."); + } + + int result = Winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, dwFlags, data, (int) data.byteSize()); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); + } + } + + //-- delete operations + + public void deleteValue(String valueName) { + try (var arena = Arena.ofConfined()) { + var lpValueName = arena.allocateFrom(valueName, StandardCharsets.UTF_16LE); + int result = Winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Deleting Key failed with error code " + result); + } + } + } + + public void deleteSubtree(String subkey) { + if (subkey == null || subkey.isBlank()) { + throw new IllegalArgumentException("Subkey must not be empty"); + } + deleteValuesAndSubtrees(subkey); + } + + public void deleteAllValuesAndSubtrees() { + deleteValuesAndSubtrees(""); + } + + private void deleteValuesAndSubtrees(String subkey) { + try (var arena = Arena.ofConfined()) { + var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); + int result = Winreg_h.RegDeleteTreeW(handle, lpSubkey); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Deleting Key failed with error code " + result); + } + } + } + + @Override + public synchronized void close() { + if (!isClosed) { + int result = Winreg_h.RegCloseKey(handle); + if (result != ERROR_SUCCESS()) { + throw new RuntimeException("Closing key %s failed with error code %d.".formatted(path, result)); + } + handle = NULL; + isClosed = true; + } + } + + MemorySegment getHandle() { + return handle; + } + + public String getPath() { + return path; + } +} diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 272ae14..0fea9a8 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -7,11 +7,9 @@ import java.lang.foreign.AddressLayout; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.charset.StandardCharsets; import static java.lang.foreign.MemorySegment.NULL; -import static org.cryptomator.windows.capi.common.Windows_h.ERROR_MORE_DATA; import static org.cryptomator.windows.capi.common.Windows_h.ERROR_SUCCESS; import static org.cryptomator.windows.capi.common.Windows_h.INVALID_HANDLE_VALUE; import static org.cryptomator.windows.capi.winreg.Winreg_h.*; @@ -154,156 +152,4 @@ private synchronized void closeInternal() { } } } - - public static class RegistryKey implements AutoCloseable { - - public static final RegistryKey HKEY_CURRENT_USER = new RegistryKey(Winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); - public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryKey(Winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); - public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryKey(Winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); - public static final RegistryKey HKEY_USERS = new RegistryKey(Winreg_h.HKEY_USERS(), "HKEY_USERS"); - - protected final String path; - private MemorySegment handle; - private volatile boolean isClosed = false; - - RegistryKey(MemorySegment handle, String path) { - this.handle = handle; - this.path = path; - } - - //-- GetValue functions -- - - public String getStringValue(String name, boolean isExpandable) throws RuntimeException { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var data = getValue(lpValueName, isExpandable? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ(), 256L); - return data.getString(0, StandardCharsets.UTF_16LE); - } - } - - public int getDwordValue(String name) { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var data = getValue(lpValueName, RRF_RT_REG_DWORD(), 5L); - return data.get(ValueLayout.JAVA_INT,0); - } - } - - private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed) throws RuntimeException { - long bufferSize = seed - 1; - try (var arena = Arena.ofConfined()) { - var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, (int) bufferSize); - - try (var dataArena = Arena.ofConfined()) { - var lpData = dataArena.allocate(bufferSize); - - int result = Winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); - if (result == ERROR_MORE_DATA()) { - throw new BufferTooSmallException(); - } else if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Getting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); - } - - var returnBuffer = Arena.ofAuto().allocate(Integer.toUnsignedLong(lpDataSize.get(ValueLayout.JAVA_INT, 0))); - MemorySegment.copy(lpData, 0L, returnBuffer, 0L, returnBuffer.byteSize()); - return returnBuffer; - - } catch (BufferTooSmallException _) { - if (bufferSize <= MAX_DATA_SIZE) { - return getValue(lpValueName, dwFlags, seed << 1); - } else { - throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); - } - } - } - } - - private static class BufferTooSmallException extends RuntimeException { - - } - - //-- SetValue functions -- - - public void setStringValue(String name, String data, boolean isExpandable) throws RuntimeException { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); - setValue(lpValueName, lpValueData, isExpandable? REG_EXPAND_SZ() : Winreg_h.REG_SZ()); - } - } - - public void setDwordValue(String name, int data) { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var lpValueData = arena.allocateFrom(ValueLayout.JAVA_INT, data); - setValue(lpValueName, lpValueData, Winreg_h.REG_DWORD()); - } - } - - private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags) { - if (data.byteSize() > MAX_DATA_SIZE) { - throw new IllegalArgumentException("Data must be smaller than " + MAX_DATA_SIZE + "bytes."); - } - - int result = Winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, dwFlags, data , (int) data.byteSize()); - if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); - } - } - - //-- delete operations - - public void deleteValue(String valueName) { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(valueName, StandardCharsets.UTF_16LE); - int result = Winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); - if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Deleting Key failed with error code " + result); - } - } - } - - public void deleteSubtree(String subkey) { - if (subkey == null || subkey.isBlank()) { - throw new IllegalArgumentException("Subkey must not be empty"); - } - deleteValuesAndSubtrees(subkey); - } - - public void deleteAllValuesAndSubtrees() { - deleteValuesAndSubtrees(""); - } - - private void deleteValuesAndSubtrees(String subkey) { - try (var arena = Arena.ofConfined()) { - var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); - int result = Winreg_h.RegDeleteTreeW(handle, lpSubkey); - if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Deleting Key failed with error code " + result); - } - } - } - - @Override - public synchronized void close() { - if (!isClosed) { - int result = Winreg_h.RegCloseKey(handle); - if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Closing key %s failed with error code %d.".formatted(path, result)); - } - handle = NULL; - isClosed = true; - } - } - - MemorySegment getHandle() { - return handle; - } - - public String getPath() { - return path; - } - } - - } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index bd75112..842ec3d 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -3,6 +3,7 @@ import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.filemanagersidebar.SidebarService; +import org.cryptomator.windows.common.RegistryKey; import org.cryptomator.windows.common.WindowsRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +29,7 @@ public SidebarEntry add(Path mountpoint) { LOG.debug("Creating sidebar entry with CLSID {}", clsid); //1. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d "MyCloudStorageApp" /f try (var t = WindowsRegistry.startTransaction()) { - try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true)) { - + try (var baseKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true)) { baseKey.setStringValue("", entryName, false); //2. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\DefaultIcon /ve /t REG_EXPAND_SZ /d %%SystemRoot%%\system32\imageres.dll,-1043 /f @@ -74,13 +74,13 @@ public SidebarEntry add(Path mountpoint) { //11. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d MyCloudStorageApp /f var nameSpaceSubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; - try (var nameSpaceKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, nameSpaceSubKey, true)) { + try (var nameSpaceKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, nameSpaceSubKey, true)) { nameSpaceKey.setStringValue("", entryName, false); LOG.trace("Created RegKey {} and setting default value", nameSpaceKey); } //12. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel /v {0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /t REG_DWORD /d 0x1 /f - try (var newStartPanelKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", true)) { + try (var newStartPanelKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", true)) { newStartPanelKey.setDwordValue(clsid, 0x1); LOG.trace("Set value {} for RegKey {}", clsid, newStartPanelKey); } @@ -97,21 +97,21 @@ public void remove() { try (var t = WindowsRegistry.startTransaction()) { //undo step 11. var nameSpaceSubkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; - t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, nameSpaceSubkey); LOG.trace("Removing RegKey {}", nameSpaceSubkey); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, nameSpaceSubkey); //undo step 12. - try (var nameSpaceKey = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel")) { + try (var nameSpaceKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel")) { LOG.trace("Removing Value {} of RegKey {}", clsid, nameSpaceKey); nameSpaceKey.deleteValue(clsid); } //undo everything else - try (var baseKey = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid), true)) { + try (var baseKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid))) { LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey); baseKey.deleteAllValuesAndSubtrees(); } - t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid)); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid)); t.commit(); } } diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index cba1b0a..531bc9c 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -1,7 +1,6 @@ package org.cryptomator.windows.common; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; @@ -17,7 +16,7 @@ public class WindowsRegistryIT { public void testOpenNotExisting() { Assertions.assertThrows(RuntimeException.class, () -> { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist")) { } }); @@ -28,12 +27,12 @@ public void testOpenNotExisting() { @Order(1) public void testCreateNotExistingRollback() { try (var t = WindowsRegistry.startTransaction(); - var k = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { + var k = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { } Assertions.assertThrows(RuntimeException.class, () -> { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } }); } @@ -43,12 +42,12 @@ public void testCreateNotExistingRollback() { @Order(2) public void testCreateNotExistingCommit() { try (var t = WindowsRegistry.startTransaction(); - var k = t.createRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { + var k = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { t.commit(); } try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } } @@ -57,14 +56,14 @@ public void testCreateNotExistingCommit() { @Order(3) public void testOpenSetValueRollback() { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.setStringValue("exampleStringValue", "In Progress", false); } //TODO: be more specific in the assertion Assertions.assertThrows(RuntimeException.class, () -> { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.getStringValue("exampleStringValue", false); } }); @@ -75,14 +74,14 @@ public void testOpenSetValueRollback() { @Order(4) public void testOpenSetValueCommit() { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.setStringValue("exampleStringValue", "In Progress", false); k.setDwordValue("exampleDwordValue", 0x42); t.commit(); } try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { var stringData = k.getStringValue("exampleStringValue", false); var binaryData = k.getDwordValue("exampleDwordValue"); Assertions.assertEquals("In Progress", stringData); @@ -95,12 +94,12 @@ public void testOpenSetValueCommit() { @Order(5) public void testOpenDeleteValueRollback() { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.deleteValue("exampleDwordValue"); } try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { var data = k.getDwordValue("exampleDwordValue"); Assertions.assertEquals(0x42, data); } @@ -111,13 +110,13 @@ public void testOpenDeleteValueRollback() { @Order(6) public void testOpenDeleteValueCommit() { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.deleteValue("exampleDwordValue"); t.commit(); } try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { Assertions.assertThrows(RuntimeException.class, () -> { k.getDwordValue("exampleDwordValue"); }); @@ -129,13 +128,13 @@ public void testOpenDeleteValueCommit() { @Order(7) public void testOpenDeleteTreeRollback() { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.deleteAllValuesAndSubtrees(); } Assertions.assertDoesNotThrow(() -> { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.getStringValue("exampleStringValue", false); } }); @@ -146,14 +145,14 @@ public void testOpenDeleteTreeRollback() { @Order(8) public void testOpenDeleteTreeCommit() { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); var subk = t.createRegKey(k, "subkey", true)) { k.deleteAllValuesAndSubtrees(); t.commit(); } try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { Assertions.assertThrows(RuntimeException.class, () -> t.openRegKey(k, "subkey")); Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("exampleStringValue", false)); @@ -165,11 +164,11 @@ public void testOpenDeleteTreeCommit() { @Order(9) public void testDeleteRollback() { try (var t = WindowsRegistry.startTransaction()) { - t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); } try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } } @@ -178,13 +177,13 @@ public void testDeleteRollback() { @Order(10) public void testDeleteCommit() { try (var t = WindowsRegistry.startTransaction()) { - t.deleteRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); t.commit(); } Assertions.assertThrows(RuntimeException.class, () -> { try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(WindowsRegistry.RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } }); } From c2b909cf085c9832f7fccc58cbc234e9f72fbfc8 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 12:32:32 +0200 Subject: [PATCH 38/78] adjust impl to changed interface --- .../ExplorerSidebarService.java | 26 ++++++++++++++++--- .../ExplorerSidebarServiceIT.java | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index 842ec3d..a2cf9d0 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -23,8 +23,14 @@ public class ExplorerSidebarService implements SidebarService { private static final Logger LOG = LoggerFactory.getLogger(ExplorerSidebarService.class); @Override - public SidebarEntry add(Path mountpoint) { - var entryName = "Vault - " + mountpoint.getFileName().toString(); + public SidebarEntry add(String displayName, Path mountpoint) { + if (displayName == null) { + throw new IllegalArgumentException("Parameter 'displayname' must not be null."); + } + if (mountpoint == null) { + throw new IllegalArgumentException("Parameter 'mountpoint' must not be null."); + } + var entryName = "Vault - " + displayName; var clsid = "{" + UUID.randomUUID() + "}"; LOG.debug("Creating sidebar entry with CLSID {}", clsid); //1. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d "MyCloudStorageApp" /f @@ -89,10 +95,21 @@ public SidebarEntry add(Path mountpoint) { return new ExplorerSidebarEntry(clsid); } - record ExplorerSidebarEntry(String clsid) implements SidebarEntry { + static class ExplorerSidebarEntry implements SidebarEntry { + + private final String clsid; + private volatile boolean isClosed = false; + + private ExplorerSidebarEntry(String clsid) { + this.clsid = clsid; + } @Override - public void remove() { + public synchronized void remove() { + if (isClosed) { + return; + } + LOG.debug("Removing sidebar entry with CLSID {}", clsid); try (var t = WindowsRegistry.startTransaction()) { //undo step 11. @@ -114,6 +131,7 @@ public void remove() { t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid)); t.commit(); } + isClosed = true; } } diff --git a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java index 3f39017..fceac30 100644 --- a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java +++ b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java @@ -23,7 +23,7 @@ public void testExplorerSidebarIntegration() throws IOException, InterruptedExce Files.createFile(p.resolve("firstLevel.file")); var sub = Files.createDirectory(p.resolve("subdir")); Files.createFile(sub.resolve("secondLevel.file")); - var entry = new ExplorerSidebarService().add(p); + var entry = new ExplorerSidebarService().add("integration-win-tempDir",p); Thread.sleep(Duration.ofSeconds(20)); From 02d88de972797e0fc3219bb17396aea334739403 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 16:39:53 +0200 Subject: [PATCH 39/78] add specific exception when using the Windows Api --- .../windows/common/WindowsException.java | 15 +++++ .../windows/common/WindowsRegistry.java | 33 +++++----- .../ExplorerSidebarService.java | 6 +- .../windows/common/WindowsRegistryIT.java | 60 ++++++++++++------- 4 files changed, 75 insertions(+), 39 deletions(-) create mode 100644 src/main/java/org/cryptomator/windows/common/WindowsException.java diff --git a/src/main/java/org/cryptomator/windows/common/WindowsException.java b/src/main/java/org/cryptomator/windows/common/WindowsException.java new file mode 100644 index 0000000..25801a0 --- /dev/null +++ b/src/main/java/org/cryptomator/windows/common/WindowsException.java @@ -0,0 +1,15 @@ +package org.cryptomator.windows.common; + +public class WindowsException extends Exception { + + private final int systemErrorCode; + + public WindowsException(String method, int systemErrorCode) { + super("Method %s returned system error code %d".formatted(method, systemErrorCode)); + this.systemErrorCode = systemErrorCode; + } + + public int getSystemErrorCode() { + return systemErrorCode; + } +} diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 0fea9a8..3daa19a 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -18,13 +18,12 @@ public class WindowsRegistry { public static final long MAX_DATA_SIZE = (1L << 32) - 1L; //unsinged integer - public static RegistryTransaction startTransaction() { + public static RegistryTransaction startTransaction() throws WindowsException { var transactionHandle = Ktmw32_h.CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL); if (transactionHandle.address() == INVALID_HANDLE_VALUE().address()) { //GetLastError() int error = Windows_h.GetLastError(); - //TODO: get error message directly? https://learn.microsoft.com/en-us/windows/win32/Debug/retrieving-the-last-error-code - throw new RuntimeException("Native code returned win32 error code " + error); + throw new WindowsException("ktmw32.h:CreateTransaction", error); } return new RegistryTransaction(transactionHandle); } @@ -39,7 +38,7 @@ public static class RegistryTransaction implements AutoCloseable { this.transactionHandle = handle; } - public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolatile) { + public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolatile) throws WindowsException { var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); @@ -57,13 +56,13 @@ public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolati NULL ); if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Creating Key failed with error code " + result); + throw new WindowsException("winreg.h:RegCreateKeyTransactedW", result); } return new RegistryKey(pointerToResultKey.get(C_POINTER, 0), key.getPath() + "\\" + subkey); } } - public RegistryKey openRegKey(RegistryKey key, String subkey) { + public RegistryKey openRegKey(RegistryKey key, String subkey) throws WindowsException { var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); @@ -77,13 +76,13 @@ public RegistryKey openRegKey(RegistryKey key, String subkey) { NULL ); if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Opening key failed with error code " + result); + throw new WindowsException("winreg.h:RegOpenKeyTransactedW", result); } return new RegistryKey(pointerToResultKey.get(C_POINTER, 0), key.getPath() + "\\" + subkey); } } - public void deleteRegKey(RegistryKey key, String subkey) { + public void deleteRegKey(RegistryKey key, String subkey) throws WindowsException { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); int result = Winreg_h.RegDeleteKeyTransactedW( @@ -101,27 +100,27 @@ public void deleteRegKey(RegistryKey key, String subkey) { } - public synchronized void commit() { + public synchronized void commit() throws WindowsException { if (isClosed) { throw new IllegalStateException("Transaction already closed"); } int result = Ktmw32_h.CommitTransaction(transactionHandle); if (result == 0) { int error = Windows_h.GetLastError(); - throw new RuntimeException("Native code returned win32 error code " + error); + throw new WindowsException("ktmw32.h:CommitTransaction", error); } isCommited = true; closeInternal(); } - public synchronized void rollback() { + public synchronized void rollback() throws WindowsException { if (isClosed) { throw new IllegalStateException("Transaction already closed"); } int result = Ktmw32_h.RollbackTransaction(transactionHandle); if (result == 0) { int error = Windows_h.GetLastError(); - throw new RuntimeException("Native code returned win32 error code " + error); + throw new WindowsException("ktmw32.h:CommitTransaction", error); } closeInternal(); } @@ -129,23 +128,23 @@ public synchronized void rollback() { ; @Override - public synchronized void close() throws RuntimeException { + public synchronized void close() throws WindowsException { if (!isCommited) { try { rollback(); - } catch (RuntimeException e) { - System.err.printf("Failed to rollback uncommited transaction on close. Exception message: %s%n", e.getMessage()); + } catch (WindowsException e) { + System.err.printf("Failed to rollback uncommited transaction on close: %s%n", e.getMessage()); } } closeInternal(); } - private synchronized void closeInternal() { + private synchronized void closeInternal() throws WindowsException { if (!isClosed) { int result = Windows_h.CloseHandle(transactionHandle); if (result == 0) { int error = Windows_h.GetLastError(); - throw new RuntimeException("Native code returned win32 error code " + error); + throw new WindowsException("Windows.h:CloseHandle", error); } transactionHandle = null; isClosed = true; diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index a2cf9d0..48e1dab 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -91,6 +91,8 @@ public SidebarEntry add(String displayName, Path mountpoint) { LOG.trace("Set value {} for RegKey {}", clsid, newStartPanelKey); } t.commit(); + } catch (WindowsException e) { + throw new RuntimeException("Adding entry to Explorer via Windows registry failed.",e); } return new ExplorerSidebarEntry(clsid); } @@ -130,8 +132,10 @@ public synchronized void remove() { } t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid)); t.commit(); + isClosed = true; + } catch (WindowsException e) { + LOG.error("Removing explorer sidebar entry with CLSID {} failed",clsid,e); } - isClosed = true; } } diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index 531bc9c..26828f5 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import static org.cryptomator.windows.capi.common.Windows_h.ERROR_FILE_NOT_FOUND; + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class WindowsRegistryIT { @@ -14,33 +16,46 @@ public class WindowsRegistryIT { @DisplayName("Open not exisitig key fails") @Order(1) public void testOpenNotExisting() { - Assertions.assertThrows(RuntimeException.class, () -> { - try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist")) { + var winException = Assertions.assertThrows(WindowsException.class, () -> { + try (var t = WindowsRegistry.startTransaction()) { + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist"); + } + }); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), winException.getSystemErrorCode()); + } + @Test + @DisplayName("Deleting not exisitig key fails") + @Order(1) + public void testDeleteNotExisting() { + var winException = Assertions.assertThrows(WindowsException.class, () -> { + try (var t = WindowsRegistry.startTransaction()) { + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist"); } }); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), winException.getSystemErrorCode()); } @Test @DisplayName("Create and no commit leads to rollback") @Order(1) - public void testCreateNotExistingRollback() { + public void testCreateNotExistingRollback() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { } - Assertions.assertThrows(RuntimeException.class, () -> { + var winException = Assertions.assertThrows(WindowsException.class, () -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } }); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), winException.getSystemErrorCode()); } @Test @DisplayName("Creating, commit, open succeeds") @Order(2) - public void testCreateNotExistingCommit() { + public void testCreateNotExistingCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { t.commit(); @@ -54,25 +69,25 @@ public void testCreateNotExistingCommit() { @Test @DisplayName("Open, setValue, rollback") @Order(3) - public void testOpenSetValueRollback() { + public void testOpenSetValueRollback() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.setStringValue("exampleStringValue", "In Progress", false); } //TODO: be more specific in the assertion - Assertions.assertThrows(RuntimeException.class, () -> { - try (var t = WindowsRegistry.startTransaction(); - var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + Assertions.assertThrows(RuntimeException.class, () -> { k.getStringValue("exampleStringValue", false); - } - }); + }); + } } @Test @DisplayName("Open, setValue, commit") @Order(4) - public void testOpenSetValueCommit() { + public void testOpenSetValueCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.setStringValue("exampleStringValue", "In Progress", false); @@ -92,7 +107,7 @@ public void testOpenSetValueCommit() { @Test @DisplayName("Open, deleteValue, rollback") @Order(5) - public void testOpenDeleteValueRollback() { + public void testOpenDeleteValueRollback() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.deleteValue("exampleDwordValue"); @@ -108,7 +123,7 @@ public void testOpenDeleteValueRollback() { @Test @DisplayName("Open, deleteValue, commit") @Order(6) - public void testOpenDeleteValueCommit() { + public void testOpenDeleteValueCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.deleteValue("exampleDwordValue"); @@ -117,6 +132,7 @@ public void testOpenDeleteValueCommit() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + //TODO: check for system error code Assertions.assertThrows(RuntimeException.class, () -> { k.getDwordValue("exampleDwordValue"); }); @@ -126,7 +142,7 @@ public void testOpenDeleteValueCommit() { @Test @DisplayName("Open, deleteValuesAndSubtrees, rollback") @Order(7) - public void testOpenDeleteTreeRollback() { + public void testOpenDeleteTreeRollback() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { k.deleteAllValuesAndSubtrees(); @@ -143,7 +159,7 @@ public void testOpenDeleteTreeRollback() { @Test @DisplayName("Open, deleteValuesAndSubtrees, commit") @Order(8) - public void testOpenDeleteTreeCommit() { + public void testOpenDeleteTreeCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); var subk = t.createRegKey(k, "subkey", true)) { @@ -154,7 +170,8 @@ public void testOpenDeleteTreeCommit() { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - Assertions.assertThrows(RuntimeException.class, () -> t.openRegKey(k, "subkey")); + //TODO: check for system error code + Assertions.assertThrows(WindowsException.class, () -> t.openRegKey(k, "subkey")); Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("exampleStringValue", false)); } } @@ -162,7 +179,7 @@ public void testOpenDeleteTreeCommit() { @Test @DisplayName("Delete, rollback") @Order(9) - public void testDeleteRollback() { + public void testDeleteRollback() throws WindowsException { try (var t = WindowsRegistry.startTransaction()) { t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); } @@ -175,13 +192,14 @@ public void testDeleteRollback() { @Test @DisplayName("Delete, commit") @Order(10) - public void testDeleteCommit() { + public void testDeleteCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction()) { t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); t.commit(); } - Assertions.assertThrows(RuntimeException.class, () -> { + //TODO: check for system error code + Assertions.assertThrows(WindowsException.class, () -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } From 9638cd60fd07c4db7b345da34e4be5c75a23d189 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 16:40:27 +0200 Subject: [PATCH 40/78] add method to ignore not-existing keys on delete --- pom.xml | 1 + .../org/cryptomator/windows/capi/common/Windows_h.java | 9 +++++++++ .../cryptomator/windows/common/WindowsRegistry.java | 10 ++++++++-- .../cryptomator/windows/common/WindowsRegistryIT.java | 9 +++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0a44335..3a21542 100644 --- a/pom.xml +++ b/pom.xml @@ -509,6 +509,7 @@ ERROR_SUCCESS INVALID_HANDLE_VALUE ERROR_SUCCESS + ERROR_FILE_NOT_FOUND ERROR_MORE_DATA diff --git a/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java index 21c163c..43f7ac7 100644 --- a/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java +++ b/src/main/java/org/cryptomator/windows/capi/common/Windows_h.java @@ -202,6 +202,15 @@ public static MemorySegment INVALID_HANDLE_VALUE() { public static int ERROR_SUCCESS() { return ERROR_SUCCESS; } + private static final int ERROR_FILE_NOT_FOUND = (int)2L; + /** + * {@snippet lang=c : + * #define ERROR_FILE_NOT_FOUND 2 + * } + */ + public static int ERROR_FILE_NOT_FOUND() { + return ERROR_FILE_NOT_FOUND; + } private static final int ERROR_MORE_DATA = (int)234L; /** * {@snippet lang=c : diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 3daa19a..e279e64 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -10,6 +10,7 @@ import java.nio.charset.StandardCharsets; import static java.lang.foreign.MemorySegment.NULL; +import static org.cryptomator.windows.capi.common.Windows_h.ERROR_FILE_NOT_FOUND; import static org.cryptomator.windows.capi.common.Windows_h.ERROR_SUCCESS; import static org.cryptomator.windows.capi.common.Windows_h.INVALID_HANDLE_VALUE; import static org.cryptomator.windows.capi.winreg.Winreg_h.*; @@ -83,6 +84,10 @@ public RegistryKey openRegKey(RegistryKey key, String subkey) throws WindowsExce } public void deleteRegKey(RegistryKey key, String subkey) throws WindowsException { + deleteRegKey(key, subkey, false); + } + + public void deleteRegKey(RegistryKey key, String subkey, boolean ignoreNotExisting) throws WindowsException { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); int result = Winreg_h.RegDeleteKeyTransactedW( @@ -93,8 +98,9 @@ public void deleteRegKey(RegistryKey key, String subkey) throws WindowsException transactionHandle, NULL ); - if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Opening key failed with error code " + result); + if( result != ERROR_SUCCESS() // + && !(result == ERROR_FILE_NOT_FOUND() && ignoreNotExisting)) { + throw new WindowsException("winreg.h:RegDeleteKeyTransactedW", result); } } } diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index 26828f5..c67bf6d 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -36,6 +36,15 @@ public void testDeleteNotExisting() { Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), winException.getSystemErrorCode()); } + @Test + @DisplayName("Deleting key, ingoring not-existence succeeds") + @Order(1) + public void testDeleteIgnoreNotExisting() throws WindowsException { + try (var t = WindowsRegistry.startTransaction()) { + Assertions.assertDoesNotThrow(() -> t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist", true)); + } + } + @Test @DisplayName("Create and no commit leads to rollback") @Order(1) From f5237aa49742e8dff601d7cf7d162e969e1fa13f Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 16:40:58 +0200 Subject: [PATCH 41/78] adjust to new api --- .../filemanagersidebar/ExplorerSidebarService.java | 10 ++++++---- .../filemanagersidebar/ExplorerSidebarServiceIT.java | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index 48e1dab..80500c4 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -3,7 +3,9 @@ import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.filemanagersidebar.SidebarService; +import org.cryptomator.integrations.filemanagersidebar.SidebarServiceException; import org.cryptomator.windows.common.RegistryKey; +import org.cryptomator.windows.common.WindowsException; import org.cryptomator.windows.common.WindowsRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,12 +25,12 @@ public class ExplorerSidebarService implements SidebarService { private static final Logger LOG = LoggerFactory.getLogger(ExplorerSidebarService.class); @Override - public SidebarEntry add(String displayName, Path mountpoint) { + public SidebarEntry add(Path target, String displayName) throws SidebarServiceException { if (displayName == null) { throw new IllegalArgumentException("Parameter 'displayname' must not be null."); } - if (mountpoint == null) { - throw new IllegalArgumentException("Parameter 'mountpoint' must not be null."); + if (target == null) { + throw new IllegalArgumentException("Parameter 'target' must not be null."); } var entryName = "Vault - " + displayName; var clsid = "{" + UUID.randomUUID() + "}"; @@ -64,7 +66,7 @@ public SidebarEntry add(String displayName, Path mountpoint) { initPropertyBagKey.setDwordValue("Attributes", 0x411); //8. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\Instance\InitPropertyBag /v TargetFolderPath /t REG_EXPAND_SZ /d %%USERPROFILE%%\MyCloudStorageApp /f - initPropertyBagKey.setStringValue("TargetFolderPath", mountpoint.toString(), false); + initPropertyBagKey.setStringValue("TargetFolderPath", target.toString(), false); } } diff --git a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java index fceac30..f2defc0 100644 --- a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java +++ b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java @@ -1,5 +1,6 @@ package org.cryptomator.filemanagersidebar; +import org.cryptomator.integrations.filemanagersidebar.SidebarServiceException; import org.cryptomator.windows.filemanagersidebar.ExplorerSidebarService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -17,13 +18,13 @@ public class ExplorerSidebarServiceIT { @Test @DisplayName("Integrates a temp dir for 20s into the file explorer sidebar") - public void testExplorerSidebarIntegration() throws IOException, InterruptedException { + public void testExplorerSidebarIntegration() throws IOException, InterruptedException, SidebarServiceException { var p = base.resolve("integration-win-testVault"); Files.createDirectory(p); Files.createFile(p.resolve("firstLevel.file")); var sub = Files.createDirectory(p.resolve("subdir")); Files.createFile(sub.resolve("secondLevel.file")); - var entry = new ExplorerSidebarService().add("integration-win-tempDir",p); + var entry = new ExplorerSidebarService().add(p, "integration-win-tempDir"); Thread.sleep(Duration.ofSeconds(20)); From 7199e747ba5501f87bfaa735fca0756a58661dc4 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 16:43:07 +0200 Subject: [PATCH 42/78] on sidebar entry removal, ignore not-existing keys (to be deleted anyway) --- .../windows/filemanagersidebar/ExplorerSidebarService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index 80500c4..a85fee4 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -119,7 +119,7 @@ public synchronized void remove() { //undo step 11. var nameSpaceSubkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; LOG.trace("Removing RegKey {}", nameSpaceSubkey); - t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, nameSpaceSubkey); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, nameSpaceSubkey, true); //undo step 12. try (var nameSpaceKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel")) { @@ -132,7 +132,7 @@ public synchronized void remove() { LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey); baseKey.deleteAllValuesAndSubtrees(); } - t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid)); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid), true); t.commit(); isClosed = true; } catch (WindowsException e) { From cdb7286a30805b3015bd5bb287cb95bcb0f49625 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 17:21:35 +0200 Subject: [PATCH 43/78] more fine grained error throwing --- .../windows/common/RegistryKey.java | 32 +++++++++---------- .../windows/common/RegistryKeyException.java | 11 +++++++ .../common/RegistryValueException.java | 8 +++++ .../windows/common/WindowsRegistry.java | 14 ++++---- .../windows/common/WindowsRegistryIT.java | 24 +++++++------- 5 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 src/main/java/org/cryptomator/windows/common/RegistryKeyException.java create mode 100644 src/main/java/org/cryptomator/windows/common/RegistryValueException.java diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index 1a61ce5..5171ee0 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -22,7 +22,7 @@ public class RegistryKey implements AutoCloseable { public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryKey(Winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); public static final RegistryKey HKEY_USERS = new RegistryKey(Winreg_h.HKEY_USERS(), "HKEY_USERS"); - protected final String path; + private final String path; private MemorySegment handle; private volatile boolean isClosed = false; @@ -33,7 +33,7 @@ public class RegistryKey implements AutoCloseable { //-- GetValue functions -- - public String getStringValue(String name, boolean isExpandable) throws RuntimeException { + public String getStringValue(String name, boolean isExpandable) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); var data = getValue(lpValueName, isExpandable ? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ(), 256L); @@ -41,7 +41,7 @@ public String getStringValue(String name, boolean isExpandable) throws RuntimeEx } } - public int getDwordValue(String name) { + public int getDwordValue(String name) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); var data = getValue(lpValueName, RRF_RT_REG_DWORD(), 5L); @@ -49,7 +49,7 @@ public int getDwordValue(String name) { } } - private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed) throws RuntimeException { + private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed) throws RegistryValueException { long bufferSize = seed - 1; try (var arena = Arena.ofConfined()) { var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, (int) bufferSize); @@ -61,7 +61,7 @@ private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed if (result == ERROR_MORE_DATA()) { throw new BufferTooSmallException(); } else if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Getting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); + throw new RegistryValueException("winreg_h:RegGetValue", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); } var returnBuffer = Arena.ofAuto().allocate(Integer.toUnsignedLong(lpDataSize.get(ValueLayout.JAVA_INT, 0))); @@ -83,7 +83,7 @@ private static class BufferTooSmallException extends RuntimeException { //-- SetValue functions -- - public void setStringValue(String name, String data, boolean isExpandable) throws RuntimeException { + public void setStringValue(String name, String data, boolean isExpandable) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); var lpValueData = arena.allocateFrom(data, StandardCharsets.UTF_16LE); @@ -91,7 +91,7 @@ public void setStringValue(String name, String data, boolean isExpandable) throw } } - public void setDwordValue(String name, int data) { + public void setDwordValue(String name, int data) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); var lpValueData = arena.allocateFrom(ValueLayout.JAVA_INT, data); @@ -99,46 +99,46 @@ public void setDwordValue(String name, int data) { } } - private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags) { + private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags) throws RegistryValueException { if (data.byteSize() > WindowsRegistry.MAX_DATA_SIZE) { throw new IllegalArgumentException("Data must be smaller than " + WindowsRegistry.MAX_DATA_SIZE + "bytes."); } int result = Winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, dwFlags, data, (int) data.byteSize()); if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Setting value %s for key %s failed with error code %d".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, result)); + throw new RegistryValueException("winreg_h:RegSetKeyValueW", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); } } //-- delete operations - public void deleteValue(String valueName) { + public void deleteValue(String valueName) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(valueName, StandardCharsets.UTF_16LE); int result = Winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Deleting Key failed with error code " + result); + throw new RegistryValueException("winreg_h:RegSetKeyValueW", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); } } } - public void deleteSubtree(String subkey) { + public void deleteSubtree(String subkey) throws RegistryKeyException { if (subkey == null || subkey.isBlank()) { throw new IllegalArgumentException("Subkey must not be empty"); } deleteValuesAndSubtrees(subkey); } - public void deleteAllValuesAndSubtrees() { + public void deleteAllValuesAndSubtrees() throws RegistryKeyException { deleteValuesAndSubtrees(""); } - private void deleteValuesAndSubtrees(String subkey) { + private void deleteValuesAndSubtrees(String subkey) throws RegistryKeyException { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); int result = Winreg_h.RegDeleteTreeW(handle, lpSubkey); if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Deleting Key failed with error code " + result); + throw new RegistryKeyException("winreg.h:RegDeleteTreeW", path + "\\" + lpSubkey, result); } } } @@ -148,7 +148,7 @@ public synchronized void close() { if (!isClosed) { int result = Winreg_h.RegCloseKey(handle); if (result != ERROR_SUCCESS()) { - throw new RuntimeException("Closing key %s failed with error code %d.".formatted(path, result)); + throw new RuntimeException(new RegistryKeyException("winreg.h:RegCloseKey", path, result)); } handle = NULL; isClosed = true; diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKeyException.java b/src/main/java/org/cryptomator/windows/common/RegistryKeyException.java new file mode 100644 index 0000000..453c5b7 --- /dev/null +++ b/src/main/java/org/cryptomator/windows/common/RegistryKeyException.java @@ -0,0 +1,11 @@ +package org.cryptomator.windows.common; + +public class RegistryKeyException extends WindowsException { + + private final String keyPath; + + public RegistryKeyException(String method, String keyPath, int systemErrorCode) { + super(method + "(regKey: "+keyPath+")", systemErrorCode); + this.keyPath = keyPath; + } +} diff --git a/src/main/java/org/cryptomator/windows/common/RegistryValueException.java b/src/main/java/org/cryptomator/windows/common/RegistryValueException.java new file mode 100644 index 0000000..e632224 --- /dev/null +++ b/src/main/java/org/cryptomator/windows/common/RegistryValueException.java @@ -0,0 +1,8 @@ +package org.cryptomator.windows.common; + +public class RegistryValueException extends RegistryKeyException { + + public RegistryValueException(String method, String keyPath, String valueName, int systemErrorCode) { + super(method, keyPath + ", value: "+valueName, systemErrorCode); + } +} diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index e279e64..cd3030e 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -39,7 +39,7 @@ public static class RegistryTransaction implements AutoCloseable { this.transactionHandle = handle; } - public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolatile) throws WindowsException { + public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolatile) throws RegistryKeyException { var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); @@ -57,13 +57,13 @@ public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolati NULL ); if (result != ERROR_SUCCESS()) { - throw new WindowsException("winreg.h:RegCreateKeyTransactedW", result); + throw new RegistryKeyException("winreg.h:RegCreateKeyTransactedW", key.getPath() + "\\" + subkey, result); } return new RegistryKey(pointerToResultKey.get(C_POINTER, 0), key.getPath() + "\\" + subkey); } } - public RegistryKey openRegKey(RegistryKey key, String subkey) throws WindowsException { + public RegistryKey openRegKey(RegistryKey key, String subkey) throws RegistryKeyException { var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); @@ -77,7 +77,7 @@ public RegistryKey openRegKey(RegistryKey key, String subkey) throws WindowsExce NULL ); if (result != ERROR_SUCCESS()) { - throw new WindowsException("winreg.h:RegOpenKeyTransactedW", result); + throw new RegistryKeyException("winreg.h:RegOpenKeyTransactedW", key.getPath() + "\\" + subkey, result); } return new RegistryKey(pointerToResultKey.get(C_POINTER, 0), key.getPath() + "\\" + subkey); } @@ -87,7 +87,7 @@ public void deleteRegKey(RegistryKey key, String subkey) throws WindowsException deleteRegKey(key, subkey, false); } - public void deleteRegKey(RegistryKey key, String subkey, boolean ignoreNotExisting) throws WindowsException { + public void deleteRegKey(RegistryKey key, String subkey, boolean ignoreNotExisting) throws RegistryKeyException { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); int result = Winreg_h.RegDeleteKeyTransactedW( @@ -98,9 +98,9 @@ public void deleteRegKey(RegistryKey key, String subkey, boolean ignoreNotExisti transactionHandle, NULL ); - if( result != ERROR_SUCCESS() // + if (result != ERROR_SUCCESS() // && !(result == ERROR_FILE_NOT_FOUND() && ignoreNotExisting)) { - throw new WindowsException("winreg.h:RegDeleteKeyTransactedW", result); + throw new RegistryKeyException("winreg.h:RegDeleteKeyTransactedW", key.getPath() + "\\" + subkey, result); } } } diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index c67bf6d..aefbdab 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -16,7 +16,7 @@ public class WindowsRegistryIT { @DisplayName("Open not exisitig key fails") @Order(1) public void testOpenNotExisting() { - var winException = Assertions.assertThrows(WindowsException.class, () -> { + var winException = Assertions.assertThrows(RegistryKeyException.class, () -> { try (var t = WindowsRegistry.startTransaction()) { var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist"); } @@ -28,7 +28,7 @@ public void testOpenNotExisting() { @DisplayName("Deleting not exisitig key fails") @Order(1) public void testDeleteNotExisting() { - var winException = Assertions.assertThrows(WindowsException.class, () -> { + var winException = Assertions.assertThrows(RegistryKeyException.class, () -> { try (var t = WindowsRegistry.startTransaction()) { t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "i\\do\\not\\exist"); } @@ -53,7 +53,7 @@ public void testCreateNotExistingRollback() throws WindowsException { var k = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { } - var winException = Assertions.assertThrows(WindowsException.class, () -> { + var winException = Assertions.assertThrows(RegistryKeyException.class, () -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } @@ -84,12 +84,12 @@ public void testOpenSetValueRollback() throws WindowsException { k.setStringValue("exampleStringValue", "In Progress", false); } - //TODO: be more specific in the assertion try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - Assertions.assertThrows(RuntimeException.class, () -> { + var regException = Assertions.assertThrows(RegistryValueException.class, () -> { k.getStringValue("exampleStringValue", false); }); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), regException.getSystemErrorCode()); } } @@ -142,9 +142,10 @@ public void testOpenDeleteValueCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { //TODO: check for system error code - Assertions.assertThrows(RuntimeException.class, () -> { + var regException = Assertions.assertThrows(RegistryValueException.class, () -> { k.getDwordValue("exampleDwordValue"); }); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), regException.getSystemErrorCode()); } } @@ -179,9 +180,10 @@ public void testOpenDeleteTreeCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - //TODO: check for system error code - Assertions.assertThrows(WindowsException.class, () -> t.openRegKey(k, "subkey")); - Assertions.assertThrows(RuntimeException.class, () -> k.getStringValue("exampleStringValue", false)); + var regKeyException = Assertions.assertThrows(RegistryKeyException.class, () -> t.openRegKey(k, "subkey")); + var regValueException = Assertions.assertThrows(RegistryValueException.class, () -> k.getStringValue("exampleStringValue", false)); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), regKeyException.getSystemErrorCode()); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), regValueException.getSystemErrorCode()); } } @@ -207,11 +209,11 @@ public void testDeleteCommit() throws WindowsException { t.commit(); } - //TODO: check for system error code - Assertions.assertThrows(WindowsException.class, () -> { + var regException = Assertions.assertThrows(WindowsException.class, () -> { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { } }); + Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), regException.getSystemErrorCode()); } } From 877665c641e3748356badbacc2e6c29c4c082a64 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 17:32:36 +0200 Subject: [PATCH 44/78] add method to ignore not existing values on deletion --- .../cryptomator/windows/common/RegistryKey.java | 8 +++++++- .../filemanagersidebar/ExplorerSidebarService.java | 2 +- .../windows/common/WindowsRegistryIT.java | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index 5171ee0..37786b9 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -8,6 +8,7 @@ import java.nio.charset.StandardCharsets; import static java.lang.foreign.MemorySegment.NULL; +import static org.cryptomator.windows.capi.common.Windows_h.ERROR_FILE_NOT_FOUND; import static org.cryptomator.windows.capi.common.Windows_h.ERROR_MORE_DATA; import static org.cryptomator.windows.capi.common.Windows_h.ERROR_SUCCESS; import static org.cryptomator.windows.capi.winreg.Winreg_h.REG_EXPAND_SZ; @@ -113,10 +114,15 @@ private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags //-- delete operations public void deleteValue(String valueName) throws RegistryValueException { + deleteValue(valueName, false); + } + + public void deleteValue(String valueName, boolean ignoreNotExisting) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(valueName, StandardCharsets.UTF_16LE); int result = Winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); - if (result != ERROR_SUCCESS()) { + if (result != ERROR_SUCCESS() // + && !(result == ERROR_FILE_NOT_FOUND() && ignoreNotExisting)) { throw new RegistryValueException("winreg_h:RegSetKeyValueW", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); } } diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index a85fee4..f924ebb 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -124,7 +124,7 @@ public synchronized void remove() { //undo step 12. try (var nameSpaceKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel")) { LOG.trace("Removing Value {} of RegKey {}", clsid, nameSpaceKey); - nameSpaceKey.deleteValue(clsid); + nameSpaceKey.deleteValue(clsid, true); } //undo everything else diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index aefbdab..26e96c9 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -12,6 +12,8 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class WindowsRegistryIT { + //independent tests + @Test @DisplayName("Open not exisitig key fails") @Order(1) @@ -61,6 +63,18 @@ public void testCreateNotExistingRollback() throws WindowsException { Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), winException.getSystemErrorCode()); } + @Test + @DisplayName("Deleting not existing Value on ignore does not throw") + @Order(1) + public void testDeleteNotExisingValue() throws WindowsException { + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "")) { + Assertions.assertDoesNotThrow(() -> k.deleteValue("i do not really exist", true)); + } + } + + // sequence required tests + @Test @DisplayName("Creating, commit, open succeeds") @Order(2) From 155a407945740481086c57deb9ed0542a4a85c8c Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 18 Jun 2024 17:32:45 +0200 Subject: [PATCH 45/78] formatting --- .../windows/filemanagersidebar/ExplorerSidebarService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java index f924ebb..ed738e2 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java @@ -94,7 +94,7 @@ public SidebarEntry add(Path target, String displayName) throws SidebarServiceEx } t.commit(); } catch (WindowsException e) { - throw new RuntimeException("Adding entry to Explorer via Windows registry failed.",e); + throw new RuntimeException("Adding entry to Explorer via Windows registry failed.", e); } return new ExplorerSidebarEntry(clsid); } @@ -136,7 +136,7 @@ public synchronized void remove() { t.commit(); isClosed = true; } catch (WindowsException e) { - LOG.error("Removing explorer sidebar entry with CLSID {} failed",clsid,e); + LOG.error("Removing explorer sidebar entry with CLSID {} failed", clsid, e); } } } From 0b9d198257c2aad5f2afaf8449eda9c3d2b50247 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 19 Jun 2024 12:37:43 +0200 Subject: [PATCH 46/78] adjust to changed API --- src/main/java/module-info.java | 4 ++-- .../ExplorerSidebarService.java | 18 +++++++++--------- ...tomator.integrations.sidebar.SidebarService | 1 + .../ExplorerSidebarServiceIT.java | 6 ++++-- 4 files changed, 16 insertions(+), 13 deletions(-) rename src/main/java/org/cryptomator/windows/{filemanagersidebar => sidebar}/ExplorerSidebarService.java (91%) create mode 100644 src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index ce2e1b8..7bed1f5 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,10 +1,10 @@ import org.cryptomator.integrations.autostart.AutoStartProvider; -import org.cryptomator.integrations.filemanagersidebar.SidebarService; +import org.cryptomator.integrations.sidebar.SidebarService; import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.integrations.uiappearance.UiAppearanceProvider; import org.cryptomator.windows.autostart.WindowsAutoStart; -import org.cryptomator.windows.filemanagersidebar.ExplorerSidebarService; +import org.cryptomator.windows.sidebar.ExplorerSidebarService; import org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess; import org.cryptomator.windows.revealpath.ExplorerRevealPathService; import org.cryptomator.windows.uiappearance.WinUiAppearanceProvider; diff --git a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/sidebar/ExplorerSidebarService.java similarity index 91% rename from src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java rename to src/main/java/org/cryptomator/windows/sidebar/ExplorerSidebarService.java index ed738e2..d7224ec 100644 --- a/src/main/java/org/cryptomator/windows/filemanagersidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/sidebar/ExplorerSidebarService.java @@ -1,9 +1,9 @@ -package org.cryptomator.windows.filemanagersidebar; +package org.cryptomator.windows.sidebar; import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; -import org.cryptomator.integrations.filemanagersidebar.SidebarService; -import org.cryptomator.integrations.filemanagersidebar.SidebarServiceException; +import org.cryptomator.integrations.sidebar.SidebarService; +import org.cryptomator.integrations.sidebar.SidebarServiceException; import org.cryptomator.windows.common.RegistryKey; import org.cryptomator.windows.common.WindowsException; import org.cryptomator.windows.common.WindowsRegistry; @@ -14,7 +14,7 @@ import java.util.UUID; /** - * Implementation of the FileManagerSidebarService for Windows Explorer + * Implementation of the {@link SidebarService} for Windows Explorer *

* Based on a Microsoft docs example. */ @@ -94,7 +94,7 @@ public SidebarEntry add(Path target, String displayName) throws SidebarServiceEx } t.commit(); } catch (WindowsException e) { - throw new RuntimeException("Adding entry to Explorer via Windows registry failed.", e); + throw new SidebarServiceException("Adding entry to Explorer sidebar via Windows registry failed.", e); } return new ExplorerSidebarEntry(clsid); } @@ -109,7 +109,7 @@ private ExplorerSidebarEntry(String clsid) { } @Override - public synchronized void remove() { + public synchronized void remove() throws SidebarServiceException { if (isClosed) { return; } @@ -128,15 +128,15 @@ public synchronized void remove() { } //undo everything else - try (var baseKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid))) { + try (var baseKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid)) { LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey); baseKey.deleteAllValuesAndSubtrees(); } - t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{%s}".formatted(clsid), true); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\"+clsid, true); t.commit(); isClosed = true; } catch (WindowsException e) { - LOG.error("Removing explorer sidebar entry with CLSID {} failed", clsid, e); + throw new SidebarServiceException("Removing entry from Explorer sidebar via Windows registry failed.", e); } } } diff --git a/src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService b/src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService new file mode 100644 index 0000000..568d8ad --- /dev/null +++ b/src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService @@ -0,0 +1 @@ +org.cryptomator.windows.sidebar.ExplorerSidebarService \ No newline at end of file diff --git a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java index f2defc0..743a8d3 100644 --- a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java +++ b/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java @@ -1,7 +1,8 @@ package org.cryptomator.filemanagersidebar; -import org.cryptomator.integrations.filemanagersidebar.SidebarServiceException; -import org.cryptomator.windows.filemanagersidebar.ExplorerSidebarService; +import org.cryptomator.integrations.sidebar.SidebarServiceException; +import org.cryptomator.windows.sidebar.ExplorerSidebarService; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -18,6 +19,7 @@ public class ExplorerSidebarServiceIT { @Test @DisplayName("Integrates a temp dir for 20s into the file explorer sidebar") + @Disabled public void testExplorerSidebarIntegration() throws IOException, InterruptedException, SidebarServiceException { var p = base.resolve("integration-win-testVault"); Files.createDirectory(p); From 5a05857371112c7c5af1a59c723abfc006afffb3 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 20 Jun 2024 17:37:06 +0200 Subject: [PATCH 47/78] renaming to quick access --- pom.xml | 4 +-- src/main/java/module-info.java | 6 ++-- .../ExplorerQuickAccessService.java} | 32 +++++++++---------- ...ntegrations.quickaccess.QuickAccessService | 1 + ...omator.integrations.sidebar.SidebarService | 1 - .../ExplorerQuickAccessServiceIT.java} | 11 +++---- 6 files changed, 27 insertions(+), 28 deletions(-) rename src/main/java/org/cryptomator/windows/{sidebar/ExplorerSidebarService.java => quickaccess/ExplorerQuickAccessService.java} (84%) create mode 100644 src/main/resources/META-INF/services/org.cryptomator.integrations.quickaccess.QuickAccessService delete mode 100644 src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService rename src/test/java/org/cryptomator/{filemanagersidebar/ExplorerSidebarServiceIT.java => windows/quickaccess/ExplorerQuickAccessServiceIT.java} (68%) diff --git a/pom.xml b/pom.xml index 3a21542..b6c06aa 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.cryptomator integrations-win - 1.3.0-sidebar + 1.3.0-quickaccess Cryptomator Integrations for Windows Provides optional Windows services used by Cryptomator @@ -37,7 +37,7 @@ 22 - 1.4.0-sidebar + 1.4.0-quickaccess 2.0.13 2.17.1 diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 7bed1f5..e30da94 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,11 +1,11 @@ import org.cryptomator.integrations.autostart.AutoStartProvider; -import org.cryptomator.integrations.sidebar.SidebarService; import org.cryptomator.integrations.keychain.KeychainAccessProvider; +import org.cryptomator.integrations.quickaccess.QuickAccessService; import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.integrations.uiappearance.UiAppearanceProvider; import org.cryptomator.windows.autostart.WindowsAutoStart; -import org.cryptomator.windows.sidebar.ExplorerSidebarService; import org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess; +import org.cryptomator.windows.quickaccess.ExplorerQuickAccessService; import org.cryptomator.windows.revealpath.ExplorerRevealPathService; import org.cryptomator.windows.uiappearance.WinUiAppearanceProvider; @@ -21,5 +21,5 @@ provides KeychainAccessProvider with WindowsProtectedKeychainAccess; provides UiAppearanceProvider with WinUiAppearanceProvider; provides RevealPathService with ExplorerRevealPathService; - provides SidebarService with ExplorerSidebarService; + provides QuickAccessService with ExplorerQuickAccessService; } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/windows/sidebar/ExplorerSidebarService.java b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java similarity index 84% rename from src/main/java/org/cryptomator/windows/sidebar/ExplorerSidebarService.java rename to src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java index d7224ec..8d86281 100644 --- a/src/main/java/org/cryptomator/windows/sidebar/ExplorerSidebarService.java +++ b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java @@ -1,9 +1,9 @@ -package org.cryptomator.windows.sidebar; +package org.cryptomator.windows.quickaccess; import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; -import org.cryptomator.integrations.sidebar.SidebarService; -import org.cryptomator.integrations.sidebar.SidebarServiceException; +import org.cryptomator.integrations.quickaccess.QuickAccessService; +import org.cryptomator.integrations.quickaccess.QuickAccessServiceException; import org.cryptomator.windows.common.RegistryKey; import org.cryptomator.windows.common.WindowsException; import org.cryptomator.windows.common.WindowsRegistry; @@ -14,18 +14,18 @@ import java.util.UUID; /** - * Implementation of the {@link SidebarService} for Windows Explorer + * Implementation of the {@link QuickAccessService} for Windows Explorer *

* Based on a Microsoft docs example. */ @Priority(100) @OperatingSystem(OperatingSystem.Value.WINDOWS) -public class ExplorerSidebarService implements SidebarService { +public class ExplorerQuickAccessService implements QuickAccessService { - private static final Logger LOG = LoggerFactory.getLogger(ExplorerSidebarService.class); + private static final Logger LOG = LoggerFactory.getLogger(ExplorerQuickAccessService.class); @Override - public SidebarEntry add(Path target, String displayName) throws SidebarServiceException { + public QuickAccessEntry add(Path target, String displayName) throws QuickAccessServiceException { if (displayName == null) { throw new IllegalArgumentException("Parameter 'displayname' must not be null."); } @@ -34,7 +34,7 @@ public SidebarEntry add(Path target, String displayName) throws SidebarServiceEx } var entryName = "Vault - " + displayName; var clsid = "{" + UUID.randomUUID() + "}"; - LOG.debug("Creating sidebar entry with CLSID {}", clsid); + LOG.debug("Creating navigation pane entry with CLSID {}", clsid); //1. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d "MyCloudStorageApp" /f try (var t = WindowsRegistry.startTransaction()) { try (var baseKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true)) { @@ -94,27 +94,27 @@ public SidebarEntry add(Path target, String displayName) throws SidebarServiceEx } t.commit(); } catch (WindowsException e) { - throw new SidebarServiceException("Adding entry to Explorer sidebar via Windows registry failed.", e); + throw new QuickAccessServiceException("Adding entry to Explorer navigation pane via Windows registry failed.", e); } - return new ExplorerSidebarEntry(clsid); + return new ExplorerQuickAccessEntry(clsid); } - static class ExplorerSidebarEntry implements SidebarEntry { + static class ExplorerQuickAccessEntry implements QuickAccessService.QuickAccessEntry { private final String clsid; private volatile boolean isClosed = false; - private ExplorerSidebarEntry(String clsid) { + private ExplorerQuickAccessEntry(String clsid) { this.clsid = clsid; } @Override - public synchronized void remove() throws SidebarServiceException { + public synchronized void remove() throws QuickAccessServiceException { if (isClosed) { return; } - LOG.debug("Removing sidebar entry with CLSID {}", clsid); + LOG.debug("Removing navigation pane entry with CLSID {}", clsid); try (var t = WindowsRegistry.startTransaction()) { //undo step 11. var nameSpaceSubkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; @@ -132,11 +132,11 @@ public synchronized void remove() throws SidebarServiceException { LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey); baseKey.deleteAllValuesAndSubtrees(); } - t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\"+clsid, true); + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true); t.commit(); isClosed = true; } catch (WindowsException e) { - throw new SidebarServiceException("Removing entry from Explorer sidebar via Windows registry failed.", e); + throw new QuickAccessServiceException("Removing entry from Explorer navigation pane via Windows registry failed.", e); } } } diff --git a/src/main/resources/META-INF/services/org.cryptomator.integrations.quickaccess.QuickAccessService b/src/main/resources/META-INF/services/org.cryptomator.integrations.quickaccess.QuickAccessService new file mode 100644 index 0000000..512fc9f --- /dev/null +++ b/src/main/resources/META-INF/services/org.cryptomator.integrations.quickaccess.QuickAccessService @@ -0,0 +1 @@ +org.cryptomator.windows.quickaccess.ExplorerQuickAccessService \ No newline at end of file diff --git a/src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService b/src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService deleted file mode 100644 index 568d8ad..0000000 --- a/src/main/resources/META-INF/services/org.cryptomator.integrations.sidebar.SidebarService +++ /dev/null @@ -1 +0,0 @@ -org.cryptomator.windows.sidebar.ExplorerSidebarService \ No newline at end of file diff --git a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java b/src/test/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessServiceIT.java similarity index 68% rename from src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java rename to src/test/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessServiceIT.java index 743a8d3..5db6c01 100644 --- a/src/test/java/org/cryptomator/filemanagersidebar/ExplorerSidebarServiceIT.java +++ b/src/test/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessServiceIT.java @@ -1,7 +1,6 @@ -package org.cryptomator.filemanagersidebar; +package org.cryptomator.windows.quickaccess; -import org.cryptomator.integrations.sidebar.SidebarServiceException; -import org.cryptomator.windows.sidebar.ExplorerSidebarService; +import org.cryptomator.integrations.quickaccess.QuickAccessServiceException; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,7 +11,7 @@ import java.nio.file.Path; import java.time.Duration; -public class ExplorerSidebarServiceIT { +public class ExplorerQuickAccessServiceIT { @TempDir Path base; @@ -20,13 +19,13 @@ public class ExplorerSidebarServiceIT { @Test @DisplayName("Integrates a temp dir for 20s into the file explorer sidebar") @Disabled - public void testExplorerSidebarIntegration() throws IOException, InterruptedException, SidebarServiceException { + public void testExplorerSidebarIntegration() throws IOException, InterruptedException, QuickAccessServiceException { var p = base.resolve("integration-win-testVault"); Files.createDirectory(p); Files.createFile(p.resolve("firstLevel.file")); var sub = Files.createDirectory(p.resolve("subdir")); Files.createFile(sub.resolve("secondLevel.file")); - var entry = new ExplorerSidebarService().add(p, "integration-win-tempDir"); + var entry = new ExplorerQuickAccessService().add(p, "integration-win-tempDir"); Thread.sleep(Duration.ofSeconds(20)); From 88907902b250bfe3a718b25888644dd7263f21e9 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 27 Jun 2024 13:16:21 +0200 Subject: [PATCH 48/78] use beta1 of integrations-api --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b6c06aa..1a1a203 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 22 - 1.4.0-quickaccess + 1.4.0-beta1 2.0.13 2.17.1 From b4d7be3377c18bbfe6e70d520644817cbca82a2f Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 27 Jun 2024 13:26:41 +0200 Subject: [PATCH 49/78] reset version to prepare PR --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1a1a203..29d49fc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.cryptomator integrations-win - 1.3.0-quickaccess + 1.3.0-SNAPSHOT Cryptomator Integrations for Windows Provides optional Windows services used by Cryptomator From 4aa601ead2769febf1db9beb2b7925ac84d8c0da Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 27 Jun 2024 13:32:21 +0200 Subject: [PATCH 50/78] migrate ci to use JDK 22 --- .github/workflows/build.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/dependency-check.yml | 2 +- .github/workflows/publish-central.yml | 2 +- .github/workflows/publish-github.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a85c28c..9cc3824 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 22 cache: 'maven' - name: Ensure to use tagged version if: startsWith(github.ref, 'refs/tags/') diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cd30ed4..e786bc1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 22 cache: 'maven' - name: Initialize CodeQL uses: github/codeql-action/init@v3 diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index d0c8357..ca8baa1 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -14,7 +14,7 @@ jobs: with: runner-os: 'windows-latest' java-distribution: 'temurin' - java-version: 17 + java-version: 22 secrets: nvd-api-key: ${{ secrets.NVD_API_KEY }} slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/publish-central.yml b/.github/workflows/publish-central.yml index 8f4b440..dc96c04 100644 --- a/.github/workflows/publish-central.yml +++ b/.github/workflows/publish-central.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 22 cache: 'maven' server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml server-username: MAVEN_USERNAME # env variable for username in deploy diff --git a/.github/workflows/publish-github.yml b/.github/workflows/publish-github.yml index 9c2d18d..2c9c5cf 100644 --- a/.github/workflows/publish-github.yml +++ b/.github/workflows/publish-github.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 22 cache: 'maven' gpg-private-key: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} # Value of the GPG private key to import gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase From 6f490e834f1730848921bc1832734f13f95207cd Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 27 Jun 2024 16:37:03 +0200 Subject: [PATCH 51/78] use icon from executable if present, otherwise default to folder icon --- .../windows/quickaccess/ExplorerQuickAccessService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java index 8d86281..da509d0 100644 --- a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java +++ b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java @@ -41,8 +41,14 @@ public QuickAccessEntry add(Path target, String displayName) throws QuickAccessS baseKey.setStringValue("", entryName, false); //2. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\DefaultIcon /ve /t REG_EXPAND_SZ /d %%SystemRoot%%\system32\imageres.dll,-1043 /f + //TODO: should this be customizable? try (var iconKey = t.createRegKey(baseKey, "DefaultIcon", true)) { - iconKey.setStringValue("", "C:\\Program Files\\Cryptomator\\Cryptomator.exe", false); + var exePath = ProcessHandle.current().info().command(); + if(exePath.isPresent()) { + iconKey.setStringValue("", exePath.get(), false); + } else { + iconKey.setStringValue("", "%SystemRoot%\\system32\\shell32.dll,4", true); //the regular folder icon + } } //3. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /v System.IsPinnedToNameSpaceTree /t REG_DWORD /d 0x1 /f From 0ba6bd7f6a50d87a330c867e576043dbf28c2e93 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 1 Jul 2024 16:28:46 +0200 Subject: [PATCH 52/78] Apply code review notes Co-authored-by: Sebastian Stenzel --- README.md | 2 +- pom.xml | 3 -- .../ExplorerQuickAccessService.java | 28 +++++++++---------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index fd40c21..5bd462d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This project uses the following JVM properties: This project uses JNI, hence you'll nedd Java as well as GCC build tools: -* JDK 17 +* JDK 22 * Maven * MinGW GCC compiler (`choco install mingw --version=10.2.0`) * Make (`choco install make`) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 29d49fc..e4c667f 100644 --- a/pom.xml +++ b/pom.xml @@ -399,9 +399,6 @@ jextract - - false - diff --git a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java index da509d0..de589b5 100644 --- a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java +++ b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java @@ -16,7 +16,7 @@ /** * Implementation of the {@link QuickAccessService} for Windows Explorer *

- * Based on a Microsoft docs example. + * Uses shell namespace extensions and based on a Microsoft docs example. */ @Priority(100) @OperatingSystem(OperatingSystem.Value.WINDOWS) @@ -35,65 +35,65 @@ public QuickAccessEntry add(Path target, String displayName) throws QuickAccessS var entryName = "Vault - " + displayName; var clsid = "{" + UUID.randomUUID() + "}"; LOG.debug("Creating navigation pane entry with CLSID {}", clsid); - //1. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d "MyCloudStorageApp" /f + //1. Creates the shell extension and names it try (var t = WindowsRegistry.startTransaction()) { try (var baseKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true)) { baseKey.setStringValue("", entryName, false); - //2. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\DefaultIcon /ve /t REG_EXPAND_SZ /d %%SystemRoot%%\system32\imageres.dll,-1043 /f + //2. Set icon //TODO: should this be customizable? try (var iconKey = t.createRegKey(baseKey, "DefaultIcon", true)) { var exePath = ProcessHandle.current().info().command(); - if(exePath.isPresent()) { + if (exePath.isPresent()) { iconKey.setStringValue("", exePath.get(), false); } else { iconKey.setStringValue("", "%SystemRoot%\\system32\\shell32.dll,4", true); //the regular folder icon } } - //3. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /v System.IsPinnedToNameSpaceTree /t REG_DWORD /d 0x1 /f + //3. Pin the entry to navigation pane baseKey.setDwordValue("System.IsPinnedToNameSpaceTree", 0x1); - //4. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /v SortOrderIndex /t REG_DWORD /d 0x42 /f + //4. Place it in the top section of the navigation pane baseKey.setDwordValue("SortOrderIndex", 0x41); - //5. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\InProcServer32 /ve /t REG_EXPAND_SZ /d /f + //5. Regsiter as a namespace extension try (var inProcServer32Key = t.createRegKey(baseKey, "InProcServer32", true)) { inProcServer32Key.setStringValue("", "%systemroot%\\system32\\shell32.dll", true); } - //6. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\Instance /v CLSID /t REG_SZ /d {0E5AAE11-A475-4c5b-AB00-C66DE400274E} /f + //6. This extenstion works like a folder try (var instanceKey = t.createRegKey(baseKey, "Instance", true)) { instanceKey.setStringValue("CLSID", "{0E5AAE11-A475-4c5b-AB00-C66DE400274E}", false); - //7. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\Instance\InitPropertyBag /v Attributes /t REG_DWORD /d 0x411 /f + //7. Set directory attributes for this "folder" // Attributes are READ_ONLY, DIRECTORY, REPARSE_POINT try (var initPropertyBagKey = t.createRegKey(instanceKey, "InitPropertyBag", true)) { initPropertyBagKey.setDwordValue("Attributes", 0x411); - //8. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\Instance\InitPropertyBag /v TargetFolderPath /t REG_EXPAND_SZ /d %%USERPROFILE%%\MyCloudStorageApp /f + //8. Set the target folder initPropertyBagKey.setStringValue("TargetFolderPath", target.toString(), false); } } - //9. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\ShellFolder /v FolderValueFlags /t REG_DWORD /d 0x28 /f + //9. Pin extenstion to the File Explorer tree try (var shellFolderKey = t.createRegKey(baseKey, "ShellFolder", true)) { shellFolderKey.setDwordValue("FolderValueFlags", 0x28); - //10. reg add HKCU\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3}\ShellFolder /v Attributes /t REG_DWORD /d 0xF080004D /f + //10. Set SFGAO attributes for the shell folder shellFolderKey.setDwordValue("Attributes", 0xF080004D); } LOG.trace("Created RegKey {} and subkeys, including Values", baseKey); } - //11. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /ve /t REG_SZ /d MyCloudStorageApp /f + //11. register extenstion in name space root var nameSpaceSubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; try (var nameSpaceKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, nameSpaceSubKey, true)) { nameSpaceKey.setStringValue("", entryName, false); LOG.trace("Created RegKey {} and setting default value", nameSpaceKey); } - //12. reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel /v {0672A6D1-A6E0-40FE-AB16-F25BADC6D9E3} /t REG_DWORD /d 0x1 /f + //12. Hide extension from Desktop try (var newStartPanelKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", true)) { newStartPanelKey.setDwordValue(clsid, 0x1); LOG.trace("Set value {} for RegKey {}", clsid, newStartPanelKey); From d67ae9d6e242e5c76afc6a951f80357e68480103 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 1 Jul 2024 16:29:18 +0200 Subject: [PATCH 53/78] Don't allow actual closing of predefined reg keys --- .../windows/common/RegistryKey.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index 37786b9..c2884ae 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -18,10 +18,10 @@ public class RegistryKey implements AutoCloseable { - public static final RegistryKey HKEY_CURRENT_USER = new RegistryKey(Winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); - public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryKey(Winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); - public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryKey(Winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); - public static final RegistryKey HKEY_USERS = new RegistryKey(Winreg_h.HKEY_USERS(), "HKEY_USERS"); + public static final RegistryKey HKEY_CURRENT_USER = new RegistryRoot(Winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); + public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryRoot(Winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); + public static final RegistryKey HKEY_CLASSES_ROOT = new RegistryRoot(Winreg_h.HKEY_CLASSES_ROOT(), "HKEY_CLASSES_ROOT"); + public static final RegistryKey HKEY_USERS = new RegistryRoot(Winreg_h.HKEY_USERS(), "HKEY_USERS"); private final String path; private MemorySegment handle; @@ -168,4 +168,20 @@ MemorySegment getHandle() { public String getPath() { return path; } + + + /** + * Closing predefined registry keys has undefined behaviour. Hence, we shade the super method and do nothing on close. + */ + private static class RegistryRoot extends RegistryKey { + + RegistryRoot(MemorySegment handle, String path) { + super(handle, path); + } + + @Override + public void close() { + //no-op + } + } } From b2856be9ce5d4b2a3ba108ca084eab15ca4ab36c Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 1 Jul 2024 17:08:37 +0200 Subject: [PATCH 54/78] fix broken RegistryKey::getValue method --- .../windows/common/RegistryKey.java | 77 +++++++++---------- .../windows/common/WindowsRegistry.java | 2 - 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index c2884ae..c59f4d0 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -17,6 +17,7 @@ import static org.cryptomator.windows.capi.winreg.Winreg_h.RRF_RT_REG_SZ; public class RegistryKey implements AutoCloseable { + private static final long MAX_DATA_SIZE = (1L << 32) - 1L; //unsinged integer public static final RegistryKey HKEY_CURRENT_USER = new RegistryRoot(Winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryRoot(Winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); @@ -35,53 +36,51 @@ public class RegistryKey implements AutoCloseable { //-- GetValue functions -- public String getStringValue(String name, boolean isExpandable) throws RegistryValueException { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var data = getValue(lpValueName, isExpandable ? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ(), 256L); - return data.getString(0, StandardCharsets.UTF_16LE); - } + var data = getValue(name, isExpandable ? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ(), 256L); + return data.getString(0, StandardCharsets.UTF_16LE); } public int getDwordValue(String name) throws RegistryValueException { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var data = getValue(lpValueName, RRF_RT_REG_DWORD(), 5L); - return data.get(ValueLayout.JAVA_INT, 0); - } + var data = getValue(name, RRF_RT_REG_DWORD(), 5L); + return data.get(ValueLayout.JAVA_INT, 0); } - private MemorySegment getValue(MemorySegment lpValueName, int dwFlags, long seed) throws RegistryValueException { - long bufferSize = seed - 1; + private MemorySegment getValue(String name, int dwFlags, long initBufferSizePlus1) throws RegistryValueException { try (var arena = Arena.ofConfined()) { - var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, (int) bufferSize); - - try (var dataArena = Arena.ofConfined()) { - var lpData = dataArena.allocate(bufferSize); - - int result = Winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); - if (result == ERROR_MORE_DATA()) { - throw new BufferTooSmallException(); - } else if (result != ERROR_SUCCESS()) { - throw new RegistryValueException("winreg_h:RegGetValue", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); - } - - var returnBuffer = Arena.ofAuto().allocate(Integer.toUnsignedLong(lpDataSize.get(ValueLayout.JAVA_INT, 0))); - MemorySegment.copy(lpData, 0L, returnBuffer, 0L, returnBuffer.byteSize()); - return returnBuffer; - } catch (BufferTooSmallException _) { - if (bufferSize <= WindowsRegistry.MAX_DATA_SIZE) { - return getValue(lpValueName, dwFlags, seed << 1); - } else { - throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, 0); + + long bufferSizePlus1 = initBufferSizePlus1; + do { + long bufferSize = bufferSizePlus1 - 1; + + //for every try, free old data memory and allocate a new one doubled in size + try (var dataArena = Arena.ofConfined()) { + var lpData = dataArena.allocate(bufferSize); + lpDataSize.set(ValueLayout.JAVA_INT_UNALIGNED, 0, (int) bufferSize); + + int result = Winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); + + if (result == ERROR_SUCCESS()) { + //success. Copy the data to a longer segment with auto lifecycle + var returnBuffer = Arena.ofAuto().allocate(Integer.toUnsignedLong(lpDataSize.get(ValueLayout.JAVA_INT, 0))); + MemorySegment.copy(lpData, 0L, returnBuffer, 0L, returnBuffer.byteSize()); + return returnBuffer; + } else if (result == ERROR_MORE_DATA()) { + //increase buffer size and retry + if (bufferSize <= MAX_DATA_SIZE) { + bufferSizePlus1 = bufferSizePlus1 << 1; + } else { + throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); + } + } else { + throw new RegistryValueException("winreg_h:RegGetValue", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); + } } - } + } while (true); } } - private static class BufferTooSmallException extends RuntimeException { - - } - //-- SetValue functions -- public void setStringValue(String name, String data, boolean isExpandable) throws RegistryValueException { @@ -101,8 +100,8 @@ public void setDwordValue(String name, int data) throws RegistryValueException { } private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags) throws RegistryValueException { - if (data.byteSize() > WindowsRegistry.MAX_DATA_SIZE) { - throw new IllegalArgumentException("Data must be smaller than " + WindowsRegistry.MAX_DATA_SIZE + "bytes."); + if (data.byteSize() > MAX_DATA_SIZE) { + throw new IllegalArgumentException("Data must be smaller than " + MAX_DATA_SIZE + "bytes."); } int result = Winreg_h.RegSetKeyValueW(handle, NULL, lpValueName, dwFlags, data, (int) data.byteSize()); diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index cd3030e..a98363e 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -17,8 +17,6 @@ public class WindowsRegistry { - public static final long MAX_DATA_SIZE = (1L << 32) - 1L; //unsinged integer - public static RegistryTransaction startTransaction() throws WindowsException { var transactionHandle = Ktmw32_h.CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL); if (transactionHandle.address() == INVALID_HANDLE_VALUE().address()) { From 4c88ab1b510b82aca294861f2d252987e2e7d2e2 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 1 Jul 2024 18:05:44 +0200 Subject: [PATCH 55/78] exclude generated code from automatic review --- .coderabbit/config.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .coderabbit/config.yml diff --git a/.coderabbit/config.yml b/.coderabbit/config.yml new file mode 100644 index 0000000..7ecd0c6 --- /dev/null +++ b/.coderabbit/config.yml @@ -0,0 +1,3 @@ +path_filters: + exclude: + - src/main/java/org/cryptomator/windows/capi/** \ No newline at end of file From 35fcd7716381880c1c4caeecd5fd2bae4388a2d0 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 4 Jul 2024 13:37:39 +0200 Subject: [PATCH 56/78] simplify RegistryKey::getValue function and add test to set/get big value data --- .../windows/common/RegistryKey.java | 71 +++++++++---------- .../windows/common/WindowsRegistryIT.java | 25 +++++++ 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index c59f4d0..cc1546f 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -17,7 +17,8 @@ import static org.cryptomator.windows.capi.winreg.Winreg_h.RRF_RT_REG_SZ; public class RegistryKey implements AutoCloseable { - private static final long MAX_DATA_SIZE = (1L << 32) - 1L; //unsinged integer + //allocate at most 128MiB when reading from the registry to keep the library responsive + static final int MAX_DATA_SIZE = (1 << 27); public static final RegistryKey HKEY_CURRENT_USER = new RegistryRoot(Winreg_h.HKEY_CURRENT_USER(), "HKEY_CURRENT_USER"); public static final RegistryKey HKEY_LOCAL_MACHINE = new RegistryRoot(Winreg_h.HKEY_LOCAL_MACHINE(), "HKEY_LOCAL_MACHINE"); @@ -36,48 +37,42 @@ public class RegistryKey implements AutoCloseable { //-- GetValue functions -- public String getStringValue(String name, boolean isExpandable) throws RegistryValueException { - var data = getValue(name, isExpandable ? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ(), 256L); - return data.getString(0, StandardCharsets.UTF_16LE); + try (var arena = Arena.ofConfined()) { + var data = getValue(arena, name, isExpandable ? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ()); + return data.getString(0, StandardCharsets.UTF_16LE); + } } public int getDwordValue(String name) throws RegistryValueException { - var data = getValue(name, RRF_RT_REG_DWORD(), 5L); - return data.get(ValueLayout.JAVA_INT, 0); + try (var arena = Arena.ofConfined()) { + var data = getValue(arena, name, RRF_RT_REG_DWORD()); + return data.get(ValueLayout.JAVA_INT, 0); + } } - private MemorySegment getValue(String name, int dwFlags, long initBufferSizePlus1) throws RegistryValueException { - try (var arena = Arena.ofConfined()) { - var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); - var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, 0); - - long bufferSizePlus1 = initBufferSizePlus1; - do { - long bufferSize = bufferSizePlus1 - 1; - - //for every try, free old data memory and allocate a new one doubled in size - try (var dataArena = Arena.ofConfined()) { - var lpData = dataArena.allocate(bufferSize); - lpDataSize.set(ValueLayout.JAVA_INT_UNALIGNED, 0, (int) bufferSize); - - int result = Winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); - - if (result == ERROR_SUCCESS()) { - //success. Copy the data to a longer segment with auto lifecycle - var returnBuffer = Arena.ofAuto().allocate(Integer.toUnsignedLong(lpDataSize.get(ValueLayout.JAVA_INT, 0))); - MemorySegment.copy(lpData, 0L, returnBuffer, 0L, returnBuffer.byteSize()); - return returnBuffer; - } else if (result == ERROR_MORE_DATA()) { - //increase buffer size and retry - if (bufferSize <= MAX_DATA_SIZE) { - bufferSizePlus1 = bufferSizePlus1 << 1; - } else { - throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); - } - } else { - throw new RegistryValueException("winreg_h:RegGetValue", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); - } - } - } while (true); + private MemorySegment getValue(Arena arena, String name, int dwFlags) throws RegistryValueException { + var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); + var lpDataSize = arena.allocateFrom(ValueLayout.JAVA_INT, 0); + + int result; + int bufferSize = 128; //will be doubled in first iteration + MemorySegment lpData; + do { + bufferSize = bufferSize << 1; + if (bufferSize >= MAX_DATA_SIZE) { + throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); + } + lpData = arena.allocate(bufferSize); + lpDataSize.set(ValueLayout.JAVA_INT, 0, bufferSize); + + result = Winreg_h.RegGetValueW(handle, NULL, lpValueName, dwFlags, NULL, lpData, lpDataSize); + + } while (result == ERROR_MORE_DATA()); + + if (result == ERROR_SUCCESS()) { + return lpData; + } else { + throw new RegistryValueException("winreg_h:RegGetValue", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); } } diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index 26e96c9..795b96c 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -230,4 +230,29 @@ public void testDeleteCommit() throws WindowsException { }); Assertions.assertEquals(ERROR_FILE_NOT_FOUND(), regException.getSystemErrorCode()); } + + @Test + @DisplayName("Set and get big value data") + @Order(11) + public void testLottaData() throws WindowsException { + var filler = "I like big nums and i cannot lie".repeat(1 << 16); //2 MiB + + try (var t = WindowsRegistry.startTransaction(); + var k = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win", true)) { + k.setStringValue("bigData", filler, false); + t.commit(); + } + + try (var t = WindowsRegistry.startTransaction(); + var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { + var value = k.getStringValue("bigData", false); + Assertions.assertEquals(filler, value); + } + + try (var t = WindowsRegistry.startTransaction()) { + t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); + t.commit(); + } + + } } From 7fe1522d24e0094d86080625281009c5e5edd920 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 5 Jul 2024 11:38:42 +0200 Subject: [PATCH 57/78] Make api misuse resistant: * remove methods RegistryKey::deleteAllValuesSubtrees and RegistryKey::deleteSubtree * rename method RegistryKey::deleteValuesSubtrees to deleteTree and make public * add doc to method --- .../windows/common/RegistryKey.java | 22 +++++++++---------- .../ExplorerQuickAccessService.java | 2 +- .../windows/common/WindowsRegistryIT.java | 4 ++-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index cc1546f..71833e3 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -122,18 +122,16 @@ public void deleteValue(String valueName, boolean ignoreNotExisting) throws Regi } } - public void deleteSubtree(String subkey) throws RegistryKeyException { - if (subkey == null || subkey.isBlank()) { - throw new IllegalArgumentException("Subkey must not be empty"); - } - deleteValuesAndSubtrees(subkey); - } - - public void deleteAllValuesAndSubtrees() throws RegistryKeyException { - deleteValuesAndSubtrees(""); - } - - private void deleteValuesAndSubtrees(String subkey) throws RegistryKeyException { + /** + * Deletes recursively content of this registry key. + *

+ * If a non-empty subkey name is specified, then the corresponding subkey and its descendants are deleted. + * If an empty string or {@code null} is specified as the subkey, this method deletes all subtrees and values of this registry key. + * + * @param subkey Name of the subkey, being the root of the subtree to be deleted. Can be {@code null} or empty. + * @throws RegistryKeyException, if winreg.h:RegDeleteTreeW returns a result != ERROR_SUCCESS + */ + public void deleteTree(String subkey) throws RegistryKeyException { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); int result = Winreg_h.RegDeleteTreeW(handle, lpSubkey); diff --git a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java index de589b5..b6a561f 100644 --- a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java +++ b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java @@ -136,7 +136,7 @@ public synchronized void remove() throws QuickAccessServiceException { //undo everything else try (var baseKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid)) { LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey); - baseKey.deleteAllValuesAndSubtrees(); + baseKey.deleteTree(""); } t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true); t.commit(); diff --git a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java index 795b96c..92006d6 100644 --- a/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java +++ b/src/test/java/org/cryptomator/windows/common/WindowsRegistryIT.java @@ -169,7 +169,7 @@ public void testOpenDeleteValueCommit() throws WindowsException { public void testOpenDeleteTreeRollback() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win")) { - k.deleteAllValuesAndSubtrees(); + k.deleteTree(""); } Assertions.assertDoesNotThrow(() -> { @@ -187,7 +187,7 @@ public void testOpenDeleteTreeCommit() throws WindowsException { try (var t = WindowsRegistry.startTransaction(); var k = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "org.cryptomator.integrations-win"); var subk = t.createRegKey(k, "subkey", true)) { - k.deleteAllValuesAndSubtrees(); + k.deleteTree(""); t.commit(); } From 85a8f231c57f9ef53822723f255971c731e08dd6 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 5 Jul 2024 11:53:27 +0200 Subject: [PATCH 58/78] add javadoc --- .../windows/common/RegistryKey.java | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index 71833e3..0a03d2c 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -36,6 +36,15 @@ public class RegistryKey implements AutoCloseable { //-- GetValue functions -- + /** + * Gets a REG_SZ or REG_EXPAND_SZ value. + * + * @param name name of the value + * @param isExpandable flag indicating if the value is of type REG_EXPAND_SZ + * @return the data of the value + * @throws RegistryValueException, if winreg.h:RegGetValueW returns a result != ERROR_SUCCESS + * @implNote This method is restricted to read at most {@value MAX_DATA_SIZE} from the registry. If the value exceeds the size, a runtime exception is thrown. + */ public String getStringValue(String name, boolean isExpandable) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var data = getValue(arena, name, isExpandable ? RRF_RT_REG_EXPAND_SZ() : RRF_RT_REG_SZ()); @@ -43,6 +52,13 @@ public String getStringValue(String name, boolean isExpandable) throws RegistryV } } + /** + * Gets a DWORD value. + * + * @param name name of the value + * @return the data of the value + * @throws RegistryValueException, if winreg.h:RegGetValueW returns a result != ERROR_SUCCESS + */ public int getDwordValue(String name) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var data = getValue(arena, name, RRF_RT_REG_DWORD()); @@ -78,6 +94,14 @@ private MemorySegment getValue(Arena arena, String name, int dwFlags) throws Reg //-- SetValue functions -- + /** + * Sets a REG_SZ or REG_EXPAND_SZ value for this registry key. + * + * @param name name of the value + * @param data Data to be set + * @param isExpandable flag marking if the value is of type REG_EXPAND_SZ + * @throws RegistryValueException, if winreg.h:RegSetKeyValueW returns a result != ERROR_SUCCESS + */ public void setStringValue(String name, String data, boolean isExpandable) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); @@ -86,6 +110,13 @@ public void setStringValue(String name, String data, boolean isExpandable) throw } } + /** + * Sets a DWORD value for this registry key. + * + * @param name name of the value + * @param data Data to be set + * @throws RegistryValueException, if winreg.h:RegSetKeyValueW returns a result != ERROR_SUCCESS + */ public void setDwordValue(String name, int data) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(name, StandardCharsets.UTF_16LE); @@ -107,10 +138,24 @@ private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags //-- delete operations + /** + * Deletes a value of this registry key. + * + * @param valueName name of the value + * @throws RegistryValueException, if winreg.h:RegDeleteKeyValueW returns a result != ERROR_SUCCESS + * @see RegistryKey#deleteValue(String, boolean) + */ public void deleteValue(String valueName) throws RegistryValueException { deleteValue(valueName, false); } + /** + * Deletes a value of this registry key. + * + * @param valueName name of the value + * @param ignoreNotExisting flag indicating wether a not existing value should be ignored + * @throws RegistryValueException, if winreg.h:RegDeleteKeyValueW returns a result != ERROR_SUCCESS, except the result is ERROR_FILE_NOT_FOUND and {@code ignoreNotExisting == true} + */ public void deleteValue(String valueName, boolean ignoreNotExisting) throws RegistryValueException { try (var arena = Arena.ofConfined()) { var lpValueName = arena.allocateFrom(valueName, StandardCharsets.UTF_16LE); @@ -141,8 +186,13 @@ public void deleteTree(String subkey) throws RegistryKeyException { } } + /** + * Closes this registry key. + * + * @throws RuntimeException wrapping a {@link RegistryKeyException}, if winreg.h:RegCloseKey returns a result != ERROR_SUCCESS + */ @Override - public synchronized void close() { + public synchronized void close() throws RuntimeException { if (!isClosed) { int result = Winreg_h.RegCloseKey(handle); if (result != ERROR_SUCCESS()) { From a74aa374fc4373285bb8f48ec4560bcf88bf4649 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 5 Jul 2024 11:54:00 +0200 Subject: [PATCH 59/78] Remove unnecessary string conversions --- .../java/org/cryptomator/windows/common/RegistryKey.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index 0a03d2c..2cc6358 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -76,7 +76,7 @@ private MemorySegment getValue(Arena arena, String name, int dwFlags) throws Reg do { bufferSize = bufferSize << 1; if (bufferSize >= MAX_DATA_SIZE) { - throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(lpValueName.getString(0, StandardCharsets.UTF_16LE), path, bufferSize)); + throw new RuntimeException("Getting value %s for key %s failed. Maximum buffer size of %d reached.".formatted(name, path, bufferSize)); } lpData = arena.allocate(bufferSize); lpDataSize.set(ValueLayout.JAVA_INT, 0, bufferSize); @@ -88,7 +88,7 @@ private MemorySegment getValue(Arena arena, String name, int dwFlags) throws Reg if (result == ERROR_SUCCESS()) { return lpData; } else { - throw new RegistryValueException("winreg_h:RegGetValue", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); + throw new RegistryValueException("winreg_h:RegGetValue", path, name, result); } } @@ -162,7 +162,7 @@ public void deleteValue(String valueName, boolean ignoreNotExisting) throws Regi int result = Winreg_h.RegDeleteKeyValueW(handle, NULL, lpValueName); if (result != ERROR_SUCCESS() // && !(result == ERROR_FILE_NOT_FOUND() && ignoreNotExisting)) { - throw new RegistryValueException("winreg_h:RegSetKeyValueW", path, lpValueName.getString(0, StandardCharsets.UTF_16LE), result); + throw new RegistryValueException("winreg_h:RegSetKeyValueW", path, valueName, result); } } } From 541361925b65e150b2cdcf855cef6d9ba99a83d2 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 5 Jul 2024 17:29:07 +0200 Subject: [PATCH 60/78] Rework close method of registry transaction: * does not throw anymore * the handle is always discarded * use logger --- .../windows/common/WindowsRegistry.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index a98363e..13cea4b 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -3,6 +3,8 @@ import org.cryptomator.windows.capi.common.Windows_h; import org.cryptomator.windows.capi.ktmw32.Ktmw32_h; import org.cryptomator.windows.capi.winreg.Winreg_h; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.foreign.AddressLayout; import java.lang.foreign.Arena; @@ -17,6 +19,8 @@ public class WindowsRegistry { + private static final Logger LOG = LoggerFactory.getLogger(WindowsRegistry.class); + public static RegistryTransaction startTransaction() throws WindowsException { var transactionHandle = Ktmw32_h.CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL); if (transactionHandle.address() == INVALID_HANDLE_VALUE().address()) { @@ -129,26 +133,31 @@ public synchronized void rollback() throws WindowsException { closeInternal(); } - ; - + /** + * Closes this transaction. + *

+ * If this transaction is not commited, it is rolled back. + * The close method always discards the transaction handle. If an error occurs in either rolling back or closing the handle, the error is logged, but no exception thrown. + */ @Override - public synchronized void close() throws WindowsException { - if (!isCommited) { - try { + public synchronized void close() { + try { + if (!isCommited) { rollback(); - } catch (WindowsException e) { - System.err.printf("Failed to rollback uncommited transaction on close: %s%n", e.getMessage()); } + } catch (WindowsException e) { + LOG.error("Failed to rollback uncommited transaction on close: {}", e.getMessage()); + } finally { + closeInternal(); } - closeInternal(); } - private synchronized void closeInternal() throws WindowsException { + private synchronized void closeInternal() { if (!isClosed) { int result = Windows_h.CloseHandle(transactionHandle); if (result == 0) { int error = Windows_h.GetLastError(); - throw new WindowsException("Windows.h:CloseHandle", error); + LOG.error("Closing transaction handle failed. Function Windows.h:CloseHandle set system error code to {}", error); } transactionHandle = null; isClosed = true; From 7037aad3b8a084496bc92bbdbc333e38d98bff06 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 5 Jul 2024 17:29:20 +0200 Subject: [PATCH 61/78] doc doc doc --- .../windows/common/WindowsRegistry.java | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java index 13cea4b..bb11c3c 100644 --- a/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java +++ b/src/main/java/org/cryptomator/windows/common/WindowsRegistry.java @@ -41,6 +41,17 @@ public static class RegistryTransaction implements AutoCloseable { this.transactionHandle = handle; } + /** + * Creates and opens a registry key. + *

+ * If the registry key already exists, it is just opened. The key is opened with the access rights KEY_READ and KEY_WRITE. + * + * @param key handle to an already opened registry key + * @param subkey name/path of a subkey that this function opens or creates + * @param isVolatile flag indicating if this key is volatile. A volatile key is not preserved over a system restart. + * @return the opened {@link RegistryKey} + * @throws RegistryKeyException if Winreg_h.RegCreateKeyTransactedW returns with a result != ERROR_SUCCESS + */ public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolatile) throws RegistryKeyException { var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { @@ -61,10 +72,22 @@ public RegistryKey createRegKey(RegistryKey key, String subkey, boolean isVolati if (result != ERROR_SUCCESS()) { throw new RegistryKeyException("winreg.h:RegCreateKeyTransactedW", key.getPath() + "\\" + subkey, result); } + //TODO: we can check if a registry root is opened (key is any regRoot && subkey == "") + // if so, we should wrap it in the corresponding class return new RegistryKey(pointerToResultKey.get(C_POINTER, 0), key.getPath() + "\\" + subkey); } } + /** + * Opens a registry key. + *

+ * The key is opened with the access rights KEY_READ and KEY_WRITE. + * + * @param key handle to an already opened registry key + * @param subkey name/path of a subkey that this function opens + * @return the opened {@link RegistryKey} + * @throws RegistryKeyException if Winreg_h.RegOpenKeyTransactedW returns with a result != ERROR_SUCCESS + */ public RegistryKey openRegKey(RegistryKey key, String subkey) throws RegistryKeyException { var pointerToResultKey = Arena.ofAuto().allocate(AddressLayout.ADDRESS); try (var arena = Arena.ofConfined()) { @@ -85,10 +108,26 @@ public RegistryKey openRegKey(RegistryKey key, String subkey) throws RegistryKey } } - public void deleteRegKey(RegistryKey key, String subkey) throws WindowsException { + /** + * Deletes a registry key. + * + * @param key handle to an already opened registry key + * @param subkey name/path of a subkey that this function opens or creates + * @throws RegistryKeyException if Winreg_h.RegDeleteKeyTransactedW returns with a result != ERROR_SUCCESS + */ + public void deleteRegKey(RegistryKey key, String subkey) throws RegistryKeyException { deleteRegKey(key, subkey, false); } + /** + * Deletes a registry key. + *

+ * If the key does not exists and {@code ignoreNotExisting == true}, no exceptions is thrown. + * + * @param key handle to an already opened registry key + * @param subkey name/path of a subkey that this function opens or creates + * @throws RegistryKeyException if Winreg_h.RegDeleteKeyTransactedW returns with a result != ERROR_SUCCESS, except the result is ERROR_FILE_NOT_FOUND and {@code ignoreNotExisting == true} + */ public void deleteRegKey(RegistryKey key, String subkey, boolean ignoreNotExisting) throws RegistryKeyException { try (var arena = Arena.ofConfined()) { var lpSubkey = arena.allocateFrom(subkey, StandardCharsets.UTF_16LE); @@ -107,7 +146,11 @@ public void deleteRegKey(RegistryKey key, String subkey, boolean ignoreNotExisti } } - + /** + * Commits and closes this transaction. + * + * @throws WindowsException if Ktmw32_h:CommitTransaction returned 0. The exception contains the system error code. + */ public synchronized void commit() throws WindowsException { if (isClosed) { throw new IllegalStateException("Transaction already closed"); @@ -121,6 +164,11 @@ public synchronized void commit() throws WindowsException { closeInternal(); } + /** + * Rolls this transaction back and closes it. + * + * @throws WindowsException if Ktmw32_h:RollbackTransaction returned 0. The exception contains the system error code. + */ public synchronized void rollback() throws WindowsException { if (isClosed) { throw new IllegalStateException("Transaction already closed"); From fcd11c44019b9222b4a6af166a4d33310e84288c Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 5 Jul 2024 17:35:29 +0200 Subject: [PATCH 62/78] fix doc doc --- .../cryptomator/windows/common/RegistryKey.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/common/RegistryKey.java b/src/main/java/org/cryptomator/windows/common/RegistryKey.java index 2cc6358..42fe644 100644 --- a/src/main/java/org/cryptomator/windows/common/RegistryKey.java +++ b/src/main/java/org/cryptomator/windows/common/RegistryKey.java @@ -38,12 +38,13 @@ public class RegistryKey implements AutoCloseable { /** * Gets a REG_SZ or REG_EXPAND_SZ value. + *

+ * The size of the data is restricted to at most {@value MAX_DATA_SIZE}. If the the value exceeds the size, a runtime exception is thrown. * * @param name name of the value * @param isExpandable flag indicating if the value is of type REG_EXPAND_SZ * @return the data of the value - * @throws RegistryValueException, if winreg.h:RegGetValueW returns a result != ERROR_SUCCESS - * @implNote This method is restricted to read at most {@value MAX_DATA_SIZE} from the registry. If the value exceeds the size, a runtime exception is thrown. + * @throws RegistryValueException if winreg.h:RegGetValueW returns a result != ERROR_SUCCESS */ public String getStringValue(String name, boolean isExpandable) throws RegistryValueException { try (var arena = Arena.ofConfined()) { @@ -57,7 +58,7 @@ public String getStringValue(String name, boolean isExpandable) throws RegistryV * * @param name name of the value * @return the data of the value - * @throws RegistryValueException, if winreg.h:RegGetValueW returns a result != ERROR_SUCCESS + * @throws RegistryValueException if winreg.h:RegGetValueW returns a result != ERROR_SUCCESS */ public int getDwordValue(String name) throws RegistryValueException { try (var arena = Arena.ofConfined()) { @@ -100,7 +101,7 @@ private MemorySegment getValue(Arena arena, String name, int dwFlags) throws Reg * @param name name of the value * @param data Data to be set * @param isExpandable flag marking if the value is of type REG_EXPAND_SZ - * @throws RegistryValueException, if winreg.h:RegSetKeyValueW returns a result != ERROR_SUCCESS + * @throws RegistryValueException if winreg.h:RegSetKeyValueW returns a result != ERROR_SUCCESS */ public void setStringValue(String name, String data, boolean isExpandable) throws RegistryValueException { try (var arena = Arena.ofConfined()) { @@ -115,7 +116,7 @@ public void setStringValue(String name, String data, boolean isExpandable) throw * * @param name name of the value * @param data Data to be set - * @throws RegistryValueException, if winreg.h:RegSetKeyValueW returns a result != ERROR_SUCCESS + * @throws RegistryValueException if winreg.h:RegSetKeyValueW returns a result != ERROR_SUCCESS */ public void setDwordValue(String name, int data) throws RegistryValueException { try (var arena = Arena.ofConfined()) { @@ -142,7 +143,7 @@ private void setValue(MemorySegment lpValueName, MemorySegment data, int dwFlags * Deletes a value of this registry key. * * @param valueName name of the value - * @throws RegistryValueException, if winreg.h:RegDeleteKeyValueW returns a result != ERROR_SUCCESS + * @throws RegistryValueException if winreg.h:RegDeleteKeyValueW returns a result != ERROR_SUCCESS * @see RegistryKey#deleteValue(String, boolean) */ public void deleteValue(String valueName) throws RegistryValueException { @@ -154,7 +155,7 @@ public void deleteValue(String valueName) throws RegistryValueException { * * @param valueName name of the value * @param ignoreNotExisting flag indicating wether a not existing value should be ignored - * @throws RegistryValueException, if winreg.h:RegDeleteKeyValueW returns a result != ERROR_SUCCESS, except the result is ERROR_FILE_NOT_FOUND and {@code ignoreNotExisting == true} + * @throws RegistryValueException if winreg.h:RegDeleteKeyValueW returns a result != ERROR_SUCCESS, except the result is ERROR_FILE_NOT_FOUND and {@code ignoreNotExisting == true} */ public void deleteValue(String valueName, boolean ignoreNotExisting) throws RegistryValueException { try (var arena = Arena.ofConfined()) { @@ -174,7 +175,7 @@ public void deleteValue(String valueName, boolean ignoreNotExisting) throws Regi * If an empty string or {@code null} is specified as the subkey, this method deletes all subtrees and values of this registry key. * * @param subkey Name of the subkey, being the root of the subtree to be deleted. Can be {@code null} or empty. - * @throws RegistryKeyException, if winreg.h:RegDeleteTreeW returns a result != ERROR_SUCCESS + * @throws RegistryKeyException if winreg.h:RegDeleteTreeW returns a result != ERROR_SUCCESS */ public void deleteTree(String subkey) throws RegistryKeyException { try (var arena = Arena.ofConfined()) { From 5f3489121d6f695c90fa22aa5ea44d977d5ba7a3 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 8 Jul 2024 10:05:51 +0200 Subject: [PATCH 63/78] Update org.owasp:dependency-check-maven from 9.2.0 to 10.0.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 758df39..5048aaa 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ 5.12.0 - 9.2.0 + 10.0.2 1.7.0 @@ -392,4 +392,4 @@ - \ No newline at end of file + From e5e44603c061f2cb07410a60fb9090d34768bd50 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 12 Jul 2024 15:08:33 +0200 Subject: [PATCH 64/78] add DisplayName to ExplorerQuickAccessService --- pom.xml | 2 +- .../windows/quickaccess/ExplorerQuickAccessService.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4c667f..aadb0f7 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 22 - 1.4.0-beta1 + 1.4.0-beta2 2.0.13 2.17.1 diff --git a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java index b6a561f..427c846 100644 --- a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java +++ b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java @@ -1,5 +1,6 @@ package org.cryptomator.windows.quickaccess; +import org.cryptomator.integrations.common.DisplayName; import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.quickaccess.QuickAccessService; @@ -20,6 +21,7 @@ */ @Priority(100) @OperatingSystem(OperatingSystem.Value.WINDOWS) +@DisplayName("Explorer Navigation Pane") public class ExplorerQuickAccessService implements QuickAccessService { private static final Logger LOG = LoggerFactory.getLogger(ExplorerQuickAccessService.class); From 5b6e2f2639abe842f575bded0071f83c53ff7b94 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 16 Jul 2024 17:27:46 +0200 Subject: [PATCH 65/78] fix log messages in quickAccess Service --- .../quickaccess/ExplorerQuickAccessService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java index 427c846..875bc6f 100644 --- a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java +++ b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java @@ -85,20 +85,20 @@ public QuickAccessEntry add(Path target, String displayName) throws QuickAccessS //10. Set SFGAO attributes for the shell folder shellFolderKey.setDwordValue("Attributes", 0xF080004D); } - LOG.trace("Created RegKey {} and subkeys, including Values", baseKey); + LOG.trace("Created RegKey {} and subkeys, including Values", baseKey.getPath()); } //11. register extenstion in name space root var nameSpaceSubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" + clsid; try (var nameSpaceKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, nameSpaceSubKey, true)) { nameSpaceKey.setStringValue("", entryName, false); - LOG.trace("Created RegKey {} and setting default value", nameSpaceKey); + LOG.trace("Created RegKey {} and setting default value", nameSpaceKey.getPath()); } //12. Hide extension from Desktop try (var newStartPanelKey = t.createRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", true)) { newStartPanelKey.setDwordValue(clsid, 0x1); - LOG.trace("Set value {} for RegKey {}", clsid, newStartPanelKey); + LOG.trace("Set value {} for RegKey {}", clsid, newStartPanelKey.getPath()); } t.commit(); } catch (WindowsException e) { @@ -131,13 +131,13 @@ public synchronized void remove() throws QuickAccessServiceException { //undo step 12. try (var nameSpaceKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel")) { - LOG.trace("Removing Value {} of RegKey {}", clsid, nameSpaceKey); + LOG.trace("Removing Value {} of RegKey {}", clsid, nameSpaceKey.getPath()); nameSpaceKey.deleteValue(clsid, true); } //undo everything else try (var baseKey = t.openRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid)) { - LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey); + LOG.trace("Wiping everything under RegKey {} and key itself.", baseKey.getPath()); baseKey.deleteTree(""); } t.deleteRegKey(RegistryKey.HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\" + clsid, true); From f97de37a168e7382d18fa1c33f8401d3db2e5ae3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:02:24 +0000 Subject: [PATCH 66/78] Bump org.junit.jupiter:junit-jupiter in the java-test-dependencies group (#70) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a1b6224..70fb121 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 2.17.1 - 5.10.2 + 5.10.3 5.12.0 From 6c4aee98f7f1958a863e69d22a3f38c88316945a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:03:27 +0000 Subject: [PATCH 67/78] Bump the maven-build-plugins group across 1 directory with 4 updates (#72) --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 70fb121..9f35261 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ 5.12.0 - 10.0.2 + 10.0.3 1.7.0 0.4.1 @@ -111,7 +111,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.3.2 + 3.4.0 @@ -185,7 +185,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.3.1 maven-source-plugin @@ -201,7 +201,7 @@ maven-javadoc-plugin - 3.7.0 + 3.8.0 attach-javadocs From 9439a9edb004440a65ad9a6293da40d010d30a59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 08:25:36 +0000 Subject: [PATCH 68/78] Bump the java-production-dependencies group across 1 directory with 4 updates (#74) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9f35261..38fba71 100644 --- a/pom.xml +++ b/pom.xml @@ -38,8 +38,8 @@ 1.4.0-beta2 - 2.0.13 - 2.17.1 + 2.0.14 + 2.17.2 5.10.3 From 4395f4281a141b2174f4e0610dad5cb168e3f92d Mon Sep 17 00:00:00 2001 From: Cryptobot Date: Thu, 8 Aug 2024 10:49:25 +0200 Subject: [PATCH 69/78] New Crowdin updates (#47) New translations strings.properties Bashkir; Marathi; Persian; Slovenian; Ukrainian; Urdu (Pakistan); [ci skip] --- src/main/resources/WinIntegrationsBundle_ba.properties | 1 + src/main/resources/WinIntegrationsBundle_fa.properties | 1 + src/main/resources/WinIntegrationsBundle_mr.properties | 0 src/main/resources/WinIntegrationsBundle_sl.properties | 1 + src/main/resources/WinIntegrationsBundle_uk.properties | 2 +- src/main/resources/WinIntegrationsBundle_ur.properties | 0 6 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/WinIntegrationsBundle_ba.properties create mode 100644 src/main/resources/WinIntegrationsBundle_mr.properties create mode 100644 src/main/resources/WinIntegrationsBundle_ur.properties diff --git a/src/main/resources/WinIntegrationsBundle_ba.properties b/src/main/resources/WinIntegrationsBundle_ba.properties new file mode 100644 index 0000000..c72bc88 --- /dev/null +++ b/src/main/resources/WinIntegrationsBundle_ba.properties @@ -0,0 +1 @@ +org.cryptomator.windows.keychain.displayName=Windows мәғлүмәттәрен һаҡлау \ No newline at end of file diff --git a/src/main/resources/WinIntegrationsBundle_fa.properties b/src/main/resources/WinIntegrationsBundle_fa.properties index e69de29..d15c740 100644 --- a/src/main/resources/WinIntegrationsBundle_fa.properties +++ b/src/main/resources/WinIntegrationsBundle_fa.properties @@ -0,0 +1 @@ +org.cryptomator.windows.keychain.displayName=محافظت از داده های ویندوز \ No newline at end of file diff --git a/src/main/resources/WinIntegrationsBundle_mr.properties b/src/main/resources/WinIntegrationsBundle_mr.properties new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/WinIntegrationsBundle_sl.properties b/src/main/resources/WinIntegrationsBundle_sl.properties index e69de29..85f4a7a 100644 --- a/src/main/resources/WinIntegrationsBundle_sl.properties +++ b/src/main/resources/WinIntegrationsBundle_sl.properties @@ -0,0 +1 @@ +org.cryptomator.windows.keychain.displayName=Windows Data Protection \ No newline at end of file diff --git a/src/main/resources/WinIntegrationsBundle_uk.properties b/src/main/resources/WinIntegrationsBundle_uk.properties index 85f4a7a..bb88c3b 100644 --- a/src/main/resources/WinIntegrationsBundle_uk.properties +++ b/src/main/resources/WinIntegrationsBundle_uk.properties @@ -1 +1 @@ -org.cryptomator.windows.keychain.displayName=Windows Data Protection \ No newline at end of file +org.cryptomator.windows.keychain.displayName=Захист даних Windows \ No newline at end of file diff --git a/src/main/resources/WinIntegrationsBundle_ur.properties b/src/main/resources/WinIntegrationsBundle_ur.properties new file mode 100644 index 0000000..e69de29 From 8916027baa0830dfe40c6f2ffb6a4164c86a5480 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 8 Aug 2024 10:54:34 +0200 Subject: [PATCH 70/78] [ci skip] prevent leakage of NVD API key into logs --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 38fba71..7345131 100644 --- a/pom.xml +++ b/pom.xml @@ -273,7 +273,7 @@ true suppression.xml false - ${env.NVD_API_KEY} + NVD_API_KEY From 90d0fde90fe0c97c636b59eea6ed24a9ec6a8912 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 8 Aug 2024 10:58:55 +0200 Subject: [PATCH 71/78] prepare 1.3.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7345131..4ed7e69 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.cryptomator integrations-win - 1.3.0-SNAPSHOT + 1.3.0 Cryptomator Integrations for Windows Provides optional Windows services used by Cryptomator From 681494ad341b9fa6a1aec871d5d4a91945de4a17 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 8 Aug 2024 15:30:26 +0200 Subject: [PATCH 72/78] use bc signer for maven-sign-plugin --- .github/workflows/publish-central.yml | 5 ++--- .github/workflows/publish-github.yml | 3 +-- pom.xml | 5 +---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-central.yml b/.github/workflows/publish-central.yml index dc96c04..f445539 100644 --- a/.github/workflows/publish-central.yml +++ b/.github/workflows/publish-central.yml @@ -26,8 +26,6 @@ jobs: server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml server-username: MAVEN_USERNAME # env variable for username in deploy server-password: MAVEN_PASSWORD # env variable for token in deploy - gpg-private-key: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} # Value of the GPG private key to import - gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Enforce project version ${{ github.event.inputs.tag }} run: mvn versions:set -B -DnewVersion="${{ github.event.inputs.tag }}" - name: Deploy @@ -40,4 +38,5 @@ jobs: --add-opens=java.desktop/java.awt.font=ALL-UNNAMED MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} \ No newline at end of file + MAVEN_GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} + MAVEN_GPG_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} \ No newline at end of file diff --git a/.github/workflows/publish-github.yml b/.github/workflows/publish-github.yml index 2c9c5cf..95fe4b8 100644 --- a/.github/workflows/publish-github.yml +++ b/.github/workflows/publish-github.yml @@ -18,8 +18,6 @@ jobs: distribution: 'temurin' java-version: 22 cache: 'maven' - gpg-private-key: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} # Value of the GPG private key to import - gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Enforce project version ${{ github.event.release.tag_name }} run: mvn versions:set -B -DnewVersion="${{ github.event.release.tag_name }}" - name: Deploy @@ -27,6 +25,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} + MAVEN_GPG_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} notify: runs-on: ubuntu-latest needs: [publish] diff --git a/pom.xml b/pom.xml index 4ed7e69..3a80b8e 100644 --- a/pom.xml +++ b/pom.xml @@ -339,10 +339,7 @@ sign - - --pinentry-mode - loopback - + bc From a1e48c8e780822e91b1aa500c683555680cd7d75 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 8 Aug 2024 15:30:26 +0200 Subject: [PATCH 73/78] use bc signer for maven-sign-plugin (cherry picked from commit 681494ad341b9fa6a1aec871d5d4a91945de4a17) --- .github/workflows/publish-central.yml | 5 ++--- .github/workflows/publish-github.yml | 3 +-- pom.xml | 5 +---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-central.yml b/.github/workflows/publish-central.yml index dc96c04..f445539 100644 --- a/.github/workflows/publish-central.yml +++ b/.github/workflows/publish-central.yml @@ -26,8 +26,6 @@ jobs: server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml server-username: MAVEN_USERNAME # env variable for username in deploy server-password: MAVEN_PASSWORD # env variable for token in deploy - gpg-private-key: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} # Value of the GPG private key to import - gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Enforce project version ${{ github.event.inputs.tag }} run: mvn versions:set -B -DnewVersion="${{ github.event.inputs.tag }}" - name: Deploy @@ -40,4 +38,5 @@ jobs: --add-opens=java.desktop/java.awt.font=ALL-UNNAMED MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} \ No newline at end of file + MAVEN_GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} + MAVEN_GPG_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} \ No newline at end of file diff --git a/.github/workflows/publish-github.yml b/.github/workflows/publish-github.yml index 2c9c5cf..95fe4b8 100644 --- a/.github/workflows/publish-github.yml +++ b/.github/workflows/publish-github.yml @@ -18,8 +18,6 @@ jobs: distribution: 'temurin' java-version: 22 cache: 'maven' - gpg-private-key: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} # Value of the GPG private key to import - gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Enforce project version ${{ github.event.release.tag_name }} run: mvn versions:set -B -DnewVersion="${{ github.event.release.tag_name }}" - name: Deploy @@ -27,6 +25,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} + MAVEN_GPG_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} notify: runs-on: ubuntu-latest needs: [publish] diff --git a/pom.xml b/pom.xml index 7345131..b4adbfe 100644 --- a/pom.xml +++ b/pom.xml @@ -339,10 +339,7 @@ sign - - --pinentry-mode - loopback - + bc From d1d39298ebaf170500e3ce9fa221287fa2f39dec Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 19 Aug 2024 10:30:22 +0200 Subject: [PATCH 74/78] open integrations-api module to this module --- src/main/java/module-info.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index e30da94..56cb5a5 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -16,10 +16,12 @@ requires com.fasterxml.jackson.databind; opens org.cryptomator.windows.keychain to com.fasterxml.jackson.databind; + opens org.cryptomator.windows.quickaccess to org.cryptomator.integrations.api; provides AutoStartProvider with WindowsAutoStart; provides KeychainAccessProvider with WindowsProtectedKeychainAccess; provides UiAppearanceProvider with WinUiAppearanceProvider; provides RevealPathService with ExplorerRevealPathService; provides QuickAccessService with ExplorerQuickAccessService; + } \ No newline at end of file From 003311914e504ef2dd27e6379892ce1846eae673 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 16 Sep 2024 15:46:09 +0200 Subject: [PATCH 75/78] resolve TODO --- .../windows/quickaccess/ExplorerQuickAccessService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java index 875bc6f..49b7fd0 100644 --- a/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java +++ b/src/main/java/org/cryptomator/windows/quickaccess/ExplorerQuickAccessService.java @@ -34,7 +34,7 @@ public QuickAccessEntry add(Path target, String displayName) throws QuickAccessS if (target == null) { throw new IllegalArgumentException("Parameter 'target' must not be null."); } - var entryName = "Vault - " + displayName; + var entryName = displayName.length() > 40? displayName.substring(0, 40) : displayName; //truncate name var clsid = "{" + UUID.randomUUID() + "}"; LOG.debug("Creating navigation pane entry with CLSID {}", clsid); //1. Creates the shell extension and names it From 6d2ef843fcaf7bd715612147f530c7d5d3198953 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 16 Sep 2024 15:46:59 +0200 Subject: [PATCH 76/78] bump dependencies --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b4adbfe..9f90b7f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 22 - 1.4.0-beta2 + 1.4.0 2.0.14 2.17.2 From 0750e9c4a09ae6d362870d71717a8691aa8054dc Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 16 Sep 2024 15:47:54 +0200 Subject: [PATCH 77/78] bump slf4j --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f90b7f..e517db6 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 1.4.0 - 2.0.14 + 2.0.16 2.17.2 From d2a27946746246a68cdf711e6a5b7d82d05d3f86 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 16 Sep 2024 15:57:41 +0200 Subject: [PATCH 78/78] fix method head to follow new interface --- .../windows/keychain/WindowsProtectedKeychainAccess.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java b/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java index abb71f1..883b81c 100644 --- a/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java +++ b/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java @@ -92,7 +92,7 @@ public String displayName() { } @Override - public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException { + public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean ignored) throws KeychainAccessException { loadKeychainEntriesIfNeeded(); ByteBuffer buf = UTF_8.encode(CharBuffer.wrap(passphrase)); byte[] cleartext = new byte[buf.remaining()];