1 /* SPDX-License-Identifier: LGPL-2.1+ */
15 #include "lxccontainer.h"
17 #include "memory_utils.h"
21 #include "storage_utils.h"
24 lxc_log_define(overlay
, lxc
);
26 static char *ovl_name
;
27 static char *ovl_version
[] = {"overlay", "overlayfs"};
29 static char *ovl_detect_name(void);
30 static int ovl_do_rsync(const char *src
, const char *dest
,
31 struct lxc_conf
*conf
);
32 static int ovl_remount_on_enodev(const char *lower
, const char *target
,
33 const char *name
, unsigned long mountflags
,
36 int ovl_clonepaths(struct lxc_storage
*orig
, struct lxc_storage
*new, const char *oldname
,
37 const char *cname
, const char *oldpath
, const char *lxcpath
,
38 int snap
, uint64_t newsize
, struct lxc_conf
*conf
)
44 ERROR("The overlay storage driver can only be used for "
49 if (!orig
->src
|| !orig
->dest
)
52 new->dest
= must_make_path(lxcpath
, cname
, "rootfs", NULL
);
54 ret
= mkdir_p(new->dest
, 0755);
55 if (ret
< 0 && errno
!= EEXIST
) {
56 SYSERROR("Failed to create directory \"%s\"", new->dest
);
60 if (am_guest_unpriv() || !lxc_list_empty(&conf
->id_map
)) {
61 ret
= chown_mapped_root(new->dest
, conf
);
63 WARN("Failed to update ownership of %s", new->dest
);
66 if (strcmp(orig
->type
, "dir") == 0) {
67 __do_free
char *delta
= NULL
, *work
= NULL
;
70 delta
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_DELTA_PATH
, NULL
);
72 ret
= mkdir_p(delta
, 0755);
73 if (ret
< 0 && errno
!= EEXIST
)
74 return log_error_errno(-errno
, errno
, "Failed to create directory \"%s\"", delta
);
77 * Make workdir for overlayfs.v22 or higher:
79 * /var/lib/lxc/c2/LXC_OVERLAY_WORK_PATH
80 * and is used to prepare files before they are atomically
81 * switched to the overlay destination. Workdirs need to be on
82 * the same filesystem as the upperdir so it's OK for it to be
85 work
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_WORK_PATH
, NULL
);
87 ret
= mkdir_p(work
, 0755);
88 if (ret
< 0 && errno
!= EEXIST
)
89 return log_error_errno(-errno
, errno
, "Failed to create directory \"%s\"", work
);
91 if (am_guest_unpriv() || !lxc_list_empty(&conf
->id_map
)) {
92 __do_free
char *lxc_overlay_delta_dir
= NULL
,
93 *lxc_overlay_private_dir
= NULL
;
95 lxc_overlay_private_dir
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_PRIVATE_DIR
, NULL
);
96 ret
= chown_mapped_root(lxc_overlay_private_dir
, conf
);
98 WARN("Failed to update ownership of %s", lxc_overlay_private_dir
);
100 lxc_overlay_delta_dir
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_DELTA_PATH
, NULL
);
101 ret
= chown_mapped_root(lxc_overlay_delta_dir
, conf
);
103 WARN("Failed to update ownership of %s", lxc_overlay_delta_dir
);
105 ret
= chown_mapped_root(work
, conf
);
107 WARN("Failed to update ownership of %s", work
);
110 src
= lxc_storage_get_path(orig
->src
, orig
->type
);
111 len
= STRLITERALLEN("overlay") + STRLITERALLEN(":") +
112 strlen(src
) + STRLITERALLEN(":") + strlen(delta
) + 1;
114 new->src
= malloc(len
);
116 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to allocate memory");
118 ret
= snprintf(new->src
, len
, "overlay:%s:%s", src
, delta
);
119 if (ret
< 0 || (size_t)ret
>= len
)
120 return log_error_errno(-EIO
, EIO
, "Failed to create string");
121 } else if (!strcmp(orig
->type
, "overlayfs") ||
122 !strcmp(orig
->type
, "overlay")) {
123 __do_free
char *clean_old_path
= NULL
, *clean_new_path
= NULL
,
124 *ndelta
= NULL
, *osrc
= NULL
, *work
= NULL
;
125 char *nsrc
, *odelta
, *s1
, *s2
, *s3
;
126 size_t len
, name_len
;
128 osrc
= strdup(orig
->src
);
130 return log_error_errno(-22, ENOMEM
, "Failed to duplicate string \"%s\"", orig
->src
);
133 if (strncmp(osrc
, "overlay:", STRLITERALLEN("overlay:")) == 0)
134 nsrc
+= STRLITERALLEN("overlay:");
135 else if (strncmp(osrc
, "overlayfs:", STRLITERALLEN("overlayfs:")) == 0)
136 nsrc
+= STRLITERALLEN("overlayfs:");
138 odelta
= strchr(nsrc
, ':');
140 return log_error_errno(-22, ENOENT
, "Failed to find \":\" in \"%s\"", nsrc
);
144 ndelta
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_DELTA_PATH
, NULL
);
146 ret
= mkdir_p(ndelta
, 0755);
147 if (ret
< 0 && errno
!= EEXIST
)
148 return log_error_errno(-errno
, errno
, "Failed to create directory \"%s\"", ndelta
);
150 /* Make workdir for overlayfs.v22 or higher (See the comment
153 work
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_WORK_PATH
, NULL
);
154 ret
= mkdir_p(work
, 0755);
155 if (ret
< 0 && errno
!= EEXIST
)
156 return log_error_errno(-errno
, errno
, "Failed to create directory \"%s\"", ndelta
);
158 if (am_guest_unpriv() || !lxc_list_empty(&conf
->id_map
)) {
159 __do_free
char *lxc_overlay_delta_dir
= NULL
,
160 *lxc_overlay_private_dir
= NULL
;
162 lxc_overlay_private_dir
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_PRIVATE_DIR
, NULL
);
163 ret
= chown_mapped_root(lxc_overlay_private_dir
, conf
);
165 WARN("Failed to update ownership of %s", lxc_overlay_private_dir
);
167 lxc_overlay_delta_dir
= must_make_path(lxcpath
, cname
, LXC_OVERLAY_DELTA_PATH
, NULL
);
168 ret
= chown_mapped_root(lxc_overlay_delta_dir
, conf
);
170 WARN("Failed to update ownership of %s", lxc_overlay_delta_dir
);
172 ret
= chown_mapped_root(work
, conf
);
174 WARN("Failed to update ownership of %s", work
);
177 len
= STRLITERALLEN("overlay") + STRLITERALLEN(":") + strlen(nsrc
) + STRLITERALLEN(":") + strlen(ndelta
) + 1;
178 new->src
= malloc(len
);
180 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to allocate memory");
182 ret
= snprintf(new->src
, len
, "overlay:%s:%s", nsrc
, ndelta
);
183 if (ret
< 0 || (size_t)ret
>= len
)
184 return log_error_errno(-EIO
, EIO
, "Failed to create string");
186 ret
= ovl_do_rsync(odelta
, ndelta
, conf
);
190 /* When we create an overlay snapshot of an overlay container in
191 * the snapshot directory under "<lxcpath>/<name>/snaps/" we
192 * don't need to record a dependency. If we would restore would
195 clean_old_path
= lxc_deslashify(oldpath
);
197 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to create clean path for \"%s\"", oldpath
);
199 clean_new_path
= lxc_deslashify(lxcpath
);
201 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to create clean path for \"%s\"", lxcpath
);
203 s1
= strrchr(clean_old_path
, '/');
205 return log_error_errno(-ENOENT
, ENOENT
, "Failed to detect \"/\" in string \"%s\"", clean_old_path
);
207 s2
= strrchr(clean_new_path
, '/');
209 return log_error_errno(-ENOENT
, ENOENT
, "Failed to detect \"/\" in string \"%s\"", clean_new_path
);
211 if (!strncmp(s1
, "/snaps", STRLITERALLEN("/snaps"))) {
215 } else if (!strncmp(s2
, "/snaps", STRLITERALLEN("/snaps"))) {
218 s3
= (char *)oldname
;
224 if (!strncmp(s1
, s2
, len
)) {
227 tmp
= (char *)(s2
+ len
+ 1);
231 name_len
= strlen(s3
);
232 if (strncmp(s3
, tmp
, name_len
))
235 return LXC_CLONE_SNAPSHOT
;
241 * Note, supporting this will require ovl_mount supporting
242 * mounting of the underlay. No big deal, just needs to be done.
244 return log_error_errno(-EINVAL
, EINVAL
, "overlay clone of %s container is not yet supported", orig
->type
);
250 /* To say "lxc-create -t ubuntu -n o1 -B overlay" means you want
251 * "<lxcpath>/<lxcname>/rootfs" to have the created container, while all changes
252 * after starting the container are written to "<lxcpath>/<lxcname>/LXC_OVERLAY_DELTA_PATH".
254 int ovl_create(struct lxc_storage
*bdev
, const char *dest
, const char *n
,
255 struct bdev_specs
*specs
, const struct lxc_conf
*conf
)
257 __do_free
char *delta
= NULL
, *tmp
= NULL
;
262 if (len
< 8 || strcmp(dest
+ len
- STRLITERALLEN("/rootfs"), "/rootfs"))
263 return log_error_errno(-ENOENT
, ENOENT
, "Failed to detect \"/rootfs\" in \"%s\"", dest
);
265 bdev
->dest
= strdup(dest
);
267 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to duplicate string \"%s\"", dest
);
269 tmp
= strndup(dest
, len
- STRLITERALLEN("/rootfs"));
271 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to duplicate string \"%s\"", dest
);
273 delta
= must_make_path(tmp
, LXC_OVERLAY_DELTA_PATH
, NULL
);
275 ret
= mkdir_p(delta
, 0755);
276 if (ret
< 0 && errno
!= EEXIST
)
277 return log_error_errno(-errno
, errno
, "Failed to create directory \"%s\"", delta
);
279 if (am_guest_unpriv() || !lxc_list_empty(&conf
->id_map
)) {
280 __do_free
char *lxc_overlay_private_dir
= NULL
;
282 lxc_overlay_private_dir
= must_make_path(tmp
, LXC_OVERLAY_PRIVATE_DIR
, NULL
);
283 ret
= chown_mapped_root(lxc_overlay_private_dir
, conf
);
285 WARN("Failed to update ownership of %s", lxc_overlay_private_dir
);
287 ret
= chown_mapped_root(delta
, conf
);
289 WARN("Failed to update ownership of %s", delta
);
292 /* overlay:lower:upper */
293 len
= STRLITERALLEN("overlay") + STRLITERALLEN(":") + len
+ STRLITERALLEN(":") + strlen(delta
) + 1;
294 bdev
->src
= malloc(len
);
296 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to allocate memory");
298 ret
= snprintf(bdev
->src
, len
, "overlay:%s:%s", dest
, delta
);
299 if (ret
< 0 || (size_t)ret
>= len
)
300 return log_error_errno(-EIO
, EIO
, "Failed to create rootfs path");
302 ret
= mkdir_p(bdev
->dest
, 0755);
303 if (ret
< 0 && errno
!= EEXIST
)
304 return log_error_errno(-errno
, errno
, "Failed to create directory \"%s\"", bdev
->dest
);
309 int ovl_destroy(struct lxc_storage
*orig
)
311 char *upper
= orig
->src
;
313 /* For an overlay container the rootfs is considered immutable
314 * and cannot be removed when restoring from a snapshot.
316 if (orig
->flags
& LXC_STORAGE_INTERNAL_OVERLAY_RESTORE
)
319 if (strncmp(upper
, "overlay:", 8) == 0)
321 else if (strncmp(upper
, "overlayfs:", 10) == 0)
324 upper
= strchr(upper
, ':');
329 return lxc_rmdir_onedev(upper
, NULL
);
332 bool ovl_detect(const char *path
)
334 if (!strncmp(path
, "overlay:", 8))
337 if (!strncmp(path
, "overlayfs:", 10))
343 int ovl_mount(struct lxc_storage
*bdev
)
345 __do_free
char *options
= NULL
, *options_work
= NULL
;
346 unsigned long mntflags
= 0;
347 char *mntdata
= NULL
;
348 char *tmp
, *dup
, *lower
, *upper
;
349 char *work
, *lastslash
;
353 if (strcmp(bdev
->type
, "overlay") && strcmp(bdev
->type
, "overlayfs"))
356 if (!bdev
->src
|| !bdev
->dest
)
360 ovl_name
= ovl_detect_name();
362 /* Separately mount it first:
363 * mount -t overlay * -o upperdir=${upper},lowerdir=${lower} lower dest
365 dup
= strdup(bdev
->src
);
367 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to allocate memory");
371 if (strncmp(dup
, "overlay:", STRLITERALLEN("overlay:")) == 0)
372 lower
+= STRLITERALLEN("overlay:");
373 else if (strncmp(dup
, "overlayfs:", STRLITERALLEN("overlayfs:")) == 0)
374 lower
+= STRLITERALLEN("overlayfs:");
378 /* support multiple lower layers */
379 while ((tmp
= strstr(upper
, ":/"))) {
385 if (upper
== lower
) {
392 /* if delta doesn't yet exist, create it */
393 ret
= mkdir_p(upper
, 0755);
394 if (ret
< 0 && errno
!= EEXIST
) {
395 SYSERROR("Failed to create directory \"%s\"", upper
);
400 /* overlayfs.v22 or higher needs workdir option:
402 * /var/lib/lxc/c2/LXC_OVERLAY_DELTA_PATH
404 * /var/lib/lxc/c2/LXC_OVERLAY_WORK_PATH
406 lastslash
= strrchr(upper
, '/');
408 ERROR("Failed to detect \"/\" in string \"%s\"", upper
);
413 upper
[lastslash
- upper
] = '\0';
414 work
= must_make_path(upper
, LXC_OVERLAY_WORK_DIR
, NULL
);
415 upper
[lastslash
- upper
] = '/';
417 ret
= parse_mntopts(bdev
->mntopts
, &mntflags
, &mntdata
);
419 ERROR("Failed to parse mount options");
426 ret
= mkdir_p(work
, 0755);
427 if (ret
< 0 && errno
!= EEXIST
) {
428 SYSERROR("Failed to create directory \"%s\"", work
);
437 * We should check whether bdev->src is a blockdev but for now only
438 * support overlays of a basic directory
442 len
= strlen(lower
) + strlen(upper
) +
443 strlen("upperdir=,lowerdir=,") + strlen(mntdata
) + 1;
444 options
= must_realloc(NULL
, len
);
445 ret
= snprintf(options
, len
, "upperdir=%s,lowerdir=%s,%s",
446 upper
, lower
, mntdata
);
448 len2
= strlen(lower
) + strlen(upper
) + strlen(work
) +
449 strlen("upperdir=,lowerdir=,workdir=") +
451 options_work
= must_realloc(NULL
, len2
);
452 ret2
= snprintf(options
, len2
,
453 "upperdir=%s,lowerdir=%s,workdir=%s,%s", upper
,
454 lower
, work
, mntdata
);
456 len
= strlen(lower
) + strlen(upper
) +
457 strlen("upperdir=,lowerdir=") + 1;
458 options
= must_realloc(NULL
, len
);
459 ret
= snprintf(options
, len
, "upperdir=%s,lowerdir=%s", upper
,
462 len2
= strlen(lower
) + strlen(upper
) + strlen(work
) +
463 strlen("upperdir=,lowerdir=,workdir=") + 1;
464 options_work
= must_realloc(NULL
, len2
);
465 ret2
= snprintf(options_work
, len2
,
466 "upperdir=%s,lowerdir=%s,workdir=%s", upper
,
470 if (ret
< 0 || ret
>= len
|| ret2
< 0 || ret2
>= len2
) {
471 ERROR("Failed to create string");
478 /* Assume we need a workdir as we are on a overlay version >= v22. */
479 ret
= ovl_remount_on_enodev(lower
, bdev
->dest
, ovl_name
,
480 MS_MGC_VAL
| mntflags
, options_work
);
482 SYSINFO("Failed to mount \"%s\" on \"%s\" with options \"%s\". "
483 "Retrying without workdir",
484 lower
, bdev
->dest
, options_work
);
486 /* Assume we cannot use a workdir as we are on a version <= v21.
488 ret
= ovl_remount_on_enodev(lower
, bdev
->dest
, ovl_name
,
489 MS_MGC_VAL
| mntflags
, options
);
491 SYSERROR("Failed to mount \"%s\" on \"%s\" with options \"%s\"",
492 lower
, bdev
->dest
, options
);
494 INFO("Mounted \"%s\" on \"%s\" with options \"%s\"",
495 lower
, bdev
->dest
, options
);
497 INFO("Mounted \"%s\" on \"%s\" with options \"%s\"", lower
,
498 bdev
->dest
, options_work
);
506 int ovl_umount(struct lxc_storage
*bdev
)
510 if (strcmp(bdev
->type
, "overlay") && strcmp(bdev
->type
, "overlayfs"))
513 if (!bdev
->src
|| !bdev
->dest
)
516 ret
= umount(bdev
->dest
);
518 SYSERROR("Failed to unmount \"%s\"", bdev
->dest
);
520 TRACE("Unmounted \"%s\"", bdev
->dest
);
525 const char *ovl_get_lower(const char *rootfs_path
)
527 const char *s1
= rootfs_path
;
529 if (strncmp(rootfs_path
, "overlay:", 8) == 0)
531 else if (strncmp(rootfs_path
, "overlayfs:", 10) == 0)
534 s1
= strstr(s1
, ":/");
542 char *ovl_get_rootfs(const char *rootfs_path
, size_t *rootfslen
)
544 char *rootfsdir
= NULL
;
549 if (!rootfs_path
|| !rootfslen
)
552 s1
= strdup(rootfs_path
);
557 if (strncmp(rootfs_path
, "overlay:", 8) == 0)
559 else if (strncmp(rootfs_path
, "overlayfs:", 10) == 0)
562 s3
= strstr(s2
, ":/");
566 rootfsdir
= strdup(s2
);
571 *rootfslen
= strlen(rootfsdir
);
576 int ovl_mkdir(const struct mntent
*mntent
, const struct lxc_rootfs
*rootfs
,
577 const char *lxc_name
, const char *lxc_path
)
579 char lxcpath
[PATH_MAX
];
582 size_t arrlen
, i
, len
, rootfslen
;
585 char *rootfs_dir
= NULL
, *rootfs_path
= NULL
, *upperdir
= NULL
,
588 /* When rootfs == NULL we have a container without a rootfs. */
589 if (rootfs
&& rootfs
->path
)
590 rootfs_path
= rootfs
->path
;
592 opts
= lxc_string_split(mntent
->mnt_opts
, ',');
594 arrlen
= lxc_array_len((void **)opts
);
598 for (i
= 0; i
< arrlen
; i
++) {
599 if (strstr(opts
[i
], "upperdir=") &&
600 (strlen(opts
[i
]) > (len
= strlen("upperdir="))))
601 upperdir
= opts
[i
] + len
;
602 else if (strstr(opts
[i
], "workdir=") &&
603 (strlen(opts
[i
]) > (len
= strlen("workdir="))))
604 workdir
= opts
[i
] + len
;
608 ret
= snprintf(lxcpath
, PATH_MAX
, "%s/%s", lxc_path
, lxc_name
);
609 if (ret
< 0 || ret
>= PATH_MAX
)
612 rootfs_dir
= ovl_get_rootfs(rootfs_path
, &rootfslen
);
616 dirlen
= strlen(lxcpath
);
620 * We neither allow users to create upperdirs and workdirs outside the
621 * containerdir nor inside the rootfs. The latter might be debatable.
622 * When we have a container without a rootfs we skip the checks.
627 ret
= mkdir_p(upperdir
, 0755);
628 else if (!strncmp(upperdir
, lxcpath
, dirlen
) &&
629 strncmp(upperdir
, rootfs_dir
, rootfslen
))
630 ret
= mkdir_p(upperdir
, 0755);
633 SYSWARN("Failed to create directory \"%s\"", upperdir
);
639 ret
= mkdir_p(workdir
, 0755);
640 else if (!strncmp(workdir
, lxcpath
, dirlen
) &&
641 strncmp(workdir
, rootfs_dir
, rootfslen
))
642 ret
= mkdir_p(workdir
, 0755);
645 SYSWARN("Failed to create directory \"%s\"", workdir
);
652 lxc_free_array((void **)opts
, free
);
656 /* To be called from lxcapi_clone() in lxccontainer.c: When we clone a container
657 * with overlay lxc.mount.entry entries we need to update absolute paths for
658 * upper- and workdir. This update is done in two locations:
659 * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done
660 * independent of each other since lxc_conf->mountlist may contain more mount
661 * entries (e.g. from other included files) than lxc_conf->unexpanded_config.
663 int ovl_update_abs_paths(struct lxc_conf
*lxc_conf
, const char *lxc_path
,
664 const char *lxc_name
, const char *newpath
,
667 char new_upper
[PATH_MAX
], new_work
[PATH_MAX
], old_upper
[PATH_MAX
],
670 struct lxc_list
*iterator
;
671 char *cleanpath
= NULL
;
674 const char *ovl_dirs
[] = {"br", "upperdir", "workdir"};
676 cleanpath
= strdup(newpath
);
680 remove_trailing_slashes(cleanpath
);
683 * We have to update lxc_conf->unexpanded_config separately from
684 * lxc_conf->mount_list.
686 for (i
= 0; i
< sizeof(ovl_dirs
) / sizeof(ovl_dirs
[0]); i
++) {
687 if (!clone_update_unexp_ovl_paths(lxc_conf
, lxc_path
, newpath
,
694 snprintf(old_work
, PATH_MAX
, "workdir=%s/%s", lxc_path
, lxc_name
);
695 if (ret
< 0 || ret
>= PATH_MAX
)
699 snprintf(new_work
, PATH_MAX
, "workdir=%s/%s", cleanpath
, newname
);
700 if (ret
< 0 || ret
>= PATH_MAX
)
703 lxc_list_for_each(iterator
, &lxc_conf
->mount_list
) {
704 char *mnt_entry
= NULL
, *new_mnt_entry
= NULL
, *tmp
= NULL
,
705 *tmp_mnt_entry
= NULL
;
707 mnt_entry
= iterator
->elem
;
709 if (strstr(mnt_entry
, "overlay"))
714 ret
= snprintf(old_upper
, PATH_MAX
, "%s=%s/%s", tmp
, lxc_path
,
716 if (ret
< 0 || ret
>= PATH_MAX
)
719 ret
= snprintf(new_upper
, PATH_MAX
, "%s=%s/%s", tmp
,
721 if (ret
< 0 || ret
>= PATH_MAX
)
724 if (strstr(mnt_entry
, old_upper
)) {
726 lxc_string_replace(old_upper
, new_upper
, mnt_entry
);
729 if (strstr(mnt_entry
, old_work
)) {
731 new_mnt_entry
= lxc_string_replace(
732 old_work
, new_work
, tmp_mnt_entry
);
734 new_mnt_entry
= lxc_string_replace(
735 old_work
, new_work
, mnt_entry
);
739 free(iterator
->elem
);
740 iterator
->elem
= strdup(new_mnt_entry
);
741 } else if (tmp_mnt_entry
) {
742 free(iterator
->elem
);
743 iterator
->elem
= strdup(tmp_mnt_entry
);
756 static int ovl_remount_on_enodev(const char *lower
, const char *target
,
757 const char *name
, unsigned long mountflags
,
761 ret
= mount(lower
, target
, ovl_name
, MS_MGC_VAL
| mountflags
, options
);
762 if (ret
< 0 && errno
== ENODEV
) /* Try other module name. */
763 ret
= mount(lower
, target
,
764 ovl_name
== ovl_version
[0] ? ovl_version
[1]
766 MS_MGC_VAL
| mountflags
, options
);
770 static char *ovl_detect_name(void)
773 char *v
= ovl_version
[0];
777 f
= fopen("/proc/filesystems", "r");
781 while (getline(&line
, &len
, f
) != -1) {
782 if (strcmp(line
, "nodev\toverlayfs\n") == 0) {
793 static int ovl_do_rsync(const char *src
, const char *dest
,
794 struct lxc_conf
*conf
)
797 struct rsync_data_char rdata
= {0};
798 char cmd_output
[PATH_MAX
] = {0};
800 rdata
.src
= (char *)src
;
801 rdata
.dest
= (char *)dest
;
802 if (am_guest_unpriv())
803 ret
= userns_exec_full(conf
, lxc_rsync_exec_wrapper
, &rdata
,
804 "lxc_rsync_exec_wrapper");
806 ret
= run_command(cmd_output
, sizeof(cmd_output
),
807 lxc_rsync_exec_wrapper
, (void *)&rdata
);
809 ERROR("Failed to rsync from \"%s\" into \"%s\"%s%s", src
, dest
,
810 cmd_output
[0] != '\0' ? ": " : "",
811 cmd_output
[0] != '\0' ? cmd_output
: "");