From nobody Fri Oct 09 14:50:22 2026 X-Original-To: dev-commits-src-main@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 4j1VCL1tTCz6vZY7 for ; Fri, 09 Oct 2026 14:50:22 +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 4j1VCL1NtGz4PsJ for ; Fri, 09 Oct 2026 14:50:22 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791557422; 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=Vbwvq2FLNKipJ79SQuqoPRd8sEPKZeX8cJjz2w+wJX1IPgGw7LsKOTwHFCh90RPw5bP9y9 gcjOdxqc1CKHWCvlhwgCHBwGp+TEEiUfYDI4IgFQ2YekYvmOendIWIJXmseG0oz0aYtmEf CYAnb2FWuuCrP3BseTrYOs6XJjf/l228x/Od5PMKfOuZs6T+Iicpaf+caTtm7VJRoPkH+t T20GB12rvaDEK4JMhhBLOPVayTZyWubrogso4AgOrhce/Rt2aVDll3OKV5pwIT9CKtyvP0 ABOVHSEWzg+as+rQilLeI1YzpjhLmAoyWFsa4b1mIrGNCyGB+FJ4G8k/leF9+A== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791557422; b=S73xqiaA9HEblcuCSUsVfNf1H4sHZ5jTUTJOGUVYLPX0RXwsa/ASznJEW+Z5E+FldmYadP 8yknIeEojx8BoFr/ZOUBaPkxQpOKFOgEtNl+fFiQbrOxr1wDlDM9dY17hqB8wd6M43NRAt z/ftlivAhhx0F/ExggacvbJ6fiPlL4H2dyjtsI7qbkpibtadIDF56N4zLZqbiewFhtj3J5 4zHzgD3tcBqwzG3SmDU5bYpXrlCao7de+um63I7eLA5ACpuex04x9ugTVZ1d28hCZCuvZL Oo3H2s/5JMLpAPKMDgOn7jhg4iOI1vn0bjx2gPO9XH9s83z5fkvK+5Q14GGRNA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791557422; 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=nW5wL0I7VzAUyRNLgHRL5NR0c87eoEK//lTyjAmKWeUR+JJxifaGtM065c15U1jaaqxNkJ 6PKJWBoIs8EguTlYjFs6VFu/Ojvv6niyjboOKNJmKY07Jhrp3icHb/AI3K/NHWq2tJo5D0 Mcof8sgYgzZdlWR6d0WSGAsM0Dduhkcu4U68Q0j2QRgIT002gSoxWA7+fDvvnbicQbVBft ZsfJhWo/W34SJdE2DfKFyXjNP+0txZxGo/S3AOEUfeuV5Pky3td1ZYcxtWBH1/plWLejpu 6I8WCZPdhbsOa/EKh3Jd2KWoh8y9ZzJw3mwWQZpUmL7BQEeAskx9NV7dfHzLuQ== 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 4j1VCL0RZ4zFHc for ; Fri, 09 Oct 2026 14:50:22 +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-main X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@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: