git: fd8c98a52f71 - main - tarfs: Correctly track link count.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Thu, 16 Mar 2023 12:19:59 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=fd8c98a52f7178d8071cf201f2eb14f00d904a7d

commit fd8c98a52f7178d8071cf201f2eb14f00d904a7d
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2023-03-16 11:31:06 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2023-03-16 11:31:22 +0000

    tarfs: Correctly track link count.
    
    Sponsored by:   Juniper Networks, Inc.
    Sponsored by:   Klara, Inc.
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D39019
---
 sys/fs/tarfs/tarfs_subr.c        | 13 +++++++++++++
 sys/fs/tarfs/tarfs_vfsops.c      |  7 +++----
 tests/sys/fs/tarfs/tarfs_test.sh |  2 ++
 3 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/sys/fs/tarfs/tarfs_subr.c b/sys/fs/tarfs/tarfs_subr.c
index 0cba43a8c21b..6aa7eaedb773 100644
--- a/sys/fs/tarfs/tarfs_subr.c
+++ b/sys/fs/tarfs/tarfs_subr.c
@@ -384,6 +384,18 @@ tarfs_free_node(struct tarfs_node *tnp)
 	tmp = tnp->tmp;
 
 	switch (tnp->type) {
+	case VREG:
+		if (tnp->nlink-- > 1)
+			return;
+		if (tnp->other != NULL)
+			tarfs_free_node(tnp->other);
+		break;
+	case VDIR:
+		if (tnp->nlink-- > 2)
+			return;
+		if (tnp->parent != NULL && tnp->parent != tnp)
+			tarfs_free_node(tnp->parent);
+		break;
 	case VLNK:
 		if (tnp->link.name)
 			free(tnp->link.name, M_TARFSNAME);
@@ -397,6 +409,7 @@ tarfs_free_node(struct tarfs_node *tnp)
 		free(tnp->blk, M_TARFSBLK);
 	if (tnp->ino >= TARFS_MININO)
 		free_unr(tmp->ino_unr, tnp->ino);
+	TAILQ_REMOVE(&tmp->allnodes, tnp, entries);
 	free(tnp, M_TARFSNODE);
 	tmp->nfiles--;
 }
diff --git a/sys/fs/tarfs/tarfs_vfsops.c b/sys/fs/tarfs/tarfs_vfsops.c
index 03dc598f4b69..9d7297eb9d73 100644
--- a/sys/fs/tarfs/tarfs_vfsops.c
+++ b/sys/fs/tarfs/tarfs_vfsops.c
@@ -406,16 +406,14 @@ static void
 tarfs_free_mount(struct tarfs_mount *tmp)
 {
 	struct mount *mp;
-	struct tarfs_node *tnp;
+	struct tarfs_node *tnp, *tnp_next;
 
 	MPASS(tmp != NULL);
 
 	TARFS_DPF(ALLOC, "%s: Freeing mount structure %p\n", __func__, tmp);
 
 	TARFS_DPF(ALLOC, "%s: freeing tarfs_node structures\n", __func__);
-	while (!TAILQ_EMPTY(&tmp->allnodes)) {
-		tnp = TAILQ_FIRST(&tmp->allnodes);
-		TAILQ_REMOVE(&tmp->allnodes, tnp, entries);
+	TAILQ_FOREACH_SAFE(tnp, &tmp->allnodes, entries, tnp_next) {
 		tarfs_free_node(tnp);
 	}
 
@@ -744,6 +742,7 @@ again:
 			error = EINVAL;
 			goto bad;
 		}
+		tnp->other->nlink++;
 		break;
 	case TAR_TYPE_SYMLINK:
 		if (link == NULL) {
diff --git a/tests/sys/fs/tarfs/tarfs_test.sh b/tests/sys/fs/tarfs/tarfs_test.sh
index 6e44a8081cb2..50d98f857e06 100644
--- a/tests/sys/fs/tarfs/tarfs_test.sh
+++ b/tests/sys/fs/tarfs/tarfs_test.sh
@@ -59,6 +59,8 @@ tarfs_basic_body() {
 	atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/short_link)"
 	atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/long_link)"
 	atf_check_equal "$(sha256 -q "${mnt}"/sparse_file)" ${sum}
+	atf_check_equal "$(stat -f%l "${mnt}"/sparse_file)" 2
+	atf_check_equal "$(stat -f%l "${mnt}"/hard_link)" 2
 }
 tarfs_basic_cleanup() {
 	umount "${mnt}" || true