1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <sys/mount.h>
4 
5 #include "alloc-util.h"
6 #include "cgroup-setup.h"
7 #include "fd-util.h"
8 #include "fileio.h"
9 #include "format-util.h"
10 #include "fs-util.h"
11 #include "mkdir.h"
12 #include "mount-util.h"
13 #include "mountpoint-util.h"
14 #include "nspawn-cgroup.h"
15 #include "nspawn-mount.h"
16 #include "path-util.h"
17 #include "rm-rf.h"
18 #include "string-util.h"
19 #include "strv.h"
20 #include "user-util.h"
21 #include "util.h"
22 
chown_cgroup_path(const char * path,uid_t uid_shift)23 static int chown_cgroup_path(const char *path, uid_t uid_shift) {
24         _cleanup_close_ int fd = -1;
25 
26         fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
27         if (fd < 0)
28                 return -errno;
29 
30         FOREACH_STRING(fn,
31                        ".",
32                        "cgroup.clone_children",
33                        "cgroup.controllers",
34                        "cgroup.events",
35                        "cgroup.procs",
36                        "cgroup.stat",
37                        "cgroup.subtree_control",
38                        "cgroup.threads",
39                        "notify_on_release",
40                        "tasks")
41                 if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0)
42                         log_full_errno(errno == ENOENT ? LOG_DEBUG :  LOG_WARNING, errno,
43                                        "Failed to chown \"%s/%s\", ignoring: %m", path, fn);
44 
45         return 0;
46 }
47 
chown_cgroup(pid_t pid,CGroupUnified unified_requested,uid_t uid_shift)48 int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
49         _cleanup_free_ char *path = NULL, *fs = NULL;
50         int r;
51 
52         r = cg_pid_get_path(NULL, pid, &path);
53         if (r < 0)
54                 return log_error_errno(r, "Failed to get container cgroup path: %m");
55 
56         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
57         if (r < 0)
58                 return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
59 
60         r = chown_cgroup_path(fs, uid_shift);
61         if (r < 0)
62                 return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
63 
64         if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) {
65                 _cleanup_free_ char *lfs = NULL;
66                 /* Always propagate access rights from unified to legacy controller */
67 
68                 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, NULL, &lfs);
69                 if (r < 0)
70                         return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
71 
72                 r = chown_cgroup_path(lfs, uid_shift);
73                 if (r < 0)
74                         return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs);
75         }
76 
77         return 0;
78 }
79 
sync_cgroup(pid_t pid,CGroupUnified unified_requested,uid_t uid_shift)80 int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
81         _cleanup_free_ char *cgroup = NULL;
82         char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
83         bool undo_mount = false;
84         const char *fn;
85         int r, unified_controller;
86 
87         unified_controller = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
88         if (unified_controller < 0)
89                 return log_error_errno(unified_controller, "Failed to determine whether the systemd hierarchy is unified: %m");
90         if ((unified_controller > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
91                 return 0;
92 
93         /* When the host uses the legacy cgroup setup, but the
94          * container shall use the unified hierarchy, let's make sure
95          * we copy the path from the name=systemd hierarchy into the
96          * unified hierarchy. Similar for the reverse situation. */
97 
98         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
99         if (r < 0)
100                 return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid);
101 
102         /* In order to access the unified hierarchy we need to mount it */
103         if (!mkdtemp(tree))
104                 return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");
105 
106         if (unified_controller > 0)
107                 r = mount_nofollow_verbose(LOG_ERR, "cgroup", tree, "cgroup",
108                                            MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
109         else
110                 r = mount_nofollow_verbose(LOG_ERR, "cgroup", tree, "cgroup2",
111                                            MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
112         if (r < 0)
113                 goto finish;
114 
115         undo_mount = true;
116 
117         /* If nspawn dies abruptly the cgroup hierarchy created below
118          * its unit isn't cleaned up. So, let's remove it
119          * https://github.com/systemd/systemd/pull/4223#issuecomment-252519810 */
120         fn = strjoina(tree, cgroup);
121         (void) rm_rf(fn, REMOVE_ROOT|REMOVE_ONLY_DIRECTORIES);
122 
123         fn = strjoina(tree, cgroup, "/cgroup.procs");
124 
125         sprintf(pid_string, PID_FMT, pid);
126         r = write_string_file(fn, pid_string, WRITE_STRING_FILE_DISABLE_BUFFER|WRITE_STRING_FILE_MKDIR_0755);
127         if (r < 0) {
128                 log_error_errno(r, "Failed to move process: %m");
129                 goto finish;
130         }
131 
132         fn = strjoina(tree, cgroup);
133         r = chown_cgroup_path(fn, uid_shift);
134         if (r < 0)
135                 log_error_errno(r, "Failed to chown() cgroup %s: %m", fn);
136 finish:
137         if (undo_mount)
138                 (void) umount_verbose(LOG_ERR, tree, UMOUNT_NOFOLLOW);
139 
140         (void) rmdir(tree);
141         return r;
142 }
143 
create_subcgroup(pid_t pid,bool keep_unit,CGroupUnified unified_requested)144 int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) {
145         _cleanup_free_ char *cgroup = NULL;
146         CGroupMask supported;
147         const char *payload;
148         int r;
149 
150         assert(pid > 1);
151 
152         /* In the unified hierarchy inner nodes may only contain subgroups, but not processes. Hence, if we running in
153          * the unified hierarchy and the container does the same, and we did not create a scope unit for the container
154          * move us and the container into two separate subcgroups.
155          *
156          * Moreover, container payloads such as systemd try to manage the cgroup they run in full (i.e. including
157          * its attributes), while the host systemd will only delegate cgroups for children of the cgroup created for a
158          * delegation unit, instead of the cgroup itself. This means, if we'd pass on the cgroup allocated from the
159          * host systemd directly to the payload, the host and payload systemd might fight for the cgroup
160          * attributes. Hence, let's insert an intermediary cgroup to cover that case too.
161          *
162          * Note that we only bother with the main hierarchy here, not with any secondary ones. On the unified setup
163          * that's fine because there's only one hierarchy anyway and controllers are enabled directly on it. On the
164          * legacy setup, this is fine too, since delegation of controllers is generally not safe there, hence we won't
165          * do it. */
166 
167         r = cg_mask_supported(&supported);
168         if (r < 0)
169                 return log_error_errno(r, "Failed to determine supported controllers: %m");
170 
171         if (keep_unit)
172                 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
173         else
174                 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
175         if (r < 0)
176                 return log_error_errno(r, "Failed to get our control group: %m");
177 
178         payload = strjoina(cgroup, "/payload");
179         r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, payload, pid);
180         if (r < 0)
181                 return log_error_errno(r, "Failed to create %s subcgroup: %m", payload);
182 
183         if (keep_unit) {
184                 const char *supervisor;
185 
186                 supervisor = strjoina(cgroup, "/supervisor");
187                 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, supervisor, 0);
188                 if (r < 0)
189                         return log_error_errno(r, "Failed to create %s subcgroup: %m", supervisor);
190         }
191 
192         /* Try to enable as many controllers as possible for the new payload. */
193         (void) cg_enable_everywhere(supported, supported, cgroup, NULL);
194         return 0;
195 }
196 
197 /* Retrieve existing subsystems. This function is called in a new cgroup
198  * namespace.
199  */
get_process_controllers(Set ** ret)200 static int get_process_controllers(Set **ret) {
201         _cleanup_set_free_ Set *controllers = NULL;
202         _cleanup_fclose_ FILE *f = NULL;
203         int r;
204 
205         assert(ret);
206 
207         f = fopen("/proc/self/cgroup", "re");
208         if (!f)
209                 return errno == ENOENT ? -ESRCH : -errno;
210 
211         for (;;) {
212                 _cleanup_free_ char *line = NULL;
213                 char *e, *l;
214 
215                 r = read_line(f, LONG_LINE_MAX, &line);
216                 if (r < 0)
217                         return r;
218                 if (r == 0)
219                         break;
220 
221                 l = strchr(line, ':');
222                 if (!l)
223                         continue;
224 
225                 l++;
226                 e = strchr(l, ':');
227                 if (!e)
228                         continue;
229 
230                 *e = 0;
231 
232                 if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
233                         continue;
234 
235                 r = set_put_strdup(&controllers, l);
236                 if (r < 0)
237                         return r;
238         }
239 
240         *ret = TAKE_PTR(controllers);
241 
242         return 0;
243 }
244 
mount_legacy_cgroup_hierarchy(const char * dest,const char * controller,const char * hierarchy,bool read_only)245 static int mount_legacy_cgroup_hierarchy(
246                 const char *dest,
247                 const char *controller,
248                 const char *hierarchy,
249                 bool read_only) {
250 
251         const char *to, *fstype, *opts;
252         int r;
253 
254         to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
255 
256         r = path_is_mount_point(to, dest, 0);
257         if (r < 0 && r != -ENOENT)
258                 return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
259         if (r > 0)
260                 return 0;
261 
262         (void) mkdir_p(to, 0755);
263 
264         /* The superblock mount options of the mount point need to be
265          * identical to the hosts', and hence writable... */
266         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
267                 fstype = "cgroup2";
268                 opts = NULL;
269         } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
270                 fstype = "cgroup";
271                 opts = "none,name=systemd,xattr";
272         } else {
273                 fstype = "cgroup";
274                 opts = controller;
275         }
276 
277         r = mount_nofollow_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
278         if (r < 0)
279                 return r;
280 
281         /* ... hence let's only make the bind mount read-only, not the superblock. */
282         if (read_only) {
283                 r = mount_nofollow_verbose(LOG_ERR, NULL, to, NULL,
284                                            MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
285                 if (r < 0)
286                         return r;
287         }
288 
289         return 1;
290 }
291 
292 /* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
mount_legacy_cgns_supported(const char * dest,CGroupUnified unified_requested,bool userns,uid_t uid_shift,uid_t uid_range,const char * selinux_apifs_context)293 static int mount_legacy_cgns_supported(
294                 const char *dest,
295                 CGroupUnified unified_requested,
296                 bool userns,
297                 uid_t uid_shift,
298                 uid_t uid_range,
299                 const char *selinux_apifs_context) {
300 
301         _cleanup_set_free_ Set *controllers = NULL;
302         const char *cgroup_root = "/sys/fs/cgroup", *c;
303         int r;
304 
305         (void) mkdir_p(cgroup_root, 0755);
306 
307         /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
308         r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
309         if (r < 0)
310                 return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
311         if (r == 0) {
312                 _cleanup_free_ char *options = NULL;
313 
314                 /* When cgroup namespaces are enabled and user namespaces are
315                  * used then the mount of the cgroupfs is done *inside* the new
316                  * user namespace. We're root in the new user namespace and the
317                  * kernel will happily translate our uid/gid to the correct
318                  * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
319                  * pass uid 0 and not uid_shift to tmpfs_patch_options().
320                  */
321                 r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, 0, selinux_apifs_context, &options);
322                 if (r < 0)
323                         return log_oom();
324 
325                 r = mount_nofollow_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
326                                            MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
327                 if (r < 0)
328                         return r;
329         }
330 
331         r = cg_all_unified();
332         if (r < 0)
333                 return r;
334         if (r > 0)
335                 goto skip_controllers;
336 
337         r = get_process_controllers(&controllers);
338         if (r < 0)
339                 return log_error_errno(r, "Failed to determine cgroup controllers: %m");
340 
341         for (;;) {
342                 _cleanup_free_ const char *controller = NULL;
343 
344                 controller = set_steal_first(controllers);
345                 if (!controller)
346                         break;
347 
348                 r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
349                 if (r < 0)
350                         return r;
351 
352                 /* When multiple hierarchies are co-mounted, make their
353                  * constituting individual hierarchies a symlink to the
354                  * co-mount.
355                  */
356                 c = controller;
357                 for (;;) {
358                         _cleanup_free_ char *target = NULL, *tok = NULL;
359 
360                         r = extract_first_word(&c, &tok, ",", 0);
361                         if (r < 0)
362                                 return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
363                         if (r == 0)
364                                 break;
365 
366                         if (streq(controller, tok))
367                                 break;
368 
369                         target = path_join("/sys/fs/cgroup/", tok);
370                         if (!target)
371                                 return log_oom();
372 
373                         r = symlink_idempotent(controller, target, false);
374                         if (r == -EINVAL)
375                                 return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
376                         if (r < 0)
377                                 return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
378                 }
379         }
380 
381 skip_controllers:
382         if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
383                 r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
384                 if (r < 0)
385                         return r;
386         }
387 
388         r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
389         if (r < 0)
390                 return r;
391 
392         if (!userns)
393                 return mount_nofollow_verbose(LOG_ERR, NULL, cgroup_root, NULL,
394                                               MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
395 
396         return 0;
397 }
398 
399 /* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
mount_legacy_cgns_unsupported(const char * dest,CGroupUnified unified_requested,bool userns,uid_t uid_shift,uid_t uid_range,const char * selinux_apifs_context)400 static int mount_legacy_cgns_unsupported(
401                 const char *dest,
402                 CGroupUnified unified_requested,
403                 bool userns,
404                 uid_t uid_shift,
405                 uid_t uid_range,
406                 const char *selinux_apifs_context) {
407 
408         _cleanup_set_free_ Set *controllers = NULL;
409         const char *cgroup_root;
410         int r;
411 
412         cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
413 
414         (void) mkdir_p(cgroup_root, 0755);
415 
416         /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
417         r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
418         if (r < 0)
419                 return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
420         if (r == 0) {
421                 _cleanup_free_ char *options = NULL;
422 
423                 r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
424                 if (r < 0)
425                         return log_oom();
426 
427                 r = mount_nofollow_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
428                                            MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
429                 if (r < 0)
430                         return r;
431         }
432 
433         r = cg_all_unified();
434         if (r < 0)
435                 return r;
436         if (r > 0)
437                 goto skip_controllers;
438 
439         r = cg_kernel_controllers(&controllers);
440         if (r < 0)
441                 return log_error_errno(r, "Failed to determine cgroup controllers: %m");
442 
443         for (;;) {
444                 _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
445 
446                 controller = set_steal_first(controllers);
447                 if (!controller)
448                         break;
449 
450                 origin = path_join("/sys/fs/cgroup/", controller);
451                 if (!origin)
452                         return log_oom();
453 
454                 r = readlink_malloc(origin, &combined);
455                 if (r == -EINVAL) {
456                         /* Not a symbolic link, but directly a single cgroup hierarchy */
457 
458                         r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
459                         if (r < 0)
460                                 return r;
461 
462                 } else if (r < 0)
463                         return log_error_errno(r, "Failed to read link %s: %m", origin);
464                 else {
465                         _cleanup_free_ char *target = NULL;
466 
467                         target = path_join(dest, origin);
468                         if (!target)
469                                 return log_oom();
470 
471                         /* A symbolic link, a combination of controllers in one hierarchy */
472 
473                         if (!filename_is_valid(combined)) {
474                                 log_warning("Ignoring invalid combined hierarchy %s.", combined);
475                                 continue;
476                         }
477 
478                         r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
479                         if (r < 0)
480                                 return r;
481 
482                         r = symlink_idempotent(combined, target, false);
483                         if (r == -EINVAL)
484                                 return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
485                         if (r < 0)
486                                 return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
487                 }
488         }
489 
490 skip_controllers:
491         if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
492                 r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
493                 if (r < 0)
494                         return r;
495         }
496 
497         r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
498         if (r < 0)
499                 return r;
500 
501         return mount_nofollow_verbose(LOG_ERR, NULL, cgroup_root, NULL,
502                                       MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
503 }
504 
mount_unified_cgroups(const char * dest)505 static int mount_unified_cgroups(const char *dest) {
506         const char *p;
507         int r;
508 
509         assert(dest);
510 
511         p = prefix_roota(dest, "/sys/fs/cgroup");
512 
513         (void) mkdir_p(p, 0755);
514 
515         r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
516         if (r < 0)
517                 return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
518         if (r > 0) {
519                 p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
520                 if (access(p, F_OK) >= 0)
521                         return 0;
522                 if (errno != ENOENT)
523                         return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
524 
525                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
526                                        "%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
527         }
528 
529         return mount_nofollow_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
530 }
531 
mount_cgroups(const char * dest,CGroupUnified unified_requested,bool userns,uid_t uid_shift,uid_t uid_range,const char * selinux_apifs_context,bool use_cgns)532 int mount_cgroups(
533                 const char *dest,
534                 CGroupUnified unified_requested,
535                 bool userns,
536                 uid_t uid_shift,
537                 uid_t uid_range,
538                 const char *selinux_apifs_context,
539                 bool use_cgns) {
540 
541         if (unified_requested >= CGROUP_UNIFIED_ALL)
542                 return mount_unified_cgroups(dest);
543         if (use_cgns)
544                 return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
545 
546         return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
547 }
548 
mount_systemd_cgroup_writable_one(const char * root,const char * own)549 static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
550         int r;
551 
552         assert(root);
553         assert(own);
554 
555         /* Make our own cgroup a (writable) bind mount */
556         r = mount_nofollow_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
557         if (r < 0)
558                 return r;
559 
560         /* And then remount the systemd cgroup root read-only */
561         return mount_nofollow_verbose(LOG_ERR, NULL, root, NULL,
562                                       MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
563 }
564 
mount_systemd_cgroup_writable(const char * dest,CGroupUnified unified_requested)565 int mount_systemd_cgroup_writable(
566                 const char *dest,
567                 CGroupUnified unified_requested) {
568 
569         _cleanup_free_ char *own_cgroup_path = NULL;
570         const char *root, *own;
571         int r;
572 
573         assert(dest);
574 
575         r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
576         if (r < 0)
577                 return log_error_errno(r, "Failed to determine our own cgroup path: %m");
578 
579         /* If we are living in the top-level, then there's nothing to do... */
580         if (path_equal(own_cgroup_path, "/"))
581                 return 0;
582 
583         if (unified_requested >= CGROUP_UNIFIED_ALL) {
584 
585                 root = prefix_roota(dest, "/sys/fs/cgroup");
586                 own = strjoina(root, own_cgroup_path);
587 
588         } else {
589 
590                 if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
591                         root = prefix_roota(dest, "/sys/fs/cgroup/unified");
592                         own = strjoina(root, own_cgroup_path);
593 
594                         r = mount_systemd_cgroup_writable_one(root, own);
595                         if (r < 0)
596                                 return r;
597                 }
598 
599                 root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
600                 own = strjoina(root, own_cgroup_path);
601         }
602 
603         return mount_systemd_cgroup_writable_one(root, own);
604 }
605