Skip to content

Commit c5c755e

Browse files
authored
Merge pull request systemd#4693 from poettering/nspawn-ephemeral
nspawn: support ephemeral boots from images
2 parents 1a1b13c + acbbf69 commit c5c755e

File tree

9 files changed

+193
-114
lines changed

9 files changed

+193
-114
lines changed

man/machinectl.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,8 @@
599599
<listitem><para>Clones a container or VM image. The arguments specify the name of the image to clone and the
600600
name of the newly cloned image. Note that plain directory container images are cloned into btrfs subvolume
601601
images with this command, if the underlying file system supports this. Note that cloning a container or VM
602-
image is optimized for btrfs file systems, and might not be efficient on others, due to file system
603-
limitations.</para>
602+
image is optimized for file systems that support copy-on-write, and might not be efficient on others, due to
603+
file system limitations.</para>
604604

605605
<para>Note that this command leaves host name, machine ID and
606606
all other settings that could identify the instance
@@ -910,7 +910,7 @@
910910
<filename>/var/lib/machines/</filename> to make them available for
911911
control with <command>machinectl</command>.</para>
912912

913-
<para>Note that many image operations are only supported,
913+
<para>Note that some image operations are only supported,
914914
efficient or atomic on btrfs file systems. Due to this, if the
915915
<command>pull-tar</command>, <command>pull-raw</command>,
916916
<command>import-tar</command>, <command>import-raw</command> and

man/systemd-nspawn.xml

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -181,25 +181,15 @@
181181
<varlistentry>
182182
<term><option>--template=</option></term>
183183

184-
<listitem><para>Directory or <literal>btrfs</literal>
185-
subvolume to use as template for the container's root
186-
directory. If this is specified and the container's root
187-
directory (as configured by <option>--directory=</option>)
188-
does not yet exist it is created as <literal>btrfs</literal>
189-
subvolume and populated from this template tree. Ideally, the
190-
specified template path refers to the root of a
191-
<literal>btrfs</literal> subvolume, in which case a simple
192-
copy-on-write snapshot is taken, and populating the root
193-
directory is instant. If the specified template path does not
194-
refer to the root of a <literal>btrfs</literal> subvolume (or
195-
not even to a <literal>btrfs</literal> file system at all),
196-
the tree is copied, which can be substantially more
197-
time-consuming. Note that if this option is used the
198-
container's root directory (in contrast to the template
199-
directory!) must be located on a <literal>btrfs</literal> file
200-
system, so that the <literal>btrfs</literal> subvolume may be
201-
created. May not be specified together with
202-
<option>--image=</option> or
184+
<listitem><para>Directory or <literal>btrfs</literal> subvolume to use as template for the container's root
185+
directory. If this is specified and the container's root directory (as configured by
186+
<option>--directory=</option>) does not yet exist it is created as <literal>btrfs</literal> snapshot (if
187+
supported) or plain directory (otherwise) and populated from this template tree. Ideally, the specified
188+
template path refers to the root of a <literal>btrfs</literal> subvolume, in which case a simple copy-on-write
189+
snapshot is taken, and populating the root directory is instant. If the specified template path does not refer
190+
to the root of a <literal>btrfs</literal> subvolume (or not even to a <literal>btrfs</literal> file system at
191+
all), the tree is copied (though possibly in a copy-on-write scheme — if the file system supports that), which
192+
can be substantially more time-consuming. May not be specified together with <option>--image=</option> or
203193
<option>--ephemeral</option>.</para>
204194

205195
<para>Note that this switch leaves host name, machine ID and
@@ -211,13 +201,8 @@
211201
<term><option>-x</option></term>
212202
<term><option>--ephemeral</option></term>
213203

214-
<listitem><para>If specified, the container is run with a
215-
temporary <literal>btrfs</literal> snapshot of its root
216-
directory (as configured with <option>--directory=</option>),
217-
that is removed immediately when the container terminates.
218-
This option is only supported if the root file system is
219-
<literal>btrfs</literal>. May not be specified together with
220-
<option>--image=</option> or
204+
<listitem><para>If specified, the container is run with a temporary snapshot of its file system that is removed
205+
immediately when the container terminates. May not be specified together with
221206
<option>--template=</option>.</para>
222207
<para>Note that this switch leaves host name, machine ID and
223208
all other settings that could identify the instance
@@ -252,11 +237,12 @@
252237
Partitions Specification</ulink>.</para></listitem>
253238
</itemizedlist>
254239

