]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-display-quota: add a little helper binary to show quota on tmpfs
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 28 Mar 2025 17:45:23 +0000 (18:45 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 1 Apr 2025 12:16:40 +0000 (14:16 +0200)
quota from quota project fails:
$ quota
quota: Cannot stat() mounted device tmpfs: No such file or directory
quota: Cannot stat() mounted device tmpfs: No such file or directory

Having this helper helped me understand what is going on with the quotas when
the tests failed. I think it'd be useful to keep it around for now, even though
it is not actually connected in the tests.

src/test/meson.build
src/test/test-display-quota.c [new file with mode: 0644]

index a4c33cb50ca5442cb840f870d06b5226e512e9d5..c9fd63f798082b4e3622e02a7b3135936ebc8ba6 100644 (file)
@@ -267,6 +267,10 @@ executables += [
                 'dependencies' : lib_openssl_or_gcrypt,
                 'conditions' : ['HAVE_OPENSSL_OR_GCRYPT'],
         },
+        test_template + {
+                'sources' : files('test-display-quota.c'),
+                'type' : 'manual',
+        },
         test_template + {
                 'sources' : files('test-dlopen-so.c'),
                 'dependencies' : [
diff --git a/src/test/test-display-quota.c b/src/test/test-display-quota.c
new file mode 100644 (file)
index 0000000..e3b6752
--- /dev/null
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bitfield.h"
+#include "fd-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "missing_syscall.h"
+#include "quota-util.h"
+#include "userdb.h"
+
+static int show_quota(uid_t uid, const char *path) {
+        int r;
+
+        _cleanup_close_ int fd = open(path, O_DIRECTORY|O_CLOEXEC);
+        if (fd < 0)
+                return log_error_errno(errno, "Failed to open '%s': %m", path);
+
+        struct dqblk req;
+        r = quotactl_fd_with_fallback(fd, QCMD_FIXED(Q_GETQUOTA, USRQUOTA), uid, &req);
+        if (r == -ESRCH) {
+                log_info_errno(r, "No quota set on %s for UID "UID_FMT": %m", path, uid);
+                return 0;
+        }
+        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+                return log_warning_errno(r, "No UID quota support on %s: %m", path);
+        if (ERRNO_IS_NEG_PRIVILEGE(r))
+                return log_error_errno(r, "Lacking privileges to query UID quota on %s: %m", path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to query disk quota on %s for UID "UID_FMT": %m", path, uid);
+
+        printf("** Quota on %s for UID "UID_FMT" **\n"
+               "block hardlimit:   %"PRIu64"\n"
+               "block softlimit:   %"PRIu64"\n"
+               "blocks current:    %"PRIu64"\n"
+               "inodes hardlimit:  %"PRIu64"\n"
+               "inodes softlimit:  %"PRIu64"\n"
+               "inodes current:    %"PRIu64"\n"
+               "excess block time: %"PRIu64"\n"
+               "excess inode time: %"PRIu64"\n"
+               "validity mask:     0x%"PRIx32,
+               path, uid,
+               req.dqb_bhardlimit,
+               req.dqb_bsoftlimit,
+               req.dqb_curspace,
+               req.dqb_ihardlimit,
+               req.dqb_isoftlimit,
+               req.dqb_curinodes,
+               req.dqb_btime,
+               req.dqb_itime,
+               req.dqb_valid);
+
+        const char* fields[] = {"BLIMITS", "SPACE", "INODES", "BTIME", "ITIME"};
+        bool first = true;
+        for (size_t i = 0; i < ELEMENTSOF(fields); i++)
+                if (BIT_SET(req.dqb_valid, i)) {
+                        printf("%c%s", first ? ' ' : '|', fields[i]);
+                        first = false;
+                }
+        printf("%s\n", first ? "(none)" : "");
+
+        return 0;
+}
+
+static int run(int argc, char **argv) {
+        int r;
+
+        if (argc < 2)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "This program requires at least one argument\n"
+                                       "syntax: test-display-quota USER PATH…");
+
+        const char *user = argv[1];
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+        r = userdb_by_name(user, /* match= */ NULL, USERDB_PARSE_NUMERIC|USERDB_SUPPRESS_SHADOW, &ur);
+        if (r < 0)
+                return log_error_errno(r, "Failed to resolve user '%s': %m", user);
+
+        if (!uid_is_valid(ur->uid))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOMSG), "User '%s' lacks UID.", ur->user_name);
+
+        r = 0;
+        STRV_FOREACH(path, strv_skip(argv, 2))
+                RET_GATHER(r, show_quota(ur->uid, *path));
+
+        return r;
+}
+
+DEFINE_MAIN_FUNCTION(run);