core: add quota support for State, Cache, and Log exec directories

This commit is contained in:
Andres Beltran
2025-07-01 17:37:48 +00:00
parent 744086b58d
commit 26c6f3271a
9 changed files with 664 additions and 9 deletions

View File

@@ -0,0 +1,14 @@
---
title: Project IDs for Disk Quotas on Exec Directories
category: Exec directories
layout: default
SPDX-License-Identifier: LGPL-2.1-or-later
---
# Project IDs on systemd Systems
Project IDs are needed to enforce disk quotas for Exec Directories.
Project IDs are unsigned, 32-bit integers. For disk quota enforcement,
the range used is 2147483648 - 4294967294, which is the highest range
inspired from `UIDS-GUID.md`. The range is defined through `PROJ_ID_MIN`
and `PROJ_ID_MAX` in `exec-invoke.c`.

View File

@@ -257,10 +257,16 @@ All execution-related settings are available for transient units.
✓ RuntimeDirectoryMode= ✓ RuntimeDirectoryMode=
✓ RuntimeDirectory= ✓ RuntimeDirectory=
✓ StateDirectoryMode= ✓ StateDirectoryMode=
✓ StateDirectoryAccounting=
✓ StateDirectoryQuota=
✓ StateDirectory= ✓ StateDirectory=
✓ CacheDirectoryMode= ✓ CacheDirectoryMode=
✓ CacheDirectoryAccounting=
✓ CacheDirectoryQuota=
✓ CacheDirectory= ✓ CacheDirectory=
✓ LogsDirectoryMode= ✓ LogsDirectoryMode=
✓ LogsDirectoryAccounting=
✓ LogsDirectoryQuota=
✓ LogsDirectory= ✓ LogsDirectory=
✓ ConfigurationDirectoryMode= ✓ ConfigurationDirectoryMode=
✓ ConfigurationDirectory= ✓ ConfigurationDirectory=

View File

