Skip to content

Commit 34ae6b6

Browse files
authored
user-runtime-dir: correct quota size calculation (systemd#36884)
2 parents c5855d9 + f77a8ed commit 34ae6b6

File tree

4 files changed

+114
-17
lines changed

4 files changed

+114
-17
lines changed

src/login/user-runtime-dir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ static int apply_tmpfs_quota(
262262
uint64_t v =
263263
(scale == 0) ? 0 :
264264
(scale == UINT32_MAX) ? UINT64_MAX :
265-
(uint64_t) ((double) (sfs.f_blocks * sfs.f_frsize) / scale * UINT32_MAX);
265+
(uint64_t) ((double) (sfs.f_blocks * sfs.f_frsize) * scale / UINT32_MAX);
266266

267267
v = MIN(v, limit);
268268
v /= QIF_DQBLKSIZE;

src/test/meson.build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,10 @@ executables += [
268268
'dependencies' : lib_openssl_or_gcrypt,
269269
'conditions' : ['HAVE_OPENSSL_OR_GCRYPT'],
270270
},
271+
test_template + {
272+
'sources' : files('test-display-quota.c'),
273+
'type' : 'manual',
274+
},
271275
test_template + {
272276
'sources' : files('test-dlopen-so.c'),
273277
'dependencies' : [

src/test/test-display-quota.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include "bitfield.h"
4+
#include "fd-util.h"
5+
#include "log.h"
6+
#include "main-func.h"
7+
#include "missing_syscall.h"
8+
#include "quota-util.h"
9+
#include "userdb.h"
10+
11+
static int show_quota(uid_t uid, const char *path) {
12+
int r;
13+
14+
_cleanup_close_ int fd = open(path, O_DIRECTORY|O_CLOEXEC);
15+
if (fd < 0)
16+
return log_error_errno(errno, "Failed to open '%s': %m", path);
17+
18+
struct dqblk req;
19+
r = quotactl_fd_with_fallback(fd, QCMD_FIXED(Q_GETQUOTA, USRQUOTA), uid, &req);
20+
if (r == -ESRCH) {
21+
log_info_errno(r, "No quota set on %s for UID "UID_FMT": %m", path, uid);
22+
return 0;
23+
}
24+
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
25+
return log_warning_errno(r, "No UID quota support on %s: %m", path);
26+
if (ERRNO_IS_NEG_PRIVILEGE(r))
27+
return log_error_errno(r, "Lacking privileges to query UID quota on %s: %m", path);
28+
if (r < 0)
29+
return log_error_errno(r, "Failed to query disk quota on %s for UID "UID_FMT": %m", path, uid);
30+
31+
printf("** Quota on %s for UID "UID_FMT" **\n"
32+
"block hardlimit: %"PRIu64"\n"
33+
"block softlimit: %"PRIu64"\n"
34+
"blocks current: %"PRIu64"\n"
35+
"inodes hardlimit: %"PRIu64"\n"
36+
"inodes softlimit: %"PRIu64"\n"
37+
"inodes current: %"PRIu64"\n"
38+
"excess block time: %"PRIu64"\n"
39+
"excess inode time: %"PRIu64"\n"
40+
"validity mask: 0x%"PRIx32,
41+
path, uid,
42+
req.dqb_bhardlimit,
43+
req.dqb_bsoftlimit,
44+
req.dqb_curspace,
45+
req.dqb_ihardlimit,
46+
req.dqb_isoftlimit,
47+
req.dqb_curinodes,
48+
req.dqb_btime,
49+
req.dqb_itime,
50+
req.dqb_valid);
51+
52+
const char* fields[] = {"BLIMITS", "SPACE", "INODES", "BTIME", "ITIME"};
53+
bool first = true;
54+
for (size_t i = 0; i < ELEMENTSOF(fields); i++)
55+
if (BIT_SET(req.dqb_valid, i)) {
56+
printf("%c%s", first ? ' ' : '|', fields[i]);
57+
first = false;
58+
}
59+
printf("%s\n", first ? "(none)" : "");
60+
61+
return 0;
62+
}
63+
64+
static int run(int argc, char **argv) {
65+
int r;
66+
67+
if (argc < 2)
68+
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
69+
"This program requires at least one argument\n"
70+
"syntax: test-display-quota USER PATH…");
71+
72+
const char *user = argv[1];
73+
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
74+
r = userdb_by_name(user, /* match= */ NULL, USERDB_PARSE_NUMERIC|USERDB_SUPPRESS_SHADOW, &ur);
75+
if (r < 0)
76+
return log_error_errno(r, "Failed to resolve user '%s': %m", user);
77+
78+
if (!uid_is_valid(ur->uid))
79+
return log_error_errno(SYNTHETIC_ERRNO(ENOMSG), "User '%s' lacks UID.", ur->user_name);
80+
81+
r = 0;
82+
STRV_FOREACH(path, strv_skip(argv, 2))
83+
RET_GATHER(r, show_quota(ur->uid, *path));
84+
85+
return r;
86+
}
87+
88+
DEFINE_MAIN_FUNCTION(run);

test/units/TEST-46-HOMED.sh

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -665,24 +665,29 @@ getent passwd aliastest@myrealm
665665
getent passwd aliastest2@myrealm
666666
getent passwd aliastest3@myrealm
667667

668-
if findmnt -n -o options /tmp | grep -q usrquota ; then
669-
670-
NEWPASSWORD=quux homectl create tmpfsquota --storage=subvolume --dev-shm-limit=50K -P
671-
672-
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/urandom of=/dev/shm/quotatestfile1 bs=1024 count=30
673-
(! run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/urandom of=/dev/shm/quotatestfile2 bs=1024 count=30)
674-
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm /dev/shm/quotatestfile1 /dev/shm/quotatestfile2
675-
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/urandom of=/dev/shm/quotatestfile1 bs=1024 count=30
676-
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm /dev/shm/quotatestfile1
677-
678-
systemctl stop user@"$(id -u tmpfsquota)".service
668+
NEWPASSWORD=quux homectl create tmpfsquota --storage=subvolume --dev-shm-limit=50K --tmp-limit=50K -P
669+
for p in /dev/shm /tmp; do
670+
if findmnt -n -o options "$p" | grep -q usrquota; then
671+
# Check if we can display the quotas. If we cannot, than it's likely
672+
# that PID1 was also not able to set the limits and we should not fail
673+
# in the tests below.
674+
/usr/lib/systemd/tests/unit-tests/manual/test-display-quota tmpfsquota "$p" || set +e
675+
676+
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile1" bs=1024 count=30
677+
(! run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile2" bs=1024 count=30)
678+
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm "$p/quotatestfile1" "$p/quotatestfile2"
679+
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile1" bs=1024 count=30
680+
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm "$p/quotatestfile1"
681+
682+
set -e
683+
fi
684+
done
679685

680-
wait_for_state tmpfsquota inactive
681-
homectl remove tmpfsquota
682-
fi
686+
systemctl stop user@"$(id -u tmpfsquota)".service
687+
wait_for_state tmpfsquota inactive
688+
homectl remove tmpfsquota
683689

684690
NEWPASSWORD=quux homectl create subareatest --storage=subvolume -P
685-
686691
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest mkdir Areas
687692
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest cp -av /etc/skel Areas/furb
688693
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
705710
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"
706711

707712
# Install a PK rule that allows 'subareatest' user to invoke run0 without password, just for testing
708-
cat > /usr/share/polkit-1/rules.d/subareatest.rules <<'EOF'
713+
cat >/usr/share/polkit-1/rules.d/subareatest.rules <<'EOF'
709714
polkit.addRule(function(action, subject) {
710715
if (action.id == "org.freedesktop.systemd1.manage-units" &&
711716
subject.user == "subareatest") {

0 commit comments

Comments
 (0)