git: aa870a1935bc - main - quot: Add tests
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 17 Oct 2025 11:55:26 UTC
The branch main has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=aa870a1935bccb66e02c4c31630706768a3e7d74
commit aa870a1935bccb66e02c4c31630706768a3e7d74
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-10-17 11:55:12 +0000
Commit: Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-10-17 11:55:12 +0000
quot: Add tests
To facilitate the task, we change the comparison function so that users
with equal filesystem usage are sorted by UID, and add an undocumented
option that prevents quot from replacing numerical UIDs with names. We
also switch from getfsfile(3) to getmntpoint(3) so the first line is
identical regardless of whether we pass quot a mountpoint or a device.
Reviewed by: markj
Differential Revision: https://reviews.freebsd.org/D53133
---
etc/mtree/BSD.tests.dist | 2 +
usr.sbin/quot/Makefile | 6 ++-
usr.sbin/quot/quot.c | 19 +++++---
usr.sbin/quot/tests/Makefile | 4 ++
usr.sbin/quot/tests/quot_test.sh | 102 +++++++++++++++++++++++++++++++++++++++
5 files changed, 126 insertions(+), 7 deletions(-)
diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index 8a752925d0a5..520b41c8b88f 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -1285,6 +1285,8 @@
..
pw
..
+ quot
+ ..
rpcbind
..
sa
diff --git a/usr.sbin/quot/Makefile b/usr.sbin/quot/Makefile
index 34ebcb1009c8..2f32c8f2df8b 100644
--- a/usr.sbin/quot/Makefile
+++ b/usr.sbin/quot/Makefile
@@ -1,5 +1,9 @@
+.include <src.opts.mk>
+
PROG= quot
MAN= quot.8
-LIBADD= ufs
+LIBADD= ufs util
+HAS_TESTS=
+SUBDIR.${MK_TESTS}= tests
.include <bsd.prog.mk>
diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c
index 323648d8d550..879580f649b9 100644
--- a/usr.sbin/quot/quot.c
+++ b/usr.sbin/quot/quot.c
@@ -42,6 +42,7 @@
#include <fcntl.h>
#include <fstab.h>
#include <libufs.h>
+#include <mntopts.h>
#include <paths.h>
#include <pwd.h>
#include <stdbool.h>
@@ -55,6 +56,7 @@
/* some flags of what to do: */
static bool all;
static bool count;
+static bool noname;
static bool unused;
static void (*func)(int, struct fs *);
static long blocksize;
@@ -227,7 +229,7 @@ user(uid_t uid)
for (usr = users + uid % nusers, i = nusers; --i >= 0; usr--) {
if (usr->name == NULL) {
usr->uid = uid;
- if ((pwd = getpwuid(uid)) == NULL)
+ if (noname || (pwd = getpwuid(uid)) == NULL)
asprintf(&usr->name, "#%u", uid);
else
usr->name = strdup(pwd->pw_name);
@@ -248,7 +250,10 @@ cmpusers(const void *v1, const void *v2)
{
const struct user *u1 = v1, *u2 = v2;
- return (u2->space - u1->space);
+ return (u2->space > u1->space ? 1 :
+ u2->space < u1->space ? -1 :
+ u1->uid > u2->uid ? 1 :
+ u1->uid < u2->uid ? -1 : 0);
}
#define sortusers(users) \
@@ -469,12 +474,11 @@ int
main(int argc, char *argv[])
{
struct statfs *mp;
- struct fstab *fs;
int ch, cnt;
func = douser;
header = getbsize(&headerlen, &blocksize);
- while ((ch = getopt(argc, argv, "acfhknv")) != -1) {
+ while ((ch = getopt(argc, argv, "acfhkNnv")) != -1) {
switch (ch) {
case 'a':
all = true;
@@ -491,6 +495,9 @@ main(int argc, char *argv[])
case 'k':
blocksize = 1024;
break;
+ case 'N':
+ noname = true;
+ break;
case 'n':
func = donames;
break;
@@ -513,8 +520,8 @@ main(int argc, char *argv[])
quot(mp->f_mntfromname, mp->f_mntonname);
}
while (argc-- > 0) {
- if ((fs = getfsfile(*argv)) != NULL)
- quot(fs->fs_spec, 0);
+ if ((mp = getmntpoint(*argv)) != NULL)
+ quot(mp->f_mntfromname, mp->f_mntonname);
else
quot(*argv, 0);
argv++;
diff --git a/usr.sbin/quot/tests/Makefile b/usr.sbin/quot/tests/Makefile
new file mode 100644
index 000000000000..d4e64691f905
--- /dev/null
+++ b/usr.sbin/quot/tests/Makefile
@@ -0,0 +1,4 @@
+PACKAGE= tests
+ATF_TESTS_SH= quot_test
+
+.include <bsd.test.mk>
diff --git a/usr.sbin/quot/tests/quot_test.sh b/usr.sbin/quot/tests/quot_test.sh
new file mode 100644
index 000000000000..21088d162a53
--- /dev/null
+++ b/usr.sbin/quot/tests/quot_test.sh
@@ -0,0 +1,102 @@
+#
+# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# Create and mount a UFS filesystem on a small memory disk
+quot_setup()
+{
+ atf_check -o save:dev mdconfig -t malloc -s 16M
+ local dev=$(cat dev)
+ atf_check -o ignore newfs "$@" /dev/$dev
+ atf_check mkdir mnt
+ local mnt=$(realpath mnt)
+ atf_check mount /dev/$dev "$mnt"
+ echo "/dev/$dev: ($mnt)" >expect
+ printf "%5d\t%5d\t%-8s\n" 8 2 "#0" >>expect
+}
+
+# Create a directory owned by a given UID
+quot_adduid()
+{
+ local uid=$1
+ atf_check install -d -o $uid -g 0 mnt/$uid
+ printf "%5d\t%5d\t%-8s\n" 4 1 "#$uid" >>expect
+}
+
+# Perform the tests
+quot_test()
+{
+ local dev=$(cat dev)
+ # Create inodes owned by a large number of users to exercise
+ # hash collisions and rehashing. The code uses an open hash
+ # table that starts out with only 8 entries and doubles every
+ # time it fills up.
+ local uid
+ for uid in $(seq 1 32); do
+ quot_adduid $uid
+ done
+ # Also create inodes owned by users with long UIDs, up to the
+ # highest possible value (2^32 - 2, because chown(2) and
+ # friends interpret 2^32 - 1 as “leave unchanged”).
+ local shift
+ for shift in $(seq 6 32); do
+ quot_adduid $(((1 << shift) - 2))
+ done
+ # Since quot operates directly on the underlying device, not
+ # on the mounted filesystem, we remount read-only to ensure
+ # that everything gets flushed to the memory disk.
+ atf_check mount -ur /dev/$dev
+ atf_check -o file:expect quot -fkN /dev/$dev
+ atf_check -o file:expect quot -fkN $(realpath mnt)
+}
+
+# Unmount and release the memory disk
+quot_cleanup()
+{
+ if [ -d mnt ]; then
+ umount mnt || true
+ fi
+ if [ -f dev ]; then
+ mdconfig -d -u $(cat dev) || true
+ fi
+}
+
+atf_test_case ufs1 cleanup
+ufs1_head()
+{
+ atf_set descr "Test quot on UFS1"
+ atf_set require.user root
+}
+ufs1_body()
+{
+ quot_setup -O1
+ quot_test
+}
+ufs1_cleanup()
+{
+ quot_cleanup
+}
+
+atf_test_case ufs2 cleanup
+ufs2_head()
+{
+ atf_set descr "Test quot on UFS2"
+ atf_set require.user root
+}
+ufs2_body()
+{
+ quot_setup -O2
+ quot_test
+}
+ufs2_cleanup()
+{
+ quot_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case ufs1
+ atf_add_test_case ufs2
+}