@@ -3288,18 +3288,30 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u StateDirectoryMode = ...; readonly u StateDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b StateDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) StateDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as StateDirectory = ['...', ...]; readonly as StateDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) CacheDirectorySymlink = [...]; readonly a(sst) CacheDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u CacheDirectoryMode = ...; readonly u CacheDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b CacheDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) CacheDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as CacheDirectory = ['...', ...]; readonly as CacheDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) LogsDirectorySymlink = [...]; readonly a(sst) LogsDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u LogsDirectoryMode = ...; readonly u LogsDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b LogsDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) LogsDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as LogsDirectory = ['...', ...]; readonly as LogsDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u ConfigurationDirectoryMode = ...; readonly u ConfigurationDirectoryMode = ...;
@@ -3351,6 +3363,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly s MountImagePolicy = '...'; readonly s MountImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ExtensionImagePolicy = '...'; readonly s ExtensionImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) StateDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) CacheDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) LogsDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...'; readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -3885,10 +3903,22 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property StateDirectoryMode is not documented!--> <!--property StateDirectoryMode is not documented!-->
<!--property StateDirectoryAccounting is not documented!-->
<!--property StateDirectoryQuota is not documented!-->
<!--property CacheDirectoryMode is not documented!--> <!--property CacheDirectoryMode is not documented!-->
<!--property CacheDirectoryAccounting is not documented!-->
<!--property CacheDirectoryQuota is not documented!-->
<!--property LogsDirectoryMode is not documented!--> <!--property LogsDirectoryMode is not documented!-->
<!--property LogsDirectoryAccounting is not documented!-->
<!--property LogsDirectoryQuota is not documented!-->
<!--property ConfigurationDirectoryMode is not documented!--> <!--property ConfigurationDirectoryMode is not documented!-->
<!--property ConfigurationDirectory is not documented!--> <!--property ConfigurationDirectory is not documented!-->
@@ -3935,6 +3965,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property ExtensionImagePolicy is not documented!--> <!--property ExtensionImagePolicy is not documented!-->
<!--property StateDirectoryQuotaUsage is not documented!-->
<!--property CacheDirectoryQuotaUsage is not documented!-->
<!--property LogsDirectoryQuotaUsage is not documented!-->
<!--property KillMode is not documented!--> <!--property KillMode is not documented!-->
<!--property KillSignal is not documented!--> <!--property KillSignal is not documented!-->
@@ -4579,18 +4615,30 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
@@ -4643,6 +4691,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/> <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/> <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/> <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
@@ -5443,18 +5497,30 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u StateDirectoryMode = ...; readonly u StateDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b StateDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) StateDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as StateDirectory = ['...', ...]; readonly as StateDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) CacheDirectorySymlink = [...]; readonly a(sst) CacheDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u CacheDirectoryMode = ...; readonly u CacheDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b CacheDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) CacheDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as CacheDirectory = ['...', ...]; readonly as CacheDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) LogsDirectorySymlink = [...]; readonly a(sst) LogsDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u LogsDirectoryMode = ...; readonly u LogsDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b LogsDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) LogsDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as LogsDirectory = ['...', ...]; readonly as LogsDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u ConfigurationDirectoryMode = ...; readonly u ConfigurationDirectoryMode = ...;
@@ -5506,6 +5572,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
readonly s MountImagePolicy = '...'; readonly s MountImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ExtensionImagePolicy = '...'; readonly s ExtensionImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) StateDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) CacheDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) LogsDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...'; readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -6060,10 +6132,22 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property StateDirectoryMode is not documented!--> <!--property StateDirectoryMode is not documented!-->
<!--property StateDirectoryAccounting is not documented!-->
<!--property StateDirectoryQuota is not documented!-->
<!--property CacheDirectoryMode is not documented!--> <!--property CacheDirectoryMode is not documented!-->
<!--property CacheDirectoryAccounting is not documented!-->
<!--property CacheDirectoryQuota is not documented!-->
<!--property LogsDirectoryMode is not documented!--> <!--property LogsDirectoryMode is not documented!-->
<!--property LogsDirectoryAccounting is not documented!-->
<!--property LogsDirectoryQuota is not documented!-->
<!--property ConfigurationDirectoryMode is not documented!--> <!--property ConfigurationDirectoryMode is not documented!-->
<!--property ConfigurationDirectory is not documented!--> <!--property ConfigurationDirectory is not documented!-->
@@ -6110,6 +6194,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property ExtensionImagePolicy is not documented!--> <!--property ExtensionImagePolicy is not documented!-->
<!--property StateDirectoryQuotaUsage is not documented!-->
<!--property CacheDirectoryQuotaUsage is not documented!-->
<!--property LogsDirectoryQuotaUsage is not documented!-->
<!--property KillMode is not documented!--> <!--property KillMode is not documented!-->
<!--property KillSignal is not documented!--> <!--property KillSignal is not documented!-->
@@ -6734,18 +6824,30 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
@@ -6798,6 +6900,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/> <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/> <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/> <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
@@ -7422,18 +7530,30 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u StateDirectoryMode = ...; readonly u StateDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b StateDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) StateDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as StateDirectory = ['...', ...]; readonly as StateDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) CacheDirectorySymlink = [...]; readonly a(sst) CacheDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u CacheDirectoryMode = ...; readonly u CacheDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b CacheDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) CacheDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as CacheDirectory = ['...', ...]; readonly as CacheDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) LogsDirectorySymlink = [...]; readonly a(sst) LogsDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u LogsDirectoryMode = ...; readonly u LogsDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b LogsDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) LogsDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as LogsDirectory = ['...', ...]; readonly as LogsDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u ConfigurationDirectoryMode = ...; readonly u ConfigurationDirectoryMode = ...;
@@ -7485,6 +7605,12 @@ node /org/freedesktop/systemd1/unit/home_2emount {
readonly s MountImagePolicy = '...'; readonly s MountImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ExtensionImagePolicy = '...'; readonly s ExtensionImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) StateDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) CacheDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) LogsDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...'; readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -7961,10 +8087,22 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property StateDirectoryMode is not documented!--> <!--property StateDirectoryMode is not documented!-->
<!--property StateDirectoryAccounting is not documented!-->
<!--property StateDirectoryQuota is not documented!-->
<!--property CacheDirectoryMode is not documented!--> <!--property CacheDirectoryMode is not documented!-->
<!--property CacheDirectoryAccounting is not documented!-->
<!--property CacheDirectoryQuota is not documented!-->
<!--property LogsDirectoryMode is not documented!--> <!--property LogsDirectoryMode is not documented!-->
<!--property LogsDirectoryAccounting is not documented!-->
<!--property LogsDirectoryQuota is not documented!-->
<!--property ConfigurationDirectoryMode is not documented!--> <!--property ConfigurationDirectoryMode is not documented!-->
<!--property ConfigurationDirectory is not documented!--> <!--property ConfigurationDirectory is not documented!-->
@@ -8011,6 +8149,12 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property ExtensionImagePolicy is not documented!--> <!--property ExtensionImagePolicy is not documented!-->
<!--property StateDirectoryQuotaUsage is not documented!-->
<!--property CacheDirectoryQuotaUsage is not documented!-->
<!--property LogsDirectoryQuotaUsage is not documented!-->
<!--property KillMode is not documented!--> <!--property KillMode is not documented!-->
<!--property KillSignal is not documented!--> <!--property KillSignal is not documented!-->
@@ -8543,18 +8687,30 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
@@ -8607,6 +8763,12 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/> <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/> <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/> <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
@@ -9364,18 +9526,30 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u StateDirectoryMode = ...; readonly u StateDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b StateDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) StateDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as StateDirectory = ['...', ...]; readonly as StateDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) CacheDirectorySymlink = [...]; readonly a(sst) CacheDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u CacheDirectoryMode = ...; readonly u CacheDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b CacheDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) CacheDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as CacheDirectory = ['...', ...]; readonly as CacheDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sst) LogsDirectorySymlink = [...]; readonly a(sst) LogsDirectorySymlink = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u LogsDirectoryMode = ...; readonly u LogsDirectoryMode = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b LogsDirectoryAccounting = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly (tus) LogsDirectoryQuota = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as LogsDirectory = ['...', ...]; readonly as LogsDirectory = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u ConfigurationDirectoryMode = ...; readonly u ConfigurationDirectoryMode = ...;
@@ -9427,6 +9601,12 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
readonly s MountImagePolicy = '...'; readonly s MountImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ExtensionImagePolicy = '...'; readonly s ExtensionImagePolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) StateDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) CacheDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (tt) LogsDirectoryQuotaUsage = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...'; readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -9885,10 +10065,22 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property StateDirectoryMode is not documented!--> <!--property StateDirectoryMode is not documented!-->
<!--property StateDirectoryAccounting is not documented!-->
<!--property StateDirectoryQuota is not documented!-->
<!--property CacheDirectoryMode is not documented!--> <!--property CacheDirectoryMode is not documented!-->
<!--property CacheDirectoryAccounting is not documented!-->
<!--property CacheDirectoryQuota is not documented!-->
<!--property LogsDirectoryMode is not documented!--> <!--property LogsDirectoryMode is not documented!-->
<!--property LogsDirectoryAccounting is not documented!-->
<!--property LogsDirectoryQuota is not documented!-->
<!--property ConfigurationDirectoryMode is not documented!--> <!--property ConfigurationDirectoryMode is not documented!-->
<!--property ConfigurationDirectory is not documented!--> <!--property ConfigurationDirectory is not documented!-->
@@ -9935,6 +10127,12 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property ExtensionImagePolicy is not documented!--> <!--property ExtensionImagePolicy is not documented!-->
<!--property StateDirectoryQuotaUsage is not documented!-->
<!--property CacheDirectoryQuotaUsage is not documented!-->
<!--property LogsDirectoryQuotaUsage is not documented!-->
<!--property KillMode is not documented!--> <!--property KillMode is not documented!-->
<!--property KillSignal is not documented!--> <!--property KillSignal is not documented!-->
@@ -10449,18 +10647,30 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryAccounting"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuota"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/> <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
<variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/> <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
@@ -10513,6 +10723,12 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/> <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImagePolicy"/>
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryQuotaUsage"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/> <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/> <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
@@ -12076,8 +12292,17 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>PrivateUsersEx</varname>, and <varname>PrivateUsersEx</varname>, and
<varname>PrivatePIDs</varname> were added in version 257.</para> <varname>PrivatePIDs</varname> were added in version 257.</para>
<para><varname>ProtectHostnameEx</varname>, <para><varname>ProtectHostnameEx</varname>,
<varname>DelegateNamespaces</varname>, and <varname>DelegateNamespaces</varname>,
<function>RemoveSubGroup()</function> were added in version 258.</para> <function>RemoveSubGroup()</function>,
<varname>StateDirectoryQuota</varname>,
<varname>StateDirectoryQuotaUsage</varname>,
<varname>StateDirectoryAccounting</varname>,
<varname>CacheDirectoryQuota</varname>,
<varname>CacheDirectoryQuotaUsage</varname>,
<varname>CacheDirectoryAccounting</varname>,
<varname>LogsDirectoryQuota</varname>,
<varname>LogsDirectoryQuotaUsage</varname>, and
<varname>LogsDirectoryAccounting</varname>, were added in version 258.</para>
</refsect2> </refsect2>
<refsect2> <refsect2>
<title>Socket Unit Objects</title> <title>Socket Unit Objects</title>
@@ -12126,8 +12351,17 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>AcceptFileDescriptors</varname>, <varname>AcceptFileDescriptors</varname>,
<varname>DelegateNamespaces</varname>, <varname>DelegateNamespaces</varname>,
<function>RemoveSubgroup()</function>, <function>RemoveSubgroup()</function>,
<varname>DeferTrigger</varname>, and <varname>DeferTrigger</varname>,
<varname>DeferTriggerMaxUSec</varname> were added in version 258.</para> <varname>DeferTriggerMaxUSec</varname>,
<varname>StateDirectoryQuota</varname>,
<varname>StateDirectoryQuotaUsage</varname>,
<varname>StateDirectoryAccounting</varname>,
<varname>CacheDirectoryQuota</varname>,
<varname>CacheDirectoryQuotaUsage</varname>,
<varname>CacheDirectoryAccounting</varname>,
<varname>LogsDirectoryQuota</varname>,
<varname>LogsDirectoryQuotaUsage</varname>, and
<varname>LogsDirectoryAccounting</varname>, were added in version 258.</para>
</refsect2> </refsect2>
<refsect2> <refsect2>
<title>Mount Unit Objects</title> <title>Mount Unit Objects</title>
@@ -12171,8 +12405,17 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<para><varname>ProtectHostnameEx</varname>, <para><varname>ProtectHostnameEx</varname>,
<varname>DelegateNamespaces</varname>, <varname>DelegateNamespaces</varname>,
<function>RemoveSubgroup()</function>, <function>RemoveSubgroup()</function>,
<varname>ReloadResult</varname>, and <varname>ReloadResult</varname>,
<varname>CleanResult</varname> were added in version 258.</para> <varname>CleanResult</varname>,
<varname>StateDirectoryQuota</varname>,
<varname>StateDirectoryQuotaUsage</varname>,
<varname>StateDirectoryAccounting</varname>,
<varname>CacheDirectoryQuota</varname>,
<varname>CacheDirectoryQuotaUsage</varname>,
<varname>CacheDirectoryAccounting</varname>,
<varname>LogsDirectoryQuota</varname>,
<varname>LogsDirectoryQuotaUsage</varname>, and
<varname>LogsDirectoryAccounting</varname>, were added in version 258.</para>
</refsect2> </refsect2>
<refsect2> <refsect2>
<title>Swap Unit Objects</title> <title>Swap Unit Objects</title>
@@ -12214,8 +12457,17 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>ProtectControlGroupsEx</varname>, and <varname>ProtectControlGroupsEx</varname>, and
<varname>PrivatePIDs</varname> were added in version 257.</para> <varname>PrivatePIDs</varname> were added in version 257.</para>
<para><varname>ProtectHostnameEx</varname>, <para><varname>ProtectHostnameEx</varname>,
<varname>DelegateNamespaces</varname>, and <varname>DelegateNamespaces</varname>,
<function>RemoveSubgroup()</function> were added in version 258.</para> <function>RemoveSubgroup()</function>,
<varname>StateDirectoryQuota</varname>,
<varname>StateDirectoryQuotaUsage</varname>,
<varname>StateDirectoryAccounting</varname>,
<varname>CacheDirectoryQuota</varname>,
<varname>CacheDirectoryQuotaUsage</varname>,
<varname>CacheDirectoryAccounting</varname>,
<varname>LogsDirectoryQuota</varname>,
<varname>LogsDirectoryQuotaUsage</varname>, and
<varname>LogsDirectoryAccounting</varname>, were added in version 258.</para>
</refsect2> </refsect2>
<refsect2> <refsect2>
<title>Slice Unit Objects</title> <title>Slice Unit Objects</title>

