diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index 968b481449..9f3660bd46 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -185,68 +185,47 @@ L /tmp/foobar - - - - /dev/null
q
- Similar to v. However,
- makes sure that the subvolume will be assigned to the same
- higher-level quota groups as the subvolume it has been
- created in. This ensures that higher-level limits and
- accounting applied to the parent subvolume also include the
- specified subvolume. On non-btrfs file systems, this line
- type is identical to d. If the subvolume
- already exists and is already assigned to one or more higher
- level quota groups, no change to the quota hierarchy is
- made. Also see Q below. See btrfs-qgroup8
- for details about the btrfs quota group
- concept.
+ Similar to v. However, makes sure that the subvolume will be assigned to
+ the same higher-level quota groups as the subvolume it has been created in. This ensures that higher-level
+ limits and accounting applied to the parent subvolume also include the specified subvolume. On non-btrfs file
+ systems, this line type is identical to d.
+
+ If the subvolume already exists, no change to the quota hierarchy is made, regardless of whether the
+ subvolume is already attached to a quota group or not. Also see Q below. See btrfs-qgroup8 for
+ details about the btrfs quota group concept.
Q
- Similar to q. However,
- instead of copying the higher-level quota group assignments
- from the parent as-is, the lowest quota group of the parent
- subvolume is determined that is not the leaf quota
- group. Then, an "intermediary" quota group is inserted that
- is one level below this level, and shares the same ID part
- as the specified subvolume. If no higher-level quota group
- exists for the parent subvolume, a new quota group at level
- 255 sharing the same ID as the specified subvolume is
- inserted instead. This new intermediary quota group is then
- assigned to the parent subvolume's higher-level quota
- groups, and the specified subvolume's leaf quota group is
- assigned to it.
+ Similar to q. However, instead of copying the higher-level quota group
+ assignments from the parent as-is, the lowest quota group of the parent subvolume is determined that is not
+ the leaf quota group. Then, an "intermediary" quota group is inserted that is one level below this level, and
+ shares the same ID part as the specified subvolume. If no higher-level quota group exists for the parent
+ subvolume, a new quota group at level 255 sharing the same ID as the specified subvolume is inserted
+ instead. This new intermediary quota group is then assigned to the parent subvolume's higher-level quota
+ groups, and the specified subvolume's leaf quota group is assigned to it.
- Effectively, this has a similar effect as
- q, however introduces a new higher-level
- quota group for the specified subvolume that may be used to
- enforce limits and accounting to the specified subvolume and
- children subvolume created within it. Thus, by creating
- subvolumes only via q and
- Q, a concept of "subtree quotas" is
- implemented. Each subvolume for which Q
- is set will get a "subtree" quota group created, and all
- child subvolumes created within it will be assigned to
- it. Each subvolume for which q is set
- will not get such a "subtree" quota group, but it is ensured
- that they are added to the same "subtree" quota group as their
- immediate parents.
+ Effectively, this has a similar effect as q, however introduces a new higher-level
+ quota group for the specified subvolume that may be used to enforce limits and accounting to the specified
+ subvolume and children subvolume created within it. Thus, by creating subvolumes only via
+ q and Q, a concept of "subtree quotas" is implemented. Each subvolume
+ for which Q is set will get a "subtree" quota group created, and all child subvolumes
+ created within it will be assigned to it. Each subvolume for which q is set will not get
+ such a "subtree" quota group, but it is ensured that they are added to the same "subtree" quota group as
+ their immediate parents.
- It is recommended to use
- Q for subvolumes that typically contain
- further subvolumes, and where it is desirable to have
- accounting and quota limits on all child subvolumes
- together. Examples for Q are typically
- /home or
- /var/lib/machines. In contrast,
- q should be used for subvolumes that
- either usually do not include further subvolumes or where no
- accounting and quota limits are needed that apply to all
- child subvolumes together. Examples for q
- are typically /var or
- /var/tmp. As with Q,
- q has no effect on the quota group
- hierarchy if the subvolume exists and already has at least
- one higher-level quota group assigned.
+ It is recommended to use Q for subvolumes that typically contain further subvolumes,
+ and where it is desirable to have accounting and quota limits on all child subvolumes together. Examples for
+ Q are typically /home or /var/lib/machines. In
+ contrast, q should be used for subvolumes that either usually do not include further
+ subvolumes or where no accounting and quota limits are needed that apply to all child subvolumes
+ together. Examples for q are typically /var or
+ /var/tmp.
+
+ As with q, Q has no effect on the quota group hierarchy if the
+ subvolume already exists, regardless of whether the subvolume already belong to a quota group or
+ not.
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index cfd9044c5b..69c611e6c4 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1525,13 +1525,16 @@ static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
-static int create_directory_or_subvolume(const char *path, mode_t mode, bool subvol) {
+static int create_directory_or_subvolume(const char *path, mode_t mode, bool subvol, CreationMode *creation) {
_cleanup_close_ int pfd = -1;
- CreationMode creation;
+ CreationMode c;
int r;
assert(path);
+ if (!creation)
+ creation = &c;
+
pfd = path_open_parent_safe(path);
if (pfd < 0)
return pfd;
@@ -1577,11 +1580,11 @@ static int create_directory_or_subvolume(const char *path, mode_t mode, bool sub
return -EEXIST;
}
- creation = CREATION_EXISTING;
+ *creation = CREATION_EXISTING;
} else
- creation = CREATION_NORMAL;
+ *creation = CREATION_NORMAL;
- log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), path);
+ log_debug("%s directory \"%s\".", creation_mode_verb_to_string(*creation), path);
r = openat(pfd, basename(path), O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (r < 0)
@@ -1595,7 +1598,7 @@ static int create_directory(Item *i, const char *path) {
assert(i);
assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY));
- fd = create_directory_or_subvolume(path, i->mode, false);
+ fd = create_directory_or_subvolume(path, i->mode, false, NULL);
if (fd == -EEXIST)
return 0;
if (fd < 0)
@@ -1606,18 +1609,20 @@ static int create_directory(Item *i, const char *path) {
static int create_subvolume(Item *i, const char *path) {
_cleanup_close_ int fd = -1;
+ CreationMode creation;
int r, q = 0;
assert(i);
assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA));
- fd = create_directory_or_subvolume(path, i->mode, true);
+ fd = create_directory_or_subvolume(path, i->mode, true, &creation);
if (fd == -EEXIST)
return 0;
if (fd < 0)
return fd;
- if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
+ if (creation == CREATION_NORMAL &&
+ IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
r = btrfs_subvol_auto_qgroup_fd(fd, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
if (r == -ENOTTY)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);