diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c index fb33fdd939..451e181cd8 100644 --- a/src/login/user-runtime-dir.c +++ b/src/login/user-runtime-dir.c @@ -262,7 +262,7 @@ static int apply_tmpfs_quota( uint64_t v = (scale == 0) ? 0 : (scale == UINT32_MAX) ? UINT64_MAX : - (uint64_t) ((double) (sfs.f_blocks * sfs.f_frsize) / scale * UINT32_MAX); + (uint64_t) ((double) (sfs.f_blocks * sfs.f_frsize) * scale / UINT32_MAX); v = MIN(v, limit); v /= QIF_DQBLKSIZE; diff --git a/src/test/meson.build b/src/test/meson.build index 9db9735a19..9a6e6104ec 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -268,6 +268,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 index 0000000000..e3b67526a3 --- /dev/null +++ b/src/test/test-display-quota.c @@ -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); diff --git a/test/units/TEST-46-HOMED.sh b/test/units/TEST-46-HOMED.sh index 78c91044b8..f7aadf38b5 100755 --- a/test/units/TEST-46-HOMED.sh +++ b/test/units/TEST-46-HOMED.sh @@ -665,24 +665,29 @@ getent passwd aliastest@myrealm getent passwd aliastest2@myrealm getent passwd aliastest3@myrealm -if findmnt -n -o options /tmp | grep -q usrquota ; then +NEWPASSWORD=quux homectl create tmpfsquota --storage=subvolume --dev-shm-limit=50K --tmp-limit=50K -P +for p in /dev/shm /tmp; do + if findmnt -n -o options "$p" | grep -q usrquota; then + # Check if we can display the quotas. If we cannot, than it's likely + # that PID1 was also not able to set the limits and we should not fail + # in the tests below. + /usr/lib/systemd/tests/unit-tests/manual/test-display-quota tmpfsquota "$p" || set +e - NEWPASSWORD=quux homectl create tmpfsquota --storage=subvolume --dev-shm-limit=50K -P + run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile1" bs=1024 count=30 + (! run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile2" bs=1024 count=30) + run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm "$p/quotatestfile1" "$p/quotatestfile2" + run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile1" bs=1024 count=30 + run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm "$p/quotatestfile1" - run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/urandom of=/dev/shm/quotatestfile1 bs=1024 count=30 - (! run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/urandom of=/dev/shm/quotatestfile2 bs=1024 count=30) - run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm /dev/shm/quotatestfile1 /dev/shm/quotatestfile2 - run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/urandom of=/dev/shm/quotatestfile1 bs=1024 count=30 - run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm /dev/shm/quotatestfile1 + set -e + fi +done - systemctl stop user@"$(id -u tmpfsquota)".service - - wait_for_state tmpfsquota inactive - homectl remove tmpfsquota -fi +systemctl stop user@"$(id -u tmpfsquota)".service +wait_for_state tmpfsquota inactive +homectl remove tmpfsquota NEWPASSWORD=quux homectl create subareatest --storage=subvolume -P - run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest mkdir Areas run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest cp -av /etc/skel Areas/furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest cp -av /etc/skel Areas/molb @@ -705,7 +710,7 @@ test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareate test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/furb" # Install a PK rule that allows 'subareatest' user to invoke run0 without password, just for testing -cat > /usr/share/polkit-1/rules.d/subareatest.rules <<'EOF' +cat >/usr/share/polkit-1/rules.d/subareatest.rules <<'EOF' polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units" && subject.user == "subareatest") {