Skip to content

Commit 4efedf2

Browse files
authored
Merge pull request #1822 from AmirF194/fix/1815-symlink-skip-not-reported
fix(discover): report a symlink skip through the ignored-files taxonomy
2 parents 86c031a + eb3bd81 commit 4efedf2

2 files changed

Lines changed: 55 additions & 3 deletions

File tree

‎src/discover/discover.c‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -758,14 +758,18 @@ static int wide_stat(const char *path, struct stat *st) {
758758
/* Stat a path, skipping symlinks (POSIX) and junctions / reparse points
759759
* (Windows). Returns 0 on success, -1 to skip. Skipping reparse points keeps
760760
* discovery from walking through a junction that points outside the project
761-
* root, mirroring the POSIX S_ISLNK skip. */
762-
static int safe_stat(const char *abs_path, struct stat *st) {
761+
* root, mirroring the POSIX S_ISLNK skip. *is_symlink reports whether the
762+
* skip (if any) was specifically the symlink/reparse-point check, as
763+
* opposed to some other stat failure (permissions, a race with a delete). */
764+
static int safe_stat(const char *abs_path, struct stat *st, bool *is_symlink) {
765+
*is_symlink = false;
763766
#ifdef _WIN32
764767
wchar_t *wpath = cbm_path_to_wide(abs_path);
765768
if (wpath) {
766769
DWORD attr = GetFileAttributesW(wpath);
767770
free(wpath);
768771
if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
772+
*is_symlink = true;
769773
return CBM_NOT_FOUND;
770774
}
771775
}
@@ -775,6 +779,7 @@ static int safe_stat(const char *abs_path, struct stat *st) {
775779
return CBM_NOT_FOUND;
776780
}
777781
if (S_ISLNK(st->st_mode)) {
782+
*is_symlink = true;
778783
return CBM_NOT_FOUND;
779784
}
780785
return 0;
@@ -891,9 +896,15 @@ static void walk_dir_process_entry(cbm_dirent_t *entry, const walk_frame_t *fram
891896
}
892897

893898
struct stat st;
894-
if (safe_stat(abs_path, &st) != 0) {
899+
bool is_symlink = false;
900+
if (safe_stat(abs_path, &st, &is_symlink) != 0) {
895901
if (out->count_only) {
896902
out->failed = true;
903+
} else if (is_symlink) {
904+
/* Deliberately not indexed (#963): record so callers can
905+
* surface it, matching the directory-exclusion and file
906+
* skip_reason paths a few lines below. */
907+
file_list_add_ignored(out, rel_path, "symlink");
897908
}
898909
return;
899910
}

‎tests/test_discover.c‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,46 @@ TEST(discover_symlink_skipped) {
909909
#endif
910910
}
911911

912+
/* #1815: a symlink skip must be as visible as the other two skip paths in
913+
* the same walk (gitignore/cbmignore/skip-list files, excluded dirs): a
914+
* subtree dropped because it's a symlink must not look identical to a
915+
* complete index. */
916+
TEST(discover_symlink_skip_is_reported) {
917+
#ifdef _WIN32
918+
SKIP_PLATFORM("Windows: symlinks need admin / symlink() unavailable");
919+
#else
920+
char *base = th_mktempdir("cbm_disc_sym_rep");
921+
ASSERT(base != NULL);
922+
923+
th_write_file(TH_PATH(base, "real.go"), "package main\n");
924+
char real_path[512], link_path[512];
925+
snprintf(real_path, sizeof(real_path), "%s/real.go", base);
926+
snprintf(link_path, sizeof(link_path), "%s/link.go", base);
927+
symlink(real_path, link_path);
928+
929+
cbm_discover_opts_t opts = {0};
930+
cbm_file_info_t *files = NULL;
931+
int count = 0;
932+
cbm_ignored_file_t *ignored = NULL;
933+
int ignored_count = 0;
934+
int ignored_total = 0;
935+
936+
int rc = cbm_discover_ex2(base, &opts, &files, &count, NULL, NULL, &ignored, &ignored_count,
937+
&ignored_total);
938+
ASSERT_EQ(rc, 0);
939+
ASSERT_EQ(count, 1); /* real.go only, same as discover_symlink_skipped */
940+
ASSERT_EQ(ignored_total, 1);
941+
ASSERT_EQ(ignored_count, 1);
942+
ASSERT_STR_EQ(ignored[0].rel_path, "link.go");
943+
ASSERT_STR_EQ(ignored[0].reason, "symlink");
944+
945+
cbm_discover_free(files, count);
946+
cbm_discover_free_ignored(ignored, ignored_count);
947+
th_cleanup(base);
948+
PASS();
949+
#endif
950+
}
951+
912952
TEST(discover_new_ignore_patterns) {
913953
char *base = th_mktempdir("cbm_disc_newign");
914954
ASSERT(base != NULL);
@@ -1785,6 +1825,7 @@ SUITE(discover) {
17851825
RUN_TEST(discover_cbmignore);
17861826
RUN_TEST(discover_cbmignore_stacks);
17871827
RUN_TEST(discover_symlink_skipped);
1828+
RUN_TEST(discover_symlink_skip_is_reported);
17881829
RUN_TEST(discover_new_ignore_patterns);
17891830
RUN_TEST(discover_generic_dirs_full_mode);
17901831
RUN_TEST(discover_generic_dirs_fast_mode);

0 commit comments

Comments
 (0)