1 /* SPDX-License-Identifier: GPL-2.0-only */
16 #include <sys/types.h>
21 #include <lxc/lxccontainer.h>
23 #include "arguments.h"
26 #include "storage_utils.h"
29 #ifndef HAVE_GETSUBOPT
30 #include "getsubopt.h"
33 lxc_log_define(lxc_copy
, lxc
);
41 enum mnttype mnt_type
;
50 static unsigned int mnt_table_size
= 0;
51 static struct mnts
*mnt_table
= NULL
;
53 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
);
55 static const struct option my_longopts
[] = {
56 { "newname", required_argument
, 0, 'N'},
57 { "newpath", required_argument
, 0, 'p'},
58 { "rename", no_argument
, 0, 'R'},
59 { "snapshot", no_argument
, 0, 's'},
60 { "allowrunning", no_argument
, 0, 'a'},
61 { "foreground", no_argument
, 0, 'F'},
62 { "daemon", no_argument
, 0, 'd'},
63 { "ephemeral", no_argument
, 0, 'e'},
64 { "mount", required_argument
, 0, 'm'},
65 { "backingstorage", required_argument
, 0, 'B'},
66 { "fssize", required_argument
, 0, 'L'},
67 { "keepdata", no_argument
, 0, 'D'},
68 { "keepname", no_argument
, 0, 'K'},
69 { "keepmac", no_argument
, 0, 'M'},
70 { "tmpfs", no_argument
, 0, 't'},
75 static char *const mount_keys
[] = {
76 [LXC_MNT_BIND
] = "bind",
77 [LXC_MNT_OVL
] = "overlay",
81 static struct lxc_arguments my_args
= {
82 .progname
= "lxc-copy",
84 --name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]] -- hook options\n\
85 --name=NAME [-P lxcpath] [-N newname] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,overlay}=/src:/dest] -- hook options\n\
86 --name=NAME [-P lxcpath] -N newname -R\n\
88 lxc-copy clone a container\n\
91 -n, --name=NAME NAME of the container\n\
92 -N, --newname=NEWNAME NEWNAME for the restored container\n\
93 -p, --newpath=NEWPATH NEWPATH for the container to be stored\n\
94 -R, --rename rename container\n\
95 -s, --snapshot create snapshot instead of clone\n\
96 -a, --allowrunning allow snapshot creation even if source container is running\n\
97 -F, --foreground start with current tty attached to /dev/console\n\
98 -d, --daemon daemonize the container (default)\n\
99 -e, --ephemeral start ephemeral container\n\
100 -m, --mount directory to mount into container, either \n\
101 {bind,overlay}=/src-path or {bind,overlay}=/src-path:/dst-path\n\
102 -B, --backingstorage=TYPE backingstorage type for the container\n\
103 -t, --tmpfs place ephemeral container on a tmpfs\n\
104 (WARNING: On reboot all changes made to the container will be lost.)\n\
105 -L, --fssize size of the new block device for block device containers\n\
106 -D, --keepdata pass together with -e start a persistent snapshot \n\
107 -K, --keepname keep the hostname of the original container\n\
108 -- hook options arguments passed to the hook program\n\
109 -M, --keepmac keep the MAC address of the original container\n\
110 --rcfile=FILE Load configuration file FILE\n",
111 .options
= my_longopts
,
119 static struct mnts
*add_mnt(struct mnts
**mnts
, unsigned int *num
,
121 static int mk_rand_ovl_dirs(struct mnts
*mnts
, unsigned int num
,
122 struct lxc_arguments
*arg
);
123 static char *construct_path(char *path
, bool as_prefix
);
124 static char *set_mnt_entry(struct mnts
*m
);
125 static int do_clone(struct lxc_container
*c
, char *newname
, char *newpath
,
126 int flags
, char *bdevtype
, uint64_t fssize
, enum task task
,
128 static int do_clone_ephemeral(struct lxc_container
*c
,
129 struct lxc_arguments
*arg
, char **args
,
131 static int do_clone_rename(struct lxc_container
*c
, char *newname
);
132 static int do_clone_task(struct lxc_container
*c
, enum task task
, int flags
,
134 static void free_mnts(void);
136 /* Place an ephemeral container started with -e flag on a tmpfs. Restrictions
137 * are that you cannot request the data to be kept while placing the container
138 * on a tmpfs and that either overlay storage driver must be used.
140 static char *mount_tmpfs(const char *oldname
, const char *newname
,
141 const char *path
, struct lxc_arguments
*arg
);
142 static int parse_mntsubopts(char *subopts
, char *const *keys
,
143 char *mntparameters
);
144 static int parse_bind_mnt(char *mntstring
, enum mnttype type
);
145 static int parse_ovl_mnt(char *mntstring
, enum mnttype type
);
147 int main(int argc
, char *argv
[])
149 struct lxc_container
*c
;
152 int ret
= EXIT_FAILURE
;
154 if (lxc_arguments_parse(&my_args
, argc
, argv
))
157 /* Only create log if explicitly instructed */
158 if (my_args
.log_file
|| my_args
.log_priority
) {
159 log
.name
= my_args
.name
;
160 log
.file
= my_args
.log_file
;
161 log
.level
= my_args
.log_priority
;
162 log
.prefix
= my_args
.progname
;
163 log
.quiet
= my_args
.quiet
;
164 log
.lxcpath
= my_args
.lxcpath
[0];
166 if (lxc_log_init(&log
))
171 if (access(my_args
.lxcpath
[0], O_RDONLY
) < 0) {
172 ERROR("You lack access to %s", my_args
.lxcpath
[0]);
177 if (!my_args
.newname
&& !(my_args
.task
== DESTROY
)) {
178 ERROR("You must provide a NEWNAME for the clone");
182 if (my_args
.task
== SNAP
|| my_args
.task
== DESTROY
)
183 flags
|= LXC_CLONE_SNAPSHOT
;
184 if (my_args
.allowrunning
)
185 flags
|= LXC_CLONE_ALLOW_RUNNING
;
186 if (my_args
.keepname
)
187 flags
|= LXC_CLONE_KEEPNAME
;
190 flags
|= LXC_CLONE_KEEPMACADDR
;
192 if (!my_args
.newpath
)
193 my_args
.newpath
= (char *)my_args
.lxcpath
[0];
195 c
= lxc_container_new(my_args
.name
, my_args
.lxcpath
[0]);
199 if (my_args
.rcfile
) {
202 if (!c
->load_config(c
, my_args
.rcfile
)) {
203 ERROR("Failed to load rcfile");
207 c
->configfile
= strdup(my_args
.rcfile
);
208 if (!c
->configfile
) {
209 ERROR("Out of memory setting new config filename");
214 if (!c
->may_control(c
)) {
215 ERROR("Insufficent privileges to control %s", c
->name
);
219 if (!c
->is_defined(c
)) {
220 ERROR("Container %s is not defined", c
->name
);
224 ret
= do_clone_task(c
, my_args
.task
, flags
, &argv
[optind
]);
227 lxc_container_put(c
);
234 static struct mnts
*add_mnt(struct mnts
**mnts
, unsigned int *num
, enum mnttype type
)
238 n
= realloc(*mnts
, (*num
+ 1) * sizeof(struct mnts
));
246 *m
= (struct mnts
) {.mnt_type
= type
};
251 static int mk_rand_ovl_dirs(struct mnts
*mnts
, unsigned int num
, struct lxc_arguments
*arg
)
253 char upperdir
[PATH_MAX
];
254 char workdir
[PATH_MAX
];
257 struct mnts
*m
= NULL
;
259 for (i
= 0, m
= mnts
; i
< num
; i
++, m
++) {
260 if (m
->mnt_type
== LXC_MNT_OVL
) {
261 ret
= snprintf(upperdir
, PATH_MAX
, "%s/%s/delta#XXXXXX",
262 arg
->newpath
, arg
->newname
);
263 if (ret
< 0 || ret
>= PATH_MAX
)
266 if (!mkdtemp(upperdir
))
269 m
->upper
= strdup(upperdir
);
274 if (m
->mnt_type
== LXC_MNT_OVL
) {
275 ret
= snprintf(workdir
, PATH_MAX
, "%s/%s/work#XXXXXX",
276 arg
->newpath
, arg
->newname
);
277 if (ret
< 0 || ret
>= PATH_MAX
)
280 if (!mkdtemp(workdir
))
283 m
->workdir
= strdup(workdir
);
292 static char **lxc_normalize_path(const char *path
)
295 size_t components_len
= 0;
298 components
= lxc_string_split(path
, '/');
302 /* resolve '.' and '..' */
303 for (pos
= 0; pos
< components_len
;) {
304 if (strequal(components
[pos
], ".") ||
305 (strequal(components
[pos
], "..") && pos
== 0)) {
306 /* eat this element */
307 free(components
[pos
]);
308 memmove(&components
[pos
], &components
[pos
+ 1],
309 sizeof(char *) * (components_len
- pos
));
311 } else if (strequal(components
[pos
], "..")) {
312 /* eat this and the previous element */
313 free(components
[pos
- 1]);
314 free(components
[pos
]);
315 memmove(&components
[pos
- 1], &components
[pos
+ 1],
316 sizeof(char *) * (components_len
- pos
));
327 static char *construct_path(char *path
, bool as_prefix
)
329 char **components
= NULL
;
330 char *cleanpath
= NULL
;
332 components
= lxc_normalize_path(path
);
336 cleanpath
= lxc_string_join("/", (const char **)components
, as_prefix
);
337 lxc_free_array((void **)components
, free
);
342 static char *set_mnt_entry(struct mnts
*m
)
344 char *mntentry
= NULL
;
348 if (m
->mnt_type
== LXC_MNT_OVL
) {
349 len
= strlen(" overlay lowerdir=,upperdir=,workdir=,create=dir") +
350 2 * strlen(m
->src
) + strlen(m
->dest
) + strlen(m
->upper
) +
351 strlen(m
->workdir
) + 1;
353 mntentry
= malloc(len
);
357 ret
= snprintf(mntentry
, len
, "%s %s overlay lowerdir=%s,upperdir=%s,workdir=%s,create=dir",
358 m
->src
, m
->dest
, m
->src
, m
->upper
, m
->workdir
);
359 if (ret
< 0 || (size_t)ret
>= len
)
361 } else if (m
->mnt_type
== LXC_MNT_BIND
) {
362 len
= strlen(" none bind,optional,, 0 0") +
363 strlen(is_dir(m
->src
) ? "create=dir" : "create=file") +
364 strlen(m
->src
) + strlen(m
->dest
) + strlen(m
->options
) + 1;
366 mntentry
= malloc(len
);
370 ret
= snprintf(mntentry
, len
, "%s %s none bind,optional,%s,%s 0 0",
371 m
->src
, m
->dest
, m
->options
,
372 is_dir(m
->src
) ? "create=dir" : "create=file");
373 if (ret
< 0 || (size_t)ret
>= len
)
384 static int do_clone(struct lxc_container
*c
, char *newname
, char *newpath
,
385 int flags
, char *bdevtype
, uint64_t fssize
, enum task task
,
388 struct lxc_container
*clone
;
390 clone
= c
->clone(c
, newname
, newpath
, flags
, bdevtype
, NULL
, fssize
,
393 ERROR("Failed to clone");
397 lxc_container_put(clone
);
402 static int do_clone_ephemeral(struct lxc_container
*c
,
403 struct lxc_arguments
*arg
, char **args
, int flags
)
406 char randname
[PATH_MAX
];
409 bool bret
= true, started
= false;
410 char *tmp_buf
= randname
;
411 struct lxc_container
*clone
;
412 lxc_attach_options_t attach_options
= LXC_ATTACH_OPTIONS_DEFAULT
;
413 attach_options
.env_policy
= LXC_ATTACH_CLEAR_ENV
;
416 ret
= snprintf(randname
, PATH_MAX
, "%s/%s_XXXXXX", arg
->newpath
, arg
->name
);
417 if (ret
< 0 || ret
>= PATH_MAX
)
420 if (!mkdtemp(randname
))
423 if (chmod(randname
, 0770) < 0) {
424 (void)remove(randname
);
428 arg
->newname
= randname
+ strlen(arg
->newpath
) + 1;
431 clone
= c
->clone(c
, arg
->newname
, arg
->newpath
, flags
,
432 arg
->bdevtype
, NULL
, arg
->fssize
, args
);
437 premount
= mount_tmpfs(arg
->name
, arg
->newname
, arg
->newpath
, arg
);
439 goto destroy_and_put
;
441 bret
= clone
->set_config_item(clone
, "lxc.hook.pre-mount", premount
);
444 goto destroy_and_put
;
448 if (!clone
->set_config_item(clone
, "lxc.ephemeral", "1"))
449 goto destroy_and_put
;
451 /* allocate and create random upper- and workdirs for overlay mounts */
452 if (mk_rand_ovl_dirs(mnt_table
, mnt_table_size
, arg
) < 0)
453 goto destroy_and_put
;
455 /* allocate and set mount entries */
456 struct mnts
*n
= NULL
;
457 for (i
= 0, n
= mnt_table
; i
< mnt_table_size
; i
++, n
++) {
458 char *mntentry
= NULL
;
460 mntentry
= set_mnt_entry(n
);
462 goto destroy_and_put
;
464 bret
= clone
->set_config_item(clone
, "lxc.mount.entry", mntentry
);
467 goto destroy_and_put
;
470 if (!clone
->save_config(clone
, NULL
))
471 goto destroy_and_put
;
474 printf("Created %s as clone of %s\n", arg
->newname
, arg
->name
);
476 if (arg
->tmpfs
&& !my_args
.quiet
)
477 printf("Container is placed on tmpfs.\nRebooting will cause "
478 "all changes made to it to be lost!\n");
480 if (!arg
->daemonize
&& arg
->argc
) {
481 clone
->want_daemonize(clone
, true);
483 } else if (!arg
->daemonize
) {
484 clone
->want_daemonize(clone
, false);
487 started
= clone
->start(clone
, 0, NULL
);
489 goto destroy_and_put
;
491 if (arg
->daemonize
&& arg
->argc
) {
492 ret
= clone
->attach_run_wait(clone
, &attach_options
, arg
->argv
[0], (const char *const *)arg
->argv
);
494 goto destroy_and_put
;
495 clone
->shutdown(clone
, -1);
499 lxc_container_put(clone
);
504 clone
->shutdown(clone
, -1);
506 ret
= clone
->get_config_item(clone
, "lxc.ephemeral", tmp_buf
, PATH_MAX
);
507 if (ret
> 0 && strcmp(tmp_buf
, "0"))
508 clone
->destroy(clone
);
511 lxc_container_put(clone
);
515 static int do_clone_rename(struct lxc_container
*c
, char *newname
)
517 if (!c
->rename(c
, newname
)) {
518 ERROR("Renaming container %s to %s failed", c
->name
, newname
);
525 static int do_clone_task(struct lxc_container
*c
, enum task task
, int flags
,
532 ret
= do_clone_ephemeral(c
, &my_args
, args
, flags
);
535 ret
= do_clone_rename(c
, my_args
.newname
);
538 ret
= do_clone(c
, my_args
.newname
, my_args
.newpath
, flags
,
539 my_args
.bdevtype
, my_args
.fssize
, my_args
.task
,
547 static void free_mnts(void)
550 struct mnts
*n
= NULL
;
552 for (i
= 0, n
= mnt_table
; i
< mnt_table_size
; i
++, n
++) {
565 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
)
567 char *subopts
= NULL
;
568 char *mntparameters
= NULL
;
583 args
->allowrunning
= 1;
592 args
->task
= DESTROY
;
596 if (parse_mntsubopts(subopts
, mount_keys
, mntparameters
) < 0)
600 if (strncmp(arg
, "overlay", strlen(arg
)) == 0)
602 args
->bdevtype
= arg
;
608 args
->fssize
= get_fssize(optarg
);
624 static int parse_bind_mnt(char *mntstring
, enum mnttype type
)
627 char **mntarray
= NULL
;
628 struct mnts
*m
= NULL
;
630 m
= add_mnt(&mnt_table
, &mnt_table_size
, type
);
634 mntarray
= lxc_string_split(mntstring
, ':');
638 m
->src
= construct_path(mntarray
[0], true);
642 len
= lxc_array_len((void **)mntarray
);
643 if (len
== 1) { /* bind=src */
644 m
->dest
= construct_path(mntarray
[0], false);
645 } else if (len
== 2) { /* bind=src:option or bind=src:dest */
646 if (strncmp(mntarray
[1], "rw", strlen(mntarray
[1])) == 0)
647 m
->options
= strdup("rw");
649 if (strncmp(mntarray
[1], "ro", strlen(mntarray
[1])) == 0)
650 m
->options
= strdup("ro");
653 m
->dest
= construct_path(mntarray
[0], false);
655 m
->dest
= construct_path(mntarray
[1], false);
656 } else if (len
== 3) { /* bind=src:dest:option */
657 m
->dest
= construct_path(mntarray
[1], false);
658 m
->options
= strdup(mntarray
[2]);
660 printf("Excess elements in mount specification\n");
667 m
->options
= strdup("rw");
669 if (!m
->options
|| (strncmp(m
->options
, "rw", strlen(m
->options
)) != 0 &&
670 strncmp(m
->options
, "ro", strlen(m
->options
)) != 0))
673 lxc_free_array((void **)mntarray
, free
);
678 lxc_free_array((void **)mntarray
, free
);
682 static int parse_mntsubopts(char *subopts
, char *const *keys
, char *mntparameters
)
684 while (*subopts
!= '\0') {
685 switch (getsubopt(&subopts
, keys
, &mntparameters
)) {
687 if (parse_bind_mnt(mntparameters
, LXC_MNT_BIND
) < 0)
691 if (parse_ovl_mnt(mntparameters
, LXC_MNT_OVL
) < 0)
702 static int parse_ovl_mnt(char *mntstring
, enum mnttype type
)
705 char **mntarray
= NULL
;
708 m
= add_mnt(&mnt_table
, &mnt_table_size
, type
);
712 mntarray
= lxc_string_split(mntstring
, ':');
716 m
->src
= construct_path(mntarray
[0], true);
720 len
= lxc_array_len((void **)mntarray
);
721 if (len
== 1) /* overlay=src */
722 m
->dest
= construct_path(mntarray
[0], false);
723 else if (len
== 2) /* overlay=src:dest */
724 m
->dest
= construct_path(mntarray
[1], false);
726 printf("Excess elements in mount specification\n");
731 lxc_free_array((void **)mntarray
, free
);
736 lxc_free_array((void **)mntarray
, free
);
740 /* For ephemeral snapshots backed by the overlay filesystem, this function
741 * mounts a fresh tmpfs over the containers directory if the user requests it.
742 * Because we mount a fresh tmpfs over the directory of the container the
743 * updated /etc/hostname file created during the clone residing in the upperdir
744 * (currently named "delta0" by default) will be hidden. Hence, if the user
745 * requests that the old name is not to be kept for the clone, we recreate this
746 * file on the tmpfs. This should be all that is required to restore the exact
747 * behaviour we would get with a normal clone.
749 static char *mount_tmpfs(const char *oldname
, const char *newname
,
750 const char *path
, struct lxc_arguments
*arg
)
755 char *premount
= NULL
;
758 if (arg
->tmpfs
&& arg
->keepdata
) {
760 "A container can only be placed on a tmpfs when the "
761 "overlay storage driver is used");
765 if (arg
->tmpfs
&& !arg
->bdevtype
) {
766 arg
->bdevtype
= "overlayfs";
767 } else if (arg
->tmpfs
&& arg
->bdevtype
&&
768 strncmp(arg
->bdevtype
, "overlayfs", strlen(arg
->bdevtype
)) != 0) {
770 "A container can only be placed on a tmpfs when the "
771 "overlay storage driver is used");
775 len
= strlen(path
) + strlen(newname
) + strlen("pre-start-XXXXXX") + /* //\0 */ 3;
776 premount
= malloc(len
);
780 ret
= snprintf(premount
, len
, "%s/%s/pre-start-XXXXXX", path
, newname
);
781 if (ret
< 0 || (size_t)ret
>= len
)
785 fd
= mkstemp(premount
);
790 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
)) {
791 ERROR("Failed to set close-on-exec on file descriptor");
795 if (chmod(premount
, 0755) < 0)
798 fp
= fdopen(fd
, "r+");
803 ret
= fprintf(fp
, "#! /bin/sh\n"
804 "mount -n -t tmpfs -o mode=0755 none %s/%s/overlay\n",
809 if (!arg
->keepname
) {
811 "mkdir -p %s/%s/%s/etc\n"
812 "echo %s > %s/%s/%s/etc/hostname\n",
813 path
, newname
, LXC_OVERLAY_DELTA_PATH
, newname
,
814 path
, newname
, LXC_OVERLAY_DELTA_PATH
);