From 1e603a482f57edb1fb863dbf23b868cf5854e004 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Nov 2015 20:08:04 +0100 Subject: [PATCH 1/7] journald: never accept fds from file systems with mandatory locking enabled This is pretty much a work-around for a security vulnerability in kernels that allow unprivileged user namespaces. Fixes #1822. --- src/journal/journald-native.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 1e3774dafb6..69a685c06f2 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "alloc-util.h" @@ -399,8 +400,37 @@ void server_process_native_file( assert_se(munmap(p, ps) >= 0); } else { _cleanup_free_ void *p = NULL; + struct statvfs vfs; ssize_t n; + if (fstatvfs(fd, &vfs) < 0) { + log_error_errno(errno, "Failed to stat file system of passed file, ignoring: %m"); + return; + } + + /* Refuse operating on file systems that have + * mandatory locking enabled, see: + * + * https://github.com/systemd/systemd/issues/1822 + */ + if (vfs.f_flag & ST_MANDLOCK) { + log_error("Received file descriptor from file system with mandatory locking enable, refusing."); + return; + } + + /* Make the fd non-blocking. On regular files this has + * the effect of bypassing mandatory locking. Of + * course, this should normally not be necessary given + * the check above, but let's better be safe than + * sorry, after all NFS is pretty confusing regarding + * file system flags, and we better don't trust it, + * and so is SMB. */ + r = fd_nonblock(fd, true); + if (r < 0) { + log_error_errno(r, "Failed to make fd non-blocking, ignoring: %m"); + return; + } + /* The file is not sealed, we can't map the file here, since * clients might then truncate it and trigger a SIGBUS for * us. So let's stupidly read it */ From ba64af90ecf48f7653a04bf3af1291385c9a69b8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Nov 2015 20:36:37 +0100 Subject: [PATCH 2/7] core: change return value of the unit's enumerate() call to void We cannot handle enumeration failures in a sensible way, hence let's try hard to continue without making such failures fatal, and log about it with precise error messages. --- src/core/device.c | 35 ++++++++++++++++++++++------------- src/core/manager.c | 16 ++++------------ src/core/manager.h | 2 +- src/core/mount.c | 28 +++++++++++++++++++--------- src/core/scope.c | 13 +++++++------ src/core/slice.c | 13 +++++++------ src/core/swap.c | 23 ++++++++++++++++------- src/core/unit.h | 2 +- 8 files changed, 77 insertions(+), 55 deletions(-) diff --git a/src/core/device.c b/src/core/device.c index 23ee7aee7ef..b54352d7331 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -602,7 +602,7 @@ static void device_shutdown(Manager *m) { m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs); } -static int device_enumerate(Manager *m) { +static void device_enumerate(Manager *m) { _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; int r; @@ -612,7 +612,7 @@ static int device_enumerate(Manager *m) { if (!m->udev_monitor) { m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); if (!m->udev_monitor) { - r = -ENOMEM; + log_oom(); goto fail; } @@ -622,37 +622,49 @@ static int device_enumerate(Manager *m) { (void) udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024); r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd"); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to add udev tag match: %m"); goto fail; + } r = udev_monitor_enable_receiving(m->udev_monitor); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enable udev event reception: %m"); goto fail; + } r = sd_event_add_io(m->event, &m->udev_event_source, udev_monitor_get_fd(m->udev_monitor), EPOLLIN, device_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch udev file descriptor: %m"); goto fail; + } (void) sd_event_source_set_description(m->udev_event_source, "device"); } e = udev_enumerate_new(m->udev); if (!e) { - r = -ENOMEM; + log_oom(); goto fail; } r = udev_enumerate_add_match_tag(e, "systemd"); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to create udev tag enumeration: %m"); goto fail; + } r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to install initialization match into enumeration: %m"); goto fail; + } r = udev_enumerate_scan_devices(e); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enumerate devices: %m"); goto fail; + } first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { @@ -675,13 +687,10 @@ static int device_enumerate(Manager *m) { device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false); } - return 0; + return; fail: - log_error_errno(r, "Failed to enumerate devices: %m"); - device_shutdown(m); - return r; } static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { diff --git a/src/core/manager.c b/src/core/manager.c index 7c3a020c4af..dbe11c2b4d1 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -993,8 +993,7 @@ Manager* manager_free(Manager *m) { return NULL; } -int manager_enumerate(Manager *m) { - int r = 0; +void manager_enumerate(Manager *m) { UnitType c; assert(m); @@ -1002,8 +1001,6 @@ int manager_enumerate(Manager *m) { /* Let's ask every type to load all units from disk/kernel * that it might know */ for (c = 0; c < _UNIT_TYPE_MAX; c++) { - int q; - if (!unit_type_supported(c)) { log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; @@ -1012,13 +1009,10 @@ int manager_enumerate(Manager *m) { if (!unit_vtable[c]->enumerate) continue; - q = unit_vtable[c]->enumerate(m); - if (q < 0) - r = q; + unit_vtable[c]->enumerate(m); } manager_dispatch_load_queue(m); - return r; } static void manager_coldplug(Manager *m) { @@ -1152,7 +1146,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* First, enumerate what we can from all config files */ dual_timestamp_get(&m->units_load_start_timestamp); - r = manager_enumerate(m); + manager_enumerate(m); dual_timestamp_get(&m->units_load_finish_timestamp); /* Second, deserialize if there is something to deserialize */ @@ -2554,9 +2548,7 @@ int manager_reload(Manager *m) { manager_build_unit_path_cache(m); /* First, enumerate what we can from all config files */ - q = manager_enumerate(m); - if (q < 0 && r >= 0) - r = q; + manager_enumerate(m); /* Second, deserialize our stored data */ q = manager_deserialize(m, f, fds); diff --git a/src/core/manager.h b/src/core/manager.h index 38d2770e974..c57a645c334 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -316,7 +316,7 @@ struct Manager { int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); Manager* manager_free(Manager *m); -int manager_enumerate(Manager *m); +void manager_enumerate(Manager *m); int manager_startup(Manager *m, FILE *serialization, FDSet *fds); Job *manager_get_job(Manager *m, uint32_t id); diff --git a/src/core/mount.c b/src/core/mount.c index 950d5d76d5d..d3766ba934a 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1596,7 +1596,7 @@ static int mount_get_timeout(Unit *u, uint64_t *timeout) { return 1; } -static int mount_enumerate(Manager *m) { +static void mount_enumerate(Manager *m) { int r; assert(m); @@ -1608,29 +1608,40 @@ static int mount_enumerate(Manager *m) { m->mount_monitor = mnt_new_monitor(); if (!m->mount_monitor) { - r = -ENOMEM; + log_oom(); goto fail; } r = mnt_monitor_enable_kernel(m->mount_monitor, 1); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enable watching of kernel mount events: %m"); goto fail; + } + r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enable watching of userspace mount events: %m"); goto fail; + } /* mnt_unref_monitor() will close the fd */ fd = r = mnt_monitor_get_fd(m->mount_monitor); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to acquire watch file descriptor: %m"); goto fail; + } r = sd_event_add_io(m->event, &m->mount_event_source, fd, EPOLLIN, mount_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch mount file descriptor: %m"); goto fail; + } r = sd_event_source_set_priority(m->mount_event_source, -10); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to adjust mount watch priority: %m"); goto fail; + } (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch"); } @@ -1639,11 +1650,10 @@ static int mount_enumerate(Manager *m) { if (r < 0) goto fail; - return 0; + return; fail: mount_shutdown(m); - return r; } static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { diff --git a/src/core/scope.c b/src/core/scope.c index 6bacb226e88..48a5eaae488 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -510,7 +510,7 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) { return scope_state_to_string(SCOPE(u)->state); } -static int scope_enumerate(Manager *m) { +static void scope_enumerate(Manager *m) { Unit *u; int r; @@ -524,13 +524,16 @@ static int scope_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_INIT_SCOPE); if (!u) { u = unit_new(m, sizeof(Scope)); - if (!u) - return log_oom(); + if (!u) { + log_oom(); + return; + } r = unit_add_name(u, SPECIAL_INIT_SCOPE); if (r < 0) { unit_free(u); - return log_error_errno(r, "Failed to add init.scope name"); + log_error_errno(r, "Failed to add init.scope name"); + return; } } @@ -551,8 +554,6 @@ static int scope_enumerate(Manager *m) { unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); - - return 0; } static const char* const scope_result_table[_SCOPE_RESULT_MAX] = { diff --git a/src/core/slice.c b/src/core/slice.c index 46021441503..2aa3fbf8be3 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -255,7 +255,7 @@ _pure_ static const char *slice_sub_state_to_string(Unit *u) { return slice_state_to_string(SLICE(u)->state); } -static int slice_enumerate(Manager *m) { +static void slice_enumerate(Manager *m) { Unit *u; int r; @@ -264,13 +264,16 @@ static int slice_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_SLICE); if (!u) { u = unit_new(m, sizeof(Slice)); - if (!u) - return log_oom(); + if (!u) { + log_oom(); + return; + } r = unit_add_name(u, SPECIAL_ROOT_SLICE); if (r < 0) { unit_free(u); - return log_error_errno(r, "Failed to add -.slice name"); + log_error_errno(r, "Failed to add -.slice name"); + return; } } @@ -288,8 +291,6 @@ static int slice_enumerate(Manager *m) { unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); - - return 0; } const UnitVTable slice_vtable = { diff --git a/src/core/swap.c b/src/core/swap.c index baaa27b6a31..e44cffc2e8c 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1268,26 +1268,36 @@ static void swap_shutdown(Manager *m) { m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode); } -static int swap_enumerate(Manager *m) { +static void swap_enumerate(Manager *m) { int r; assert(m); if (!m->proc_swaps) { m->proc_swaps = fopen("/proc/swaps", "re"); - if (!m->proc_swaps) - return errno == ENOENT ? 0 : -errno; + if (!m->proc_swaps) { + if (errno == ENOENT) + log_debug("Not swap enabled, skipping enumeration"); + else + log_error_errno(errno, "Failed to open /proc/swaps: %m"); + + return; + } r = sd_event_add_io(m->event, &m->swap_event_source, fileno(m->proc_swaps), EPOLLPRI, swap_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch /proc/swaps: %m"); goto fail; + } /* Dispatch this before we dispatch SIGCHLD, so that * we always get the events from /proc/swaps before * the SIGCHLD of /sbin/swapon. */ r = sd_event_source_set_priority(m->swap_event_source, -10); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to change /proc/swaps priority: %m"); goto fail; + } (void) sd_event_source_set_description(m->swap_event_source, "swap-proc"); } @@ -1296,11 +1306,10 @@ static int swap_enumerate(Manager *m) { if (r < 0) goto fail; - return 0; + return; fail: swap_shutdown(m); - return r; } int swap_process_device_new(Manager *m, struct udev_device *dev) { diff --git a/src/core/unit.h b/src/core/unit.h index 6f775c5ce10..eb2af229b57 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -389,7 +389,7 @@ struct UnitVTable { * everything that is loaded here should still stay in * inactive state. It is the job of the coldplug() call above * to put the units into the initial state. */ - int (*enumerate)(Manager *m); + void (*enumerate)(Manager *m); /* Type specific cleanups. */ void (*shutdown)(Manager *m); From 5a6158b6412100672e1cbddb22efdcc5101fed7f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Nov 2015 20:38:30 +0100 Subject: [PATCH 3/7] core: try to continue if coldplugging of a unit fails --- src/core/unit.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/core/unit.c b/src/core/unit.c index 6c130d4cd16..ae019c7f7e3 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -2890,7 +2890,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { } int unit_coldplug(Unit *u) { - int r; + int r = 0, q = 0; assert(u); @@ -2901,17 +2901,16 @@ int unit_coldplug(Unit *u) { u->coldplugged = true; - if (UNIT_VTABLE(u)->coldplug) { + if (UNIT_VTABLE(u)->coldplug) r = UNIT_VTABLE(u)->coldplug(u); - if (r < 0) - return r; - } - if (u->job) { - r = job_coldplug(u->job); - if (r < 0) - return r; - } + if (u->job) + q = job_coldplug(u->job); + + if (r < 0) + return r; + if (q < 0) + return q; return 0; } From 9ff1a6f1d61d4569920d5b75c88cf1c2ad9adaae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Nov 2015 20:42:58 +0100 Subject: [PATCH 4/7] core: change type of distribute_fds() prototype to return void We can't handle errors of thisc all sanely anyway, and we never actually return any errors from the unit type that implements the call. Hence, let's make this void, in order to simplify things. --- src/core/manager.c | 22 +++++++--------------- src/core/socket.c | 4 +--- src/core/unit.h | 2 +- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/core/manager.c b/src/core/manager.c index dbe11c2b4d1..f712ac29dcf 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1094,10 +1094,9 @@ fail: } -static int manager_distribute_fds(Manager *m, FDSet *fds) { - Unit *u; +static void manager_distribute_fds(Manager *m, FDSet *fds) { Iterator i; - int r; + Unit *u; assert(m); @@ -1106,14 +1105,11 @@ static int manager_distribute_fds(Manager *m, FDSet *fds) { if (fdset_size(fds) <= 0) break; - if (UNIT_VTABLE(u)->distribute_fds) { - r = UNIT_VTABLE(u)->distribute_fds(u, fds); - if (r < 0) - return r; - } - } + if (!UNIT_VTABLE(u)->distribute_fds) + continue; - return 0; + UNIT_VTABLE(u)->distribute_fds(u, fds); + } } int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { @@ -1157,11 +1153,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { * useful to allow container managers to pass some file * descriptors to us pre-initialized. This enables * socket-based activation of entire containers. */ - if (fdset_size(fds) > 0) { - q = manager_distribute_fds(m, fds); - if (q < 0 && r == 0) - r = q; - } + manager_distribute_fds(m, fds); /* We might have deserialized the notify fd, but if we didn't * then let's create the bus now */ diff --git a/src/core/socket.c b/src/core/socket.c index 3c7f972fbc4..c73ee78b139 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2332,7 +2332,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, return 0; } -static int socket_distribute_fds(Unit *u, FDSet *fds) { +static void socket_distribute_fds(Unit *u, FDSet *fds) { Socket *s = SOCKET(u); SocketPort *p; @@ -2356,8 +2356,6 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) { } } } - - return 0; } _pure_ static UnitActiveState socket_active_state(Unit *u) { diff --git a/src/core/unit.h b/src/core/unit.h index eb2af229b57..03d7d8de7f4 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -322,7 +322,7 @@ struct UnitVTable { int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds); /* Try to match up fds with what we need for this unit */ - int (*distribute_fds)(Unit *u, FDSet *fds); + void (*distribute_fds)(Unit *u, FDSet *fds); /* Boils down the more complex internal state of this unit to * a simpler one that the engine can understand */ From 17afc8f27b7850ca479f0c3720680b90881d8e6e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Nov 2015 21:04:39 +0100 Subject: [PATCH 5/7] journald: be less picky when receiving epoll events The event might be flagged with stuff we don't expect, hence don't be needlessly picky, just rely on the kernel passing us sensible events. --- src/journal/journald-server.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 36fe739073e..a6e5e4a20fb 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1484,11 +1484,6 @@ static int dispatch_notify_event(sd_event_source *es, int fd, uint32_t revents, assert(s->notify_event_source == es); assert(s->notify_fd == fd); - if (revents != EPOLLOUT) { - log_error("Invalid events on notify file descriptor."); - return -EINVAL; - } - /* The $NOTIFY_SOCKET is writable again, now send exactly one * message on it. Either it's the wtachdog event, the initial * READY=1 event or an stdout stream event. If there's nothing From 12ee6186dc4431b8d75f8becb236ec840feedb6e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Nov 2015 21:37:49 +0100 Subject: [PATCH 6/7] btrfs: when querying quota, make sure we don't choke if quota is disabled When quota is disabled there's no quota tree on the fs, which results in the SEARCH ioctl to return ENOENT. Handle this nicely: treat this the same way as the case where the quota tree is around but doesn't carry the searched for fields. --- src/basic/btrfs-util.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 4c90bc0c80b..661331e8b24 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -575,8 +575,12 @@ int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) { unsigned i; args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { + if (errno == ENOENT) /* quota tree is missing: quota disabled */ + break; + return -errno; + } if (args.key.nr_items <= 0) break; @@ -1335,8 +1339,12 @@ int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupi unsigned i; args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { + if (errno == ENOENT) /* quota tree missing: quota is not enabled, hence nothing to copy */ + break; + return -errno; + } if (args.key.nr_items <= 0) break; @@ -1766,8 +1774,12 @@ int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) { unsigned i; args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { + if (errno == ENOENT) /* quota tree missing: quota is disabled */ + break; + return -errno; + } if (args.key.nr_items <= 0) break; From be6d467c1f9219b2016d556a0a369828d463ca27 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Nov 2015 21:39:52 +0100 Subject: [PATCH 7/7] tmpfiles: don't consider it a problem if quota is not enabled on btrfs If quota is not enabled on a btrfs file system, accept that, and only log a debug message, but do not consider this a reason for failure. Fixes: #1809 --- src/basic/btrfs-util.c | 4 ++++ src/tmpfiles/tmpfiles.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 661331e8b24..290fabdeffe 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -1010,6 +1010,10 @@ static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) { for (c = 0;; c++) { if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) { + /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */ + if (errno == EINVAL) + return -ENOPROTOOPT; + if (errno == EBUSY && c < 10) { (void) btrfs_quota_scan_wait(fd); continue; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 64f0c9396c6..74b6b91593d 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1267,6 +1267,10 @@ static int create_item(Item *i) { log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path); return 0; } + if (r == -ENOPROTOOPT) { + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %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)