View File

@@ -1680,6 +1680,46 @@ StateDirectory=aaa/bbb ccc</programlisting>
<xi:include href="version-info.xml" xpointer="v234"/></listitem> <xi:include href="version-info.xml" xpointer="v234"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>StateDirectoryQuota=</varname></term>
<term><varname>CacheDirectoryQuota=</varname></term>
<term><varname>LogsDirectoryQuota=</varname></term>
<listitem><para>Specifies the storage limits for the directories specified in <varname>StateDirectory=</varname>,
<varname>CacheDirectory=</varname>, or <varname>LogsDirectory=</varname> respectively.</para>
<para>The storage quota is defined in terms of disk blocks and inodes, as per
<ulink url="https://man7.org/linux/man-pages/man2/quotactl.2.html">quotactl</ulink>. Takes an absolute size limit
in bytes. If the value is suffixed with K, M, G or T, the specified size is parsed as Kilobytes, Megabytes, Gigabytes,
or Terabytes (with the base 1024), respectively. If an absolute size limit is specified, only the block quota is set
(rounded up to the nearest block). Alternatively, a percentage value may be specified, which applies the same percent
quota to both blocks and inodes. Defaults to <constant>off</constant>, in which case no storage limits will be set.</para>
<para>Only hard limits are set, not soft limits. If the underlying filesystem for the specified directories does not
support project quotas, the specified storage limits will not be set. In addition to enabling per-unit quotas with
these settings, it is necessary to enable <constant>prjquota</constant> on the file system level as well
(i.e. <command>tune2fs -Q prjquota</command>). Quotas must also be turned on with
<ulink url="https://linux.die.net/man/8/quotaon">quotaon.</ulink></para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>StateDirectoryAccounting=</varname></term>
<term><varname>CacheDirectoryAccounting=</varname></term>
<term><varname>LogsDirectoryAccounting=</varname></term>
<listitem><para>Takes a boolean argument. If true, a project ID is assigned to the directories specified in
<varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>, or <varname>LogsDirectory=</varname>
respectively, which is used for tracking disk usage when disk quotas are turned on
(see <ulink url="https://man7.org/linux/man-pages/man8/repquota.8.html">repquota</ulink>). Defaults to false.</para>
<para>To set and enforce disk quotas, <varname>StateDirectoryQuota=</varname>, <varname>CacheDirectoryQuota=</varname>,
or <varname>LogsDirectoryQuota=</varname> must be specified.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>RuntimeDirectoryPreserve=</varname></term> <term><varname>RuntimeDirectoryPreserve=</varname></term>

