Keep hardlink target files selected with selected hardlinks

This commit is contained in:
Andrea Mazzoleni 2026-09-25 09:40:58 +02:00
parent 13267085d1
commit ce7d29f178
2 changed files with 32 additions and 0 deletions

View file

@ -904,6 +904,17 @@ endif # HAVE_POSIX
grep -q '^file:disk1:a-hardlink-main:' test.log
grep -q '^link_hardlink:disk1:b-hardlink-link:a-hardlink-main$$' test.log
! grep -q '^file:disk1:b-hardlink-link:' test.log
$(MSG) Missing hardlink recovery preserves its present target
rm bench/disk1/b-hardlink-link
echo NEWER-UNSYNCED-CONTENT-WITH-DIFFERENT-SIZE > bench/hardlink-missing-expected
cp bench/hardlink-missing-expected bench/disk1/a-hardlink-main
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(PAR1) -m fix -l test.log
cmp bench/hardlink-missing-expected bench/disk1/a-hardlink-main
cmp bench/hardlink-missing-expected bench/disk1/b-hardlink-link
if HAVE_POSIX
test bench/disk1/a-hardlink-main -ef bench/disk1/b-hardlink-link
endif # HAVE_POSIX
rm bench/hardlink-missing-expected
rm bench/disk1/a-hardlink-main bench/disk1/b-hardlink-link
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
test -f bench/disk1/a-hardlink-main

View file

@ -5856,6 +5856,27 @@ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tom
}
}
/*
* Hardlink entries don't own data blocks; their data is represented by the
* target file. If a hardlink pathname is selected while its target file is
* excluded by a path filter, keep the target selected as well so check/fix
* can process the data backing the selected hardlink.
* With the missing filter, a present target must remain excluded because
* fix -m promises to leave existing files untouched. It can be used directly
* to recreate the missing hardlink without processing its data.
*/
for (j = tommy_list_head(&disk->linklist); j != 0; j = j->next) {
struct snapraid_link* slink = j->data;
if (link_flag_has(slink, FILE_IS_HARDLINK) && !link_flag_has(slink, FILE_IS_EXCLUDED)) {
struct snapraid_file* file;
file = tommy_hashdyn_search(&disk->pathset, file_path_compare_to_arg, slink->linkto, file_path_hash(slink->linkto));
if (file && filter_existence(filter_missing, disk->dir, file->sub) == 0)
file_flag_clear(file, FILE_IS_EXCLUDED);
}
}
/* for each empty dir */
for (j = tommy_list_head(&disk->dirlist); j != 0; j = j->next) {
struct snapraid_dir* dir = j->data;