255-
<para>Any other partitions, such as foreign partitions, swap
256-
partitions or EFI system partitions are not mounted. May not
257-
be specified together with <option>--directory=</option>,
258-
<option>--template=</option> or
259-
<option>--ephemeral</option>.</para></listitem>
240+
<para>On GPT images, if an EFI System Partition (ESP) is discovered, it is automatically mounted to
241+
<filename>/efi</filename> (or <filename>/boot</filename> as fallback) in case a directory by this name exists
242+
and is empty.</para>
243+
244+
<para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
245+
together with <option>--directory=</option>, <option>--template=</option>.</para></listitem>
260246
</varlistentry>
261247

262248
<varlistentry>
@@ -1056,14 +1042,12 @@
10561042
</example>
10571043

10581044
<example>
1059-
<title>Boot into an ephemeral <literal>btrfs</literal> snapshot of the host system</title>
1045+
<title>Boot into an ephemeral snapshot of the host system</title>
10601046

10611047
<programlisting># systemd-nspawn -D / -xb</programlisting>
10621048

1063-
<para>This runs a copy of the host system in a
1064-
<literal>btrfs</literal> snapshot which is removed immediately
1065-
when the container exits. All file system changes made during
1066-
runtime will be lost on shutdown, hence.</para>
1049+
<para>This runs a copy of the host system in a snapshot which is removed immediately when the container
1050+
exits. All file system changes made during runtime will be lost on shutdown, hence.</para>
10671051
</example>
10681052

10691053
<example>

src/basic/btrfs-util.c

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <errno.h>
2121
#include <fcntl.h>
2222
#include <inttypes.h>
23+
#include <linux/fs.h>
2324
#include <linux/loop.h>
2425
#include <stddef.h>
2526
#include <stdio.h>
@@ -38,13 +39,15 @@
3839
#include "alloc-util.h"
3940
#include "btrfs-ctree.h"
4041
#include "btrfs-util.h"
42+
#include "chattr-util.h"
4143
#include "copy.h"
4244
#include "fd-util.h"
4345
#include "fileio.h"
4446
#include "io-util.h"
4547
#include "macro.h"
4648
#include "missing.h"
4749
#include "path-util.h"
50+
#include "rm-rf.h"
4851
#include "selinux-util.h"
4952
#include "smack-util.h"
5053
#include "sparse-endian.h"
@@ -1718,28 +1721,46 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
17181721
if (r < 0)
17191722
return r;
17201723
if (r == 0) {
1724+
bool plain_directory = false;
1725+
1726+
/* If the source isn't a proper subvolume, fail unless fallback is requested */
17211727
if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
17221728
return -EISDIR;
17231729

17241730
r = btrfs_subvol_make(new_path);
1725-
if (r < 0)
1731+
if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
1732+
/* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
1733+
if (mkdir(new_path, 0755) < 0)
1734+
return r;
1735+
1736+
plain_directory = true;
1737+
} else if (r < 0)
17261738
return r;
17271739

17281740
r = copy_directory_fd(old_fd, new_path, true);
1729-
if (r < 0) {
1730-
(void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
1731-
return r;
1732-
}
1741+
if (r < 0)
1742+
goto fallback_fail;
17331743

17341744
if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1735-
r = btrfs_subvol_set_read_only(new_path, true);
1736-
if (r < 0) {
1737-
(void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
1738-
return r;
1745+
1746+
if (plain_directory) {
1747+
/* Plain directories have no recursive read-only flag, but something pretty close to
1748+
* it: the IMMUTABLE bit. Let's use this here, if this is requested. */
1749+
1750+
if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
1751+
(void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
1752+
} else {
1753+
r = btrfs_subvol_set_read_only(new_path, true);
1754+
if (r < 0)
1755+
goto fallback_fail;
17391756
}
17401757
}
17411758

17421759
return 0;
1760+
1761+
fallback_fail:
1762+
(void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
1763+
return r;
17431764
}
17441765

17451766
r = extract_subvolume_name(new_path, &subvolume);

src/basic/btrfs-util.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ typedef struct BtrfsQuotaInfo {
4545
} BtrfsQuotaInfo;
4646

4747
typedef enum BtrfsSnapshotFlags {
48-
BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
48+
BTRFS_SNAPSHOT_FALLBACK_COPY = 1, /* If the source isn't a subvolume, reflink everything */
4949
BTRFS_SNAPSHOT_READ_ONLY = 2,
5050
BTRFS_SNAPSHOT_RECURSIVE = 4,
5151
BTRFS_SNAPSHOT_QUOTA = 8,
52+
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 16, /* If the destination doesn't support subvolumes, reflink/copy instead */
53+
BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 32, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */
5254
} BtrfsSnapshotFlags;
5355

5456
typedef enum BtrfsRemoveFlags {

src/basic/missing.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@
143143
#define GRND_RANDOM 0x0002
144144
#endif
145145

146+
#ifndef FS_NOCOW_FL
147+
#define FS_NOCOW_FL 0x00800000
148+
#endif
149+
146150
#ifndef BTRFS_IOCTL_MAGIC
147151
#define BTRFS_IOCTL_MAGIC 0x94
148152
#endif

src/import/pull-common.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ int pull_make_local_copy(const char *final, const char *image_root, const char *
144144
if (force_local)
145145
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
146146

147-
r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA);
148-
if (r == -ENOTTY) {
149-
r = copy_tree(final, p, false);
150-
if (r < 0)
151-
return log_error_errno(r, "Failed to copy image: %m");
152-
} else if (r < 0)
147+
r = btrfs_subvol_snapshot(final, p,
148+
BTRFS_SNAPSHOT_QUOTA|
149+
BTRFS_SNAPSHOT_FALLBACK_COPY|
150+
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
151+
BTRFS_SNAPSHOT_RECURSIVE);
152+
if (r < 0)
153153
return log_error_errno(r, "Failed to create local image: %m");
154154

155155
log_info("Created new local image '%s'.", local);

src/nspawn/nspawn-mount.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
298298
MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL);
299299
}
300300

301-
static int mkdir_userns(const char *path, mode_t mode, bool in_userns, uid_t uid_shift) {
301+
static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) {
302302
int r;
303303

304304
assert(path);
@@ -307,16 +307,20 @@ static int mkdir_userns(const char *path, mode_t mode, bool in_userns, uid_t uid
307307
if (r < 0 && errno != EEXIST)
308308
return -errno;
309309

310-
if (!in_userns) {
311-
r = lchown(path, uid_shift, uid_shift);
312-
if (r < 0)
313-
return -errno;
314-
}
310+
if ((mask & MOUNT_USE_USERNS) == 0)
311+
return 0;
312+
313+
if (mask & MOUNT_IN_USERNS)
314+
return 0;
315+
316+
r = lchown(path, uid_shift, uid_shift);
317+
if (r < 0)
318+
return -errno;
315319

316320
return 0;
317321
}
318322

319-
static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, bool in_userns, uid_t uid_shift) {
323+
static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) {
320324
const char *p, *e;
321325
int r;
322326

@@ -343,12 +347,12 @@ static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, boo
343347
if (prefix && path_startswith(prefix, t))
344348
continue;
345349

346-
r = mkdir_userns(t, mode, in_userns, uid_shift);
350+
r = mkdir_userns(t, mode, mask, uid_shift);
347351
if (r < 0)
348352
return r;
349353
}
350354

351-
return mkdir_userns(path, mode, in_userns, uid_shift);
355+
return mkdir_userns(path, mode, mask, uid_shift);
352356
}
353357

354358
int mount_all(const char *dest,
@@ -422,7 +426,7 @@ int mount_all(const char *dest,
422426
if (mount_table[k].what && r > 0)
423427
continue;
424428

425-
r = mkdir_userns_p(dest, where, 0755, in_userns, uid_shift);
429+
r = mkdir_userns_p(dest, where, 0755, mount_settings, uid_shift);
426430
if (r < 0 && r != -EEXIST) {
427431
if (fatal)
428432
return log_error_errno(r, "Failed to create directory %s: %m", where);

0 commit comments

Comments
 (0)