View File

@@ -10,6 +10,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/statvfs.h>
#include <unistd.h> #include <unistd.h>
#if HAVE_PAM #if HAVE_PAM
@@ -30,6 +31,7 @@
#include "cgroup-setup.h" #include "cgroup-setup.h"
#include "cgroup.h" #include "cgroup.h"
#include "chase.h" #include "chase.h"
#include "chattr-util.h"
#include "chown-recursive.h" #include "chown-recursive.h"
#include "constants.h" #include "constants.h"
#include "copy.h" #include "copy.h"
@@ -60,15 +62,19 @@
#include "open-file.h" #include "open-file.h"
#include "osc-context.h" #include "osc-context.h"
#include "path-util.h" #include "path-util.h"
#include "percent-util.h"
#include "pidref.h" #include "pidref.h"
#include "proc-cmdline.h" #include "proc-cmdline.h"
#include "process-util.h" #include "process-util.h"
#include "psi-util.h" #include "psi-util.h"
#include "quota-util.h"
#include "random-util.h"
#include "rlimit-util.h" #include "rlimit-util.h"
#include "seccomp-util.h" #include "seccomp-util.h"
#include "selinux-util.h" #include "selinux-util.h"
#include "set.h" #include "set.h"
#include "signal-util.h" #include "signal-util.h"
#include "siphash24.h"
#include "smack-util.h" #include "smack-util.h"
#include "socket-util.h" #include "socket-util.h"
#include "stat-util.h" #include "stat-util.h"
@@ -84,6 +90,11 @@
#define SNDBUF_SIZE (8*1024*1024) #define SNDBUF_SIZE (8*1024*1024)
/* Project id range for disk quotas */
#define PROJ_ID_MIN UINT32_C(2147483648)
#define PROJ_ID_MAX UINT32_C(4294967294)
#define PROJ_ID_CLAMP_INTO_QUOTA_RANGE(id) ((uint32_t) ((id) % (PROJ_ID_MAX - PROJ_ID_MIN + 1)) + PROJ_ID_MIN)
static int flag_fds( static int flag_fds(
const int fds[], const int fds[],
size_t n_socket_fds, size_t n_socket_fds,
@@ -2575,6 +2586,217 @@ static int create_many_symlinks(const char *root, const char *source, char **sym
return 0; return 0;
} }
static int set_exec_storage_quota(int fd, uint32_t proj_id, const QuotaLimit *ql) {
int r;
uint64_t block_limit = 0, inode_limit = 0;
assert(fd >= 0);
assert(ql);
if (ql->quota_absolute == 0 || ql->quota_scale == 0)
/* Limit of 0 means no usage is allowed. For quotactl, use 1 as the limit, since 0 means that
* hard limits are disabled */
block_limit = inode_limit = 1;
else if (ql->quota_absolute == UINT64_MAX) {
_cleanup_close_ int fd_parent = -EBADF;
/* Use target_dir's parent when setting quotas. If a FD for target_dir has been previously
* used for quotactl_fd(SET) and is passed again for fstatvfs(), the total number of blocks is not
* reported accurately (instead, the block limit is reported as total blocks). Thus, use the FD
* associated with the parent, so that total blocks is accurate */
fd_parent = openat(fd, "..", O_PATH|O_CLOEXEC|O_DIRECTORY);
if (fd_parent < 0)
return -errno;
uint32_t xattr_flags = 0;
r = read_fs_xattr_fd(fd_parent, &xattr_flags, /* ret_projid = */ NULL);
if (r < 0)
return r;
/* Refuse if parent has FS_XFLAG_PROJINHERIT since this will mean the total number of blocks will not
* be reported accurately */
if (FLAGS_SET(xattr_flags, FS_XFLAG_PROJINHERIT))
return -ENOMEDIUM;
struct statvfs disk_st;
if (fstatvfs(fd_parent, &disk_st) < 0)
return -errno;
block_limit = (uint64_t) DIV_ROUND_UP((uint64_t)((double) (disk_st.f_frsize * disk_st.f_blocks) / UINT32_MAX * ql->quota_scale), QIF_DQBLKSIZE);
inode_limit = (uint64_t) ((double) disk_st.f_files / UINT32_MAX * ql->quota_scale);
} else
block_limit = (uint64_t) DIV_ROUND_UP(ql->quota_absolute, QIF_DQBLKSIZE);
struct dqblk req = {
.dqb_bhardlimit = block_limit,
.dqb_ihardlimit = inode_limit,
.dqb_valid = QIF_LIMITS,
};
r = quotactl_fd_with_fallback(fd, QCMD_FIXED(Q_SETQUOTA, PRJQUOTA), proj_id, &req);
if (r < 0)
return r;
log_debug("Storage quotas set for project id %" PRIu32 ". Block limit = %" PRIu64 ", inode limit = %" PRIu64, proj_id, block_limit, inode_limit);
return 0;
}
static int unset_exec_storage_quota(int fd, uint32_t proj_id, bool quota_accounting) {
int r, quota_supported;
struct dqblk req;
assert(fd >= 0);
quota_supported = quota_query_proj_id(fd, proj_id, &req);
if (quota_supported < 0)
return log_debug_errno(quota_supported, "Failed to query disk quota for project ID %" PRIu32 ": %m", proj_id);
/* Do not enforce quotas anymore */
if (quota_supported && FLAGS_SET(req.dqb_valid, QIF_BLIMITS) && (req.dqb_bhardlimit > 0 || req.dqb_ihardlimit > 0)) {
req.dqb_bhardlimit = 0, req.dqb_ihardlimit = 0;
r = quotactl_fd_with_fallback(fd, QCMD_FIXED(Q_SETQUOTA, PRJQUOTA), proj_id, &req);
if (r < 0)
return log_debug_errno(r, "Failed to disable project quotas for project ID %" PRIu32 ": %m", proj_id);
log_debug("Storage quotas for project ID %" PRIu32 " were disabled", proj_id);
}
/* Release project ID if no accounting needed */
if (!quota_accounting) {
r = set_proj_id_recursive(fd, 0);
if (r < 0)
log_warning_errno(r, "Failed to release project ID %" PRIu32 ", ignoring: %m", proj_id);
}
return 0;
}
static int apply_exec_quotas(
const char *target_dir,
const char *cgroup_path,
ExecDirectoryType type,
const QuotaLimit *ql,
uint32_t *exec_dt_proj_id, /* in/out */
bool *already_enforced) { /* in/out */
_cleanup_close_ int fd = -EBADF;
int r, quota_supported = 0;
assert(target_dir);
assert(cgroup_path);
assert(ql);
assert(exec_dt_proj_id);
assert(already_enforced);
/* Do not apply to the Runtime directory since tmpfs does not support project IDs yet */
if (!IN_SET(type, EXEC_DIRECTORY_STATE, EXEC_DIRECTORY_CACHE, EXEC_DIRECTORY_LOGS))
return 0;
fd = open(target_dir, O_PATH|O_CLOEXEC|O_DIRECTORY);
if (fd < 0)
return log_debug_errno(errno, "Failed to open %s: %m", target_dir);
/* Get the project ID of the current directory */
uint32_t proj_id;
r = read_fs_xattr_fd(fd, /* ret_xflags = */ NULL, &proj_id);
if (ERRNO_IS_NEG_IOCTL_NOT_SUPPORTED(r)) {
log_debug_errno(r, "Not applying storage quotas. FS_IOC_FSGETXATTR not supported for %s: %m", target_dir);
return 0;
}
if (r < 0)
return log_debug_errno(r, "Failed to retrieve project ID for %s: %m", target_dir);
/* If the first directory of this ExecType already has a project ID, adopt it as the project ID for all dirs of this ExecType */
bool proj_id_exists = PROJ_ID_MIN <= proj_id && proj_id <= PROJ_ID_MAX;
if (proj_id_exists && *exec_dt_proj_id == 0)
*exec_dt_proj_id = proj_id;
/* Check if enforcement should be disabled. Do not release project ID if accounting is enabled */
if (!ql->quota_enforce) {
if (proj_id_exists) {
r = unset_exec_storage_quota(fd, proj_id, ql->quota_accounting);
if (r < 0)
return log_debug_errno(r, "Failed to unset project quotas for %s: %m", target_dir);
}
if (!ql->quota_accounting)
return 0;
}
if (*exec_dt_proj_id > 0 && *exec_dt_proj_id != proj_id) {
/* Set the existing project ID only if the current directory's ID does not exist or does not match */
proj_id = *exec_dt_proj_id;
r = quota_proj_id_set_recursive(fd, proj_id, false);
if (r < 0)
return log_debug_errno(r, "Failed to set project ID for %s: %m", target_dir);
} else if (*exec_dt_proj_id == 0) {
/* Only generate a new project ID if it's the first directory of this ExecType to be processed and does not have an existing ID */
static const sd_id128_t k = SD_ID128_ARRAY(e1,4a,79,9b,64,40,41,4a,a8,46,c2,f3,f9,19,4f,01);
_cleanup_free_ char *proj_id_plain = NULL;
/* Generate candidate project id */
proj_id_plain = strjoin(cgroup_path, "|", exec_directory_type_to_string(type));
if (!proj_id_plain)
return log_oom_debug();
struct siphash state;
siphash24_init(&state, k.bytes);
siphash24_compress_string(proj_id_plain, &state);
proj_id = PROJ_ID_CLAMP_INTO_QUOTA_RANGE(siphash24_finalize(&state));
#define MAX_PROJ_ID_RETRIES 10
for (unsigned attempt = 0;; attempt++) {
if (attempt >= MAX_PROJ_ID_RETRIES)
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Failed to generate unique project ID for %s: %m", target_dir);
/* Check if project quotas are supported */
struct dqblk req;
quota_supported = quota_query_proj_id(fd, proj_id, &req);
if (quota_supported < 0)
return log_debug_errno(quota_supported, "Failed to query disk quota for project ID %" PRIu32 ": %m", proj_id);
if (!quota_supported) {
log_debug("Not applying storage quotas. Project quotas are not supported for %s", target_dir);
return 0;
}
if (!quota_dqblk_is_populated(&req)) {
int proj_id_was_set = quota_proj_id_set_recursive(fd, proj_id, true);
if (proj_id_was_set < 0)
return log_debug_errno(proj_id_was_set, "Failed to set project ID for %s: %m", target_dir);
if (proj_id_was_set) {
*exec_dt_proj_id = proj_id;
log_debug("Project ID %u generated for %s", proj_id, target_dir);
break;
}
}
proj_id = (uint32_t) (random_u64_range(PROJ_ID_MAX - PROJ_ID_MIN + 1) + PROJ_ID_MIN);
}
}
if (ql->quota_enforce && !*already_enforced) {
if (!quota_supported) {
struct dqblk req;
quota_supported = quota_query_proj_id(fd, proj_id, &req);
if (quota_supported < 0)
return log_debug_errno(quota_supported, "Failed to query disk quota for project ID %" PRIu32 ": %m", proj_id);
if (!quota_supported) {
log_debug("Not applying storage quotas. Project quotas are not supported for %s", target_dir);
return 0;
}
}
r = set_exec_storage_quota(fd, proj_id, ql);
if (r < 0)
return log_debug_errno(r, "Failed to set storage quotas for %s: %m", target_dir);
*already_enforced = true;
}
return r;
}
static int setup_exec_directory( static int setup_exec_directory(
const ExecContext *context, const ExecContext *context,
const ExecParameters *params, const ExecParameters *params,
@@ -2608,6 +2830,9 @@ static int setup_exec_directory(
gid = 0; gid = 0;
} }
uint32_t exec_dt_proj_id = 0;
bool quota_already_enforced = false;
FOREACH_ARRAY(i, context->directories[type].items, context->directories[type].n_items) { FOREACH_ARRAY(i, context->directories[type].items, context->directories[type].n_items) {
_cleanup_free_ char *p = NULL, *pp = NULL; _cleanup_free_ char *p = NULL, *pp = NULL;
@@ -2898,6 +3123,11 @@ static int setup_exec_directory(
if (r < 0) if (r < 0)
goto fail; goto fail;
} }
/* Apply storage quotas and accounting */
r = apply_exec_quotas(target_dir, params->cgroup_path, type, &context->directories[type].exec_quota, &exec_dt_proj_id, &quota_already_enforced);
if (r < 0)
goto fail;
} }
/* If we are not going to run in a namespace, set up the symlinks - otherwise /* If we are not going to run in a namespace, set up the symlinks - otherwise

View File

@@ -1848,6 +1848,28 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
r = serialize_item(f, key, value); r = serialize_item(f, key, value);
if (r < 0) if (r < 0)
return r; return r;
if (c->directories[dt].exec_quota.quota_enforce) {
_cleanup_free_ char *key_quota = NULL;
key_quota = strjoin("exec-context-quota-directories-", exec_directory_type_to_string(dt));
if (!key_quota)
return log_oom_debug();
r = serialize_item_format(f, key_quota, "%" PRIu64 " %" PRIu32, c->directories[dt].exec_quota.quota_absolute,
c->directories[dt].exec_quota.quota_scale);
if (r < 0)
return r;
} else if (c->directories[dt].exec_quota.quota_accounting) {
_cleanup_free_ char *key_quota = NULL;
key_quota = strjoin("exec-context-quota-accounting-directories-", exec_directory_type_to_string(dt));
if (!key_quota)
return log_oom_debug();
r = serialize_bool(f, key_quota, c->directories[dt].exec_quota.quota_accounting);
if (r < 0)
return r;
}
} }
r = serialize_usec(f, "exec-context-timeout-clean-usec", c->timeout_clean_usec); r = serialize_usec(f, "exec-context-timeout-clean-usec", c->timeout_clean_usec);
@@ -2735,7 +2757,7 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
dt = exec_directory_type_from_string(type); dt = exec_directory_type_from_string(type);
if (dt < 0) if (dt < 0)
return -EINVAL; return dt;
r = parse_mode(mode, &c->directories[dt].mode); r = parse_mode(mode, &c->directories[dt].mode);
if (r < 0) if (r < 0)
@@ -2793,6 +2815,48 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
return r; return r;
} }
} }
} else if ((val = startswith(l, "exec-context-quota-accounting-directories-"))) {
_cleanup_free_ char *type = NULL, *quota_accounting = NULL;
ExecDirectoryType dt;
r = split_pair(val, "=", &type, &quota_accounting);
if (r < 0)
return r;
dt = exec_directory_type_from_string(type);
if (dt < 0)
return dt;
r = parse_boolean(quota_accounting);
if (r < 0)
return r;
c->directories[dt].exec_quota.quota_accounting = r;
} else if ((val = startswith(l, "exec-context-quota-directories-"))) {
_cleanup_free_ char *type = NULL, *quota_info = NULL, *quota_absolute = NULL, *quota_scale = NULL;
ExecDirectoryType dt;
r = split_pair(val, "=", &type, &quota_info);
if (r < 0)
return r;
r = split_pair(quota_info, " ", &quota_absolute, &quota_scale);
if (r < 0)
return r;
dt = exec_directory_type_from_string(type);
if (dt < 0)
return dt;
r = safe_atou64(quota_absolute, &c->directories[dt].exec_quota.quota_absolute);
if (r < 0)
return r;
r = safe_atou32(quota_scale, &c->directories[dt].exec_quota.quota_scale);
if (r < 0)
return r;
c->directories[dt].exec_quota.quota_enforce = true;
} else if ((val = startswith(l, "exec-context-timeout-clean-usec="))) { } else if ((val = startswith(l, "exec-context-timeout-clean-usec="))) {
r = deserialize_usec(val, &c->timeout_clean_usec); r = deserialize_usec(val, &c->timeout_clean_usec);
if (r < 0) if (r < 0)

View File

@@ -145,10 +145,16 @@
{{type}}.RuntimeDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].mode) {{type}}.RuntimeDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].mode)
{{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME]) {{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME])
{{type}}.StateDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].mode) {{type}}.StateDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].mode)
{{type}}.StateDirectoryAccounting, config_parse_bool, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].exec_quota.quota_accounting)
{{type}}.StateDirectoryQuota, config_parse_exec_quota, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].exec_quota)
{{type}}.StateDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE]) {{type}}.StateDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE])
{{type}}.CacheDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].mode) {{type}}.CacheDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].mode)
{{type}}.CacheDirectoryAccounting, config_parse_bool, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].exec_quota.quota_accounting)
{{type}}.CacheDirectoryQuota, config_parse_exec_quota, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].exec_quota)
{{type}}.CacheDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE]) {{type}}.CacheDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE])
{{type}}.LogsDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].mode) {{type}}.LogsDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].mode)
{{type}}.LogsDirectoryAccounting, config_parse_bool, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].exec_quota.quota_accounting)
{{type}}.LogsDirectoryQuota, config_parse_exec_quota, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].exec_quota)
{{type}}.LogsDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS]) {{type}}.LogsDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS])
{{type}}.ConfigurationDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode) {{type}}.ConfigurationDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode)
{{type}}.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION]) {{type}}.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION])

View File

@@ -4602,6 +4602,48 @@ int config_parse_exec_directories(
} }
} }
int config_parse_exec_quota(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
QuotaLimit *quota_limit = ASSERT_PTR(data);
uint64_t quota_absolute = UINT64_MAX;
uint32_t quota_scale = UINT32_MAX;
int r;
if (isempty(rvalue) || streq(rvalue, "off")) {
quota_limit->quota_enforce = false;
quota_limit->quota_absolute = UINT64_MAX;
quota_limit->quota_scale = UINT32_MAX;
return 0;
}
r = parse_permyriad(rvalue);
if (r < 0) {
r = parse_size(rvalue, 1024, &quota_absolute);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse disk quota value, ignoring: %s", rvalue);
return 0;
}
} else
/* Normalize to 2^32-1 == 100% */
quota_scale = UINT32_SCALE_FROM_PERMYRIAD(r);
quota_limit->quota_absolute = quota_absolute;
quota_limit->quota_scale = quota_scale;
quota_limit->quota_enforce = true;
return 0;
}
int config_parse_set_credential( int config_parse_set_credential(
const char *unit, const char *unit,
const char *filename, const char *filename,

View File

@@ -101,6 +101,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_smack_process_label);
CONFIG_PARSER_PROTOTYPE(config_parse_address_families); CONFIG_PARSER_PROTOTYPE(config_parse_address_families);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_preserve_mode); CONFIG_PARSER_PROTOTYPE(config_parse_exec_preserve_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories); CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_quota);
CONFIG_PARSER_PROTOTYPE(config_parse_set_credential); CONFIG_PARSER_PROTOTYPE(config_parse_set_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_load_credential); CONFIG_PARSER_PROTOTYPE(config_parse_load_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_import_credential); CONFIG_PARSER_PROTOTYPE(config_parse_import_credential);