From nobody Fri Oct 09 14:50:22 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4j1VCR5l0Wz6vZY9 for ; Fri, 09 Oct 2026 14:50:27 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4j1VCR3YFSz4Psn for ; Fri, 09 Oct 2026 14:50:27 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791557427; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=ruZ9iS7xEQfFzXUbZlWM4XI3avutB3e2P2DSFHMnI+A=; b=no0Qsh0gJRQ1I8S4vCN1wijLZuMK3JyPqQxmVzamlry21bZnkMLHy95lxbi2hUh80eshu+ XZ3UQ+iKMRm/v0b/GEOSMLpBS3zkpLfcpZaLLEu7IqYCpzqUzZkIZZ/bJyh4RHvJpSTefe i095mGlWjCT5ux8Lsh00CCTqoqPlA34L/ozew0ZD0I4FLUykoe67CWRdHPoBtr9H9IH11n seM1F9Hdi5zdnSDbdRTtpiaEur1kd1WeC9rMa6tUyum5brUwZy0kVb3sNbuun5YD2N+AsW mbl+IDH0GS8mReOv20o7onwFDKleWcL55N42Adl9lK8uv1ZHDhiK9fg221pSJg== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791557427; b=RUesv1680R7LQUvJaOXdDDHkjiMDCA06sFZOPdmDuo89YgKNrQ35gKrY6Jh/9rWMN66Iqm dFKhfcQfCcHZC2kGOzoD3mJUrNzT9ihzzoxBcgJTAoTAKQtDPZJtf9DTjiWEDo++MBDVHF Ewaow6Sk47GuydirSnmjS/raPgD64saxSeYUVY7KBfe4+NSMdmfZ5KyYZKidZiI31lVYUb UB7HxX2TYyCbC9zpvsC4PdiKuJp2TOsPoel6gYZu3zXyBw2vW8nGRZR9L+m/Da4voBqN4/ eDchzuPOhHrA0DReqDensHunVAYt8r+sHYet6FximV5cXpZA1CrB+cBPBYifSw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791557427; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=ruZ9iS7xEQfFzXUbZlWM4XI3avutB3e2P2DSFHMnI+A=; b=CTOCMX2oGPvkYo8xtmgmyy4WTQe7m9kqMcHch2sc5aacoZEas1rfMpr90PcE+ffYOYk+4C ixZhcfDoIbIFzZSKWyADvxnC5I0n7n6iUTireU2CcpRrHd3nMyYR6Zc1znDS0VDiHkytAv hTdk0CHTrDRlXPev7LvAHOpSzBSE5qqyL+UtEcdi7F3GsLcI6jTfUdh7hpaYVKMiCLMVzY KHK1ykTWq+KXhw3OW1M5W/lJUmOyQPfgcSbgRAyI4XpuGkho4OSzbEqTnwINI9BVTuz90E OHTVrVNaDYua3oq8+ujenj26qjOsQcZ9Y/vXCVGuoq2RcMbBPABZADRzp0r4VQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4j1VCR2dhvzDnv for ; Fri, 09 Oct 2026 14:50:27 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3962e by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 09 Oct 2026 14:50:22 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Renato Botelho Subject: git: 81b5b2265d77 - main - makefs/zfs: count "." in the link count and size of dataset roots List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: garga X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 81b5b2265d7734f9c4d4603031b3e8254c7e23d8 Auto-Submitted: auto-generated Date: Fri, 09 Oct 2026 14:50:22 +0000 Message-Id: <6ac8ff2e.3962e.7e8a4b4a@gitrepo.freebsd.org> The branch main has been updated by garga: URL: https://cgit.FreeBSD.org/src/commit/?id=81b5b2265d7734f9c4d4603031b3e8254c7e23d8 commit 81b5b2265d7734f9c4d4603031b3e8254c7e23d8 Author: Renato Botelho AuthorDate: 2026-10-07 11:32:45 +0000 Commit: Renato Botelho CommitDate: 2026-10-09 14:49:32 +0000 makefs/zfs: count "." in the link count and size of dataset roots The root directory of a dataset is populated from an fsnode that is the "." entry of the staging directory, and its children are the siblings of that node, so the loop that computes the link count and the size of the directory (which expects "." among the children, as the nodes of the other directories have) never counts it. The link count of the root of every dataset is one less than it should be (1 for an empty dataset instead of 2, 19 instead of 20 for a root with 18 subdirectories), and so is its size. fts(3) trusts the link count of the directories of ZFS when called with FTS_NOSTAT and FTS_PHYSICAL: it stops looking for subdirectories once it has seen as many as the link count says, and does not descend into the ones it did not look at. As a result find(1), and rm(1) when it does not need to stat the entries, silently skip directories of a pool made by makefs. On a root filesystem made by makefs, find / does not list /etc at all. Start the loop at the "." node itself for the roots of datasets, so that it is counted the same way as in every other directory. Add a test that checks the link counts and the sizes of the dataset roots and of the other directories, with nested datasets and a dataset whose mountpoint is not in the staging tree, and that find(1) sees the whole tree. Fixes: 240afd8c1fc ("makefs: Add ZFS support") MFC after: 1 week Approved by: markj Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D60439 --- usr.sbin/makefs/tests/makefs_zfs_tests.sh | 54 +++++++++++++++++++++++++++++++ usr.sbin/makefs/zfs/fs.c | 7 ++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/usr.sbin/makefs/tests/makefs_zfs_tests.sh b/usr.sbin/makefs/tests/makefs_zfs_tests.sh index fe27c62ab765..b8595a98a2ab 100644 --- a/usr.sbin/makefs/tests/makefs_zfs_tests.sh +++ b/usr.sbin/makefs/tests/makefs_zfs_tests.sh @@ -277,6 +277,59 @@ compression_cleanup() common_cleanup } +# +# Make sure that the link count and the size of the directories which are the +# roots of datasets count "." and the directories which are the mountpoints +# of other datasets. fts(3) trusts the link count of the directories of ZFS +# and skips the subdirectories that it does not account for. +# +atf_test_case dataset_link_counts cleanup +dataset_link_counts_body() +{ + local dir entries subdirs + + create_test_dirs + cd $TEST_INPUTS_DIR + + mkdir -p dir1/sub1 dir1/sub2 dir1/dir2/sub3 dir1/dir2/dir3 dir4 + echo a > dir1/a + echo b > dir1/dir2/b + echo c > dir1/sub1/c + echo d > dir1/sub2/d + echo e > dir1/dir2/sub3/e + + cd - + + atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \ + -o fs=${ZFS_POOL_NAME}/dir1 -o fs=${ZFS_POOL_NAME}/dir1/dir2 \ + -o fs=${ZFS_POOL_NAME}/dir1/dir2/dir3 -o fs=${ZFS_POOL_NAME}/dir4 \ + -o fs=${ZFS_POOL_NAME}/dir5 \ + $TEST_IMAGE $TEST_INPUTS_DIR + + import_image + + # find(1) relies on the link counts to decide which entries may be + # directories, so it must see the whole tree. + (cd $TEST_INPUTS_DIR && find dir1 dir4 | sort) > ./expected + atf_check -o file:./expected -x \ + "cd $TEST_MOUNT_DIR && find dir1 dir4 | sort" + + # The link count is the number of directories in the directory plus + # "." and "..", and the size is the number of entries plus the same. + for dir in . dir1 dir1/sub1 dir1/dir2 dir1/dir2/dir3 dir4 dir5; do + subdirs=$(ls -d $TEST_MOUNT_DIR/$dir/*/ 2>/dev/null | wc -l) + entries=$(ls -A $TEST_MOUNT_DIR/$dir | wc -l) + atf_check -o inline:$((subdirs + 2))\\n \ + stat -f %l $TEST_MOUNT_DIR/$dir + atf_check -o inline:$((entries + 2))\\n \ + stat -f %z $TEST_MOUNT_DIR/$dir + done +} +dataset_link_counts_cleanup() +{ + common_cleanup +} + # # Try destroying a dataset that was created by makefs. # @@ -1171,6 +1224,7 @@ atf_init_test_cases() atf_add_test_case autotrim atf_add_test_case basic atf_add_test_case compression + atf_add_test_case dataset_link_counts atf_add_test_case dataset_removal atf_add_test_case devfs atf_add_test_case empty_dir diff --git a/usr.sbin/makefs/zfs/fs.c b/usr.sbin/makefs/zfs/fs.c index 75f6e30e1500..4e15b4a79f14 100644 --- a/usr.sbin/makefs/zfs/fs.c +++ b/usr.sbin/makefs/zfs/fs.c @@ -389,10 +389,11 @@ fs_populate_sattrs(struct fs_populate_arg *arg, const fsnode *cur, * The size of a ZPL directory is the number of entries * (including "." and ".."), and the link count is the * number of entries which are directories - * (including "." and ".."). + * (including "." and ".."). The root of a dataset is + * the "." node itself, followed by the other entries. */ - for (fsnode *c = - fsnode_isroot(cur) ? cur->next : cur->child; + for (const fsnode *c = + fsnode_isroot(cur) ? cur : cur->child; c != NULL; c = c->next) { switch (c->type) { case S_IFDIR: