diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-10-21 19:46:23 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-10-22 01:59:25 +0200 |
commit | 5fb13eb51bf641a3434828947e137e1a5f2d7eb2 (patch) | |
tree | 891b21eb9e2bd536843d0f9cb8bcfe023188e70f | |
parent | 8c9cfc28448bf671807e7d96b580221edebd71da (diff) |
tmpfiles: introduce "q" and "Q" for creating quota-enabled btrfs subvolumes
This allows us to set up the quota group hierarchy in a reasonable way
on btrfs file systems.
-rw-r--r-- | man/tmpfiles.d.xml | 92 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 43 |
2 files changed, 116 insertions, 19 deletions
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 3cee0fff59..662ec4518b 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -1,5 +1,4 @@ -<?xml version="1.0"?> -<!--*-nxml-*--> +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> <!-- This file is part of systemd. @@ -172,7 +171,77 @@ <listitem><para>Create a subvolume if the path does not exist yet and the file system supports this (btrfs). Otherwise create a normal directory, in the same - way as <varname>d</varname>.</para></listitem> + way as <varname>d</varname>. A subvolume created with this + line type is not assigned to any higher-level quota + group. For that use <varname>q</varname> or + <varname>Q</varname> which allow creating simple quota group + hierarchies, see below.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>q</varname></term> + <listitem><para>Similar to <varname>v</varname>, 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 <varname>d</varname>. 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 <varname>Q</varname> below. See <citerefentry + project='die-net'><refentrytitle>btrfs-qgroup</refentrytitle><manvolnum>8</manvolnum></citerefentry> + for details about the btrfs quota group + concept.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>Q</varname></term> + <listitem><para>Similar to <varname>q</varname>, 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.</para> + + <para>Effectively, this has a similar effect as + <varname>q</varname>, 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 <varname>q</varname> and + <varname>Q</varname> a concept of "subtree quotas" is + implemented. Each subvolume for which <varname>Q</varname> + 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 <varname>q</varname> 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.</para> + + <para>It is recommended to use + <varname>Q</varname> 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 <varname>Q</varname> are typically + <filename>/home</filename> or + <filename>/var/lib/machines</filename>. In contrast, + <varname>q</varname> 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 <varname>q</varname> + are typically <filename>/var</filename> or + <filename>/var/tmp</filename>. As with <varname>Q</varname>, + <varname>q</varname> has no effect on the quota group + hierarchy if the subvolume exists and already has at least + one higher-level quota group assigned.</para></listitem> </varlistentry> <varlistentry> @@ -504,13 +573,12 @@ <para>When the age is set to zero, the files are cleaned unconditionally.</para> - <para>The age field only applies to lines - starting with <varname>d</varname>, - <varname>D</varname>, <varname>v</varname>, - <varname>C</varname>, <varname>x</varname> and - <varname>X</varname>. If omitted or set to - <literal>-</literal>, no automatic clean-up is - done.</para> + <para>The age field only applies to lines starting with + <varname>d</varname>, <varname>D</varname>, + <varname>v</varname>, <varname>q</varname>, + <varname>Q</varname>, <varname>C</varname>, <varname>x</varname> + and <varname>X</varname>. If omitted or set to + <literal>-</literal>, no automatic clean-up is done.</para> <para>If the age field starts with a tilde character <literal>~</literal>, the clean-up is only applied to files and @@ -572,7 +640,9 @@ x /var/tmp/abrt/*</programlisting> <citerefentry project='man-pages'><refentrytitle>setfattr</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>setfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>getfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, - <citerefentry project='man-pages'><refentrytitle>chattr</refentrytitle><manvolnum>1</manvolnum></citerefentry> + <citerefentry project='man-pages'><refentrytitle>chattr</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry project='die-net'><refentrytitle>btrfs-subvolume</refentrytitle><manvolnum>8</manvolnum></citerefentry>, + <citerefentry project='die-net'><refentrytitle>btrfs-qgroup</refentrytitle><manvolnum>8</manvolnum></citerefentry> </para> </refsect1> diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index d219764bc6..f636a4d33b 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -69,6 +69,8 @@ typedef enum ItemType { CREATE_DIRECTORY = 'd', TRUNCATE_DIRECTORY = 'D', CREATE_SUBVOLUME = 'v', + CREATE_SUBVOLUME_INHERIT_QUOTA = 'q', + CREATE_SUBVOLUME_NEW_QUOTA = 'Q', CREATE_FIFO = 'p', CREATE_SYMLINK = 'L', CREATE_CHAR_DEVICE = 'c', @@ -180,6 +182,8 @@ static bool takes_ownership(ItemType t) { CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, + CREATE_SUBVOLUME_INHERIT_QUOTA, + CREATE_SUBVOLUME_NEW_QUOTA, CREATE_FIFO, CREATE_SYMLINK, CREATE_CHAR_DEVICE, @@ -1198,16 +1202,16 @@ static int create_item(Item *i) { case CREATE_DIRECTORY: case TRUNCATE_DIRECTORY: case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: RUN_WITH_UMASK(0000) mkdir_parents_label(i->path, 0755); - if (i->type == CREATE_SUBVOLUME) - RUN_WITH_UMASK((~i->mode) & 0777) { + if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { + RUN_WITH_UMASK((~i->mode) & 0777) r = btrfs_subvol_make(i->path); - log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path); - } - else + } else r = 0; if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) @@ -1236,6 +1240,24 @@ static int create_item(Item *i) { log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); + if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { + r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); + if (r == -ENOTTY) { + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path); + return 0; + } + if (r == -EROFS) { + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); + if (r > 0) + log_debug("Adjusted quota for subvolume \"%s\".", i->path); + if (r == 0) + log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); + } + r = path_set_perms(i, i->path); if (r < 0) return r; @@ -1492,6 +1514,8 @@ static int remove_item(Item *i) { case TRUNCATE_FILE: case CREATE_DIRECTORY: case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: case CREATE_FIFO: case CREATE_SYMLINK: case CREATE_CHAR_DEVICE: @@ -1583,6 +1607,8 @@ static int clean_item(Item *i) { switch (i->type) { case CREATE_DIRECTORY: case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: case TRUNCATE_DIRECTORY: case IGNORE_PATH: case COPY_FILES: @@ -1819,6 +1845,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { case CREATE_DIRECTORY: case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: case TRUNCATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: @@ -1983,8 +2011,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { i.mode = m; i.mode_set = true; } else - i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY) - ? 0755 : 0644; + i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644; if (!isempty(age) && !streq(age, "-")) { const char *a = age; @@ -2186,7 +2213,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { continue; ORDERED_HASHMAP_FOREACH(j, items, iter) { - if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME) + if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) continue; if (path_equal(j->path, i->path)) { |