From 99f5f2aa651c03ad0b26077396569e716d55a67a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 10 Jul 2026 16:50:50 +0200 Subject: [PATCH 1/5] fix(windows): classify reparse links by tag Use the name-surrogate bit to distinguish symlinks and junctions from non-link reparse points such as OneDrive and data deduplication. --- src/path/sentry_path_windows.c | 17 +++++++++++-- src/sentry_path.h | 3 ++- tests/unit/test_path.c | 45 +++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/path/sentry_path_windows.c b/src/path/sentry_path_windows.c index b27b4d2486..7169a89afd 100644 --- a/src/path/sentry_path_windows.c +++ b/src/path/sentry_path_windows.c @@ -359,8 +359,21 @@ sentry__path_is_symlink(const sentry_path_t *path) return false; } const DWORD attributes = GetFileAttributesW(path_w); - return attributes != INVALID_FILE_ATTRIBUTES - && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; + if (attributes == INVALID_FILE_ATTRIBUTES + || (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { + return false; + } + + // https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-point-tags + WIN32_FIND_DATAW data; + const HANDLE find_handle = FindFirstFileW(path_w, &data); + if (find_handle == INVALID_HANDLE_VALUE) { + return true; + } + FindClose(find_handle); + + return (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 + && IsReparseTagNameSurrogate(data.dwReserved0) != 0; } size_t diff --git a/src/sentry_path.h b/src/sentry_path.h index 4275c01754..afe8247905 100644 --- a/src/sentry_path.h +++ b/src/sentry_path.h @@ -149,7 +149,8 @@ bool sentry__path_ends_with(const sentry_path_t *path, const char *suffix); bool sentry__path_is_dir(const sentry_path_t *path); /** - * Return whether the path refers to a symlink. + * Return whether the path refers to a symlink. On Windows, this includes + * name-surrogate reparse points such as junctions. */ bool sentry__path_is_symlink(const sentry_path_t *path); diff --git a/tests/unit/test_path.c b/tests/unit/test_path.c index 6b782e8285..62ddc39563 100644 --- a/tests/unit/test_path.c +++ b/tests/unit/test_path.c @@ -2,7 +2,40 @@ #include "sentry_string.h" #include "sentry_testsupport.h" -#if defined(SENTRY_PLATFORM_UNIX) && !defined(SENTRY_PLATFORM_NX) \ +#if defined(SENTRY_PLATFORM_WINDOWS) && !defined(SENTRY_PLATFORM_XBOX) +static bool +is_developer_mode_enabled(void) +{ + DWORD enabled = 0; + DWORD size = sizeof(enabled); + return RegGetValueW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", + L"AllowDevelopmentWithoutDevLicense", RRF_RT_REG_DWORD, NULL, + &enabled, &size) + == ERROR_SUCCESS + && enabled != 0; +} + +static int +symlink(const char *target, const char *link) +{ + sentry_path_t *target_path = sentry__path_from_str(target); + sentry_path_t *link_path = sentry__path_from_str(link); + if (!target_path || !link_path) { + sentry__path_free(link_path); + sentry__path_free(target_path); + return -1; + } + + BOOLEAN success + = CreateSymbolicLinkW(link_path->path_w, target_path->path_w, + SYMBOLIC_LINK_FLAG_DIRECTORY + | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE); + sentry__path_free(link_path); + sentry__path_free(target_path); + return success ? 0 : -1; +} +#elif defined(SENTRY_PLATFORM_UNIX) && !defined(SENTRY_PLATFORM_NX) \ && !defined(SENTRY_PLATFORM_PS) # include #endif @@ -254,10 +287,16 @@ SENTRY_TEST(path_directory) SENTRY_TEST(path_remove_all_symlink) { -#if !defined(SENTRY_PLATFORM_UNIX) || defined(SENTRY_PLATFORM_NX) \ - || defined(SENTRY_PLATFORM_PS) +#if defined(SENTRY_PLATFORM_NX) || defined(SENTRY_PLATFORM_PS) \ + || defined(SENTRY_PLATFORM_XBOX) SKIP_TEST(); #else +# ifdef SENTRY_PLATFORM_WINDOWS + // Creating symlinks without elevation requires Developer Mode. + if (!is_developer_mode_enabled()) { + SKIP_TEST(); + } +# endif sentry_path_t *base = sentry__path_from_str(SENTRY_TEST_PATH_PREFIX ".remove-all"); TEST_ASSERT(!!base); From 7cf8cf1bcae1504890a567e7404b68047b7c0ca9 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 10 Jul 2026 17:05:36 +0200 Subject: [PATCH 2/5] drop developer mode check integration tests already assume python's symlink_to() is available --- tests/unit/test_path.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tests/unit/test_path.c b/tests/unit/test_path.c index 62ddc39563..7ebe2045a9 100644 --- a/tests/unit/test_path.c +++ b/tests/unit/test_path.c @@ -3,19 +3,6 @@ #include "sentry_testsupport.h" #if defined(SENTRY_PLATFORM_WINDOWS) && !defined(SENTRY_PLATFORM_XBOX) -static bool -is_developer_mode_enabled(void) -{ - DWORD enabled = 0; - DWORD size = sizeof(enabled); - return RegGetValueW(HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", - L"AllowDevelopmentWithoutDevLicense", RRF_RT_REG_DWORD, NULL, - &enabled, &size) - == ERROR_SUCCESS - && enabled != 0; -} - static int symlink(const char *target, const char *link) { @@ -291,12 +278,6 @@ SENTRY_TEST(path_remove_all_symlink) || defined(SENTRY_PLATFORM_XBOX) SKIP_TEST(); #else -# ifdef SENTRY_PLATFORM_WINDOWS - // Creating symlinks without elevation requires Developer Mode. - if (!is_developer_mode_enabled()) { - SKIP_TEST(); - } -# endif sentry_path_t *base = sentry__path_from_str(SENTRY_TEST_PATH_PREFIX ".remove-all"); TEST_ASSERT(!!base); From be1486779ff82e7a081fda5f6d4a499ed83c3328 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 10 Jul 2026 17:09:39 +0200 Subject: [PATCH 3/5] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a87f54041..eb7d4cc404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Apply the propagation context to events that already have contexts set, so that events captured with a local scope or with event-level contexts keep their trace. ([#1843](https://github.com/getsentry/sentry-native/pull/1843)) - Route libcurl debug output through the Sentry logger (`SENTRY_TRACE`) instead of writing to `stderr`. ([#1854](https://github.com/getsentry/sentry-native/pull/1854)) - NOTE: `sentry_options_set_debug(options, true)` no longer displays verbose libcurl debug output by default. To restore it, call `sentry_options_set_logger_level(options, SENTRY_LEVEL_TRACE)`. +- Windows: fix symlink detection used to prevent database cleanup from following symlinks in run and cache directories. ([#1857](https://github.com/getsentry/sentry-native/pull/1857)) ## 0.15.3 From 7968c74b354a59f5ccb74a8b26cd0e07356bcf17 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 10 Jul 2026 17:16:16 +0200 Subject: [PATCH 4/5] fix review finding --- src/path/sentry_path_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/path/sentry_path_windows.c b/src/path/sentry_path_windows.c index 7169a89afd..02bb620c57 100644 --- a/src/path/sentry_path_windows.c +++ b/src/path/sentry_path_windows.c @@ -368,7 +368,7 @@ sentry__path_is_symlink(const sentry_path_t *path) WIN32_FIND_DATAW data; const HANDLE find_handle = FindFirstFileW(path_w, &data); if (find_handle == INVALID_HANDLE_VALUE) { - return true; + return false; } FindClose(find_handle); From 1f7bd48897cdcb702b7797cb1b806d8c8cb9a397 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 10 Jul 2026 17:22:21 +0200 Subject: [PATCH 5/5] Revert "fix review finding" This reverts commit 7968c74b354a59f5ccb74a8b26cd0e07356bcf17. --- src/path/sentry_path_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/path/sentry_path_windows.c b/src/path/sentry_path_windows.c index 02bb620c57..7169a89afd 100644 --- a/src/path/sentry_path_windows.c +++ b/src/path/sentry_path_windows.c @@ -368,7 +368,7 @@ sentry__path_is_symlink(const sentry_path_t *path) WIN32_FIND_DATAW data; const HANDLE find_handle = FindFirstFileW(path_w, &data); if (find_handle == INVALID_HANDLE_VALUE) { - return false; + return true; } FindClose(find_handle);