<cmdsynopsis>
<command>systemd-repart</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
- <arg choice="opt" rep="repeat"><replaceable><optional>BLOCKDEVICE</optional></replaceable></arg>
+ <arg choice="opt">BLOCKDEVICE</arg>
</cmdsynopsis>
<para><filename>systemd-repart.service</filename></para>
<refsect1>
<title>Description</title>
- <para><command>systemd-repart</command> creates partition tables, and adds or grows partitions,
- based on the configuration files described in
- <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- </para>
+ <para><command>systemd-repart</command> creates partition tables, and adds or grows partitions, based on
+ the configuration files described in
+ <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. It operates
+ on the block device or file image specified on the command line.</para>
<para><command>systemd-repart</command> is used when <emphasis>building</emphasis> OS images, and also
when <emphasis>deploying</emphasis> images to automatically adjust them, during boot, to the system they
<filename>systemd-repart.service</filename> service is generally run at boot in the initrd, in order to
augment the partition table of the OS before its partitions are mounted.</para>
+ <para>If the block device is specified as <literal>-</literal> (or as an empty string),
+ <command>systemd-repart</command> will not operate on any block device or image file, and instead
+ determine and output the minimum disk/image size for the specified partition configuration, taking all
+ configured size constraints into account.</para>
+
<para><command>systemd-repart</command> operations are mostly incremental: it grows existing partitions
or adds new ones, but does not shrink, delete, or move existing partitions. The service is intended to be
run on every boot, but when it detects that the partition table already matches the installed
static EmptyMode arg_empty = EMPTY_UNSET;
static bool arg_dry_run = true;
static char *arg_node = NULL;
+static bool arg_node_none = false;
static char *arg_root = NULL;
static char *arg_image = NULL;
static char **arg_definitions = NULL;
.private_key = private_key,
.empty = empty,
.dry_run = dry_run,
+ .backing_fd = -EBADF,
};
return context;
hmac_sha256(base.bytes, sizeof(base.bytes), token, strlen(token), ret);
}
+static int context_load_fallback_metrics(Context *context) {
+ assert(context);
+
+ context->sector_size = arg_sector_size > 0 ? arg_sector_size : 512;
+ context->grain_size = MAX(context->sector_size, 4096U);
+ context->default_fs_sector_size = arg_sector_size > 0 ? arg_sector_size : DEFAULT_FILESYSTEM_SECTOR_SIZE;
+ return 1; /* Starting from scratch */
+}
+
static int context_load_partition_table(Context *context) {
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
_cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
int r;
assert(context);
+ assert(context->node);
assert(!context->fdisk_context);
assert(!context->free_areas);
assert(context->start == UINT64_MAX);
if (fd < 0)
return log_error_errno(fd, "Failed to create temporary file: %m");
- r = read_attr_fd(fdisk_get_devfd(context->fdisk_context), &attrs);
- if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
- log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node);
-
- if (FLAGS_SET(attrs, FS_NOCOW_FL)) {
- r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL);
- if (r < 0 && !ERRNO_IS_IOCTL_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to disable copy-on-write on %s: %m", temp);
+ if (context->fdisk_context) {
+ r = read_attr_fd(fdisk_get_devfd(context->fdisk_context), &attrs);
+ if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node);
+
+ if (FLAGS_SET(attrs, FS_NOCOW_FL)) {
+ r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL);
+ if (r < 0 && !ERRNO_IS_IOCTL_NOT_SUPPORTED(r))
+ return log_error_errno(r, "Failed to disable copy-on-write on %s: %m", temp);
+ }
}
if (ftruncate(fd, size) < 0)
assert(context);
- r = read_attr_fd(context->backing_fd, &attrs);
- if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
- log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node);
+ if (context->backing_fd >= 0) {
+ r = read_attr_fd(context->backing_fd, &attrs);
+ if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node);
+ }
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
}
if (argc > optind) {
- arg_node = strdup(argv[optind]);
- if (!arg_node)
- return log_oom();
+ if (empty_or_dash(argv[optind]))
+ arg_node_none = true;
+ else {
+ arg_node = strdup(argv[optind]);
+ if (!arg_node)
+ return log_oom();
+ arg_node_none = false;
+ }
}
if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image)
assert(context);
+ if (arg_node_none)
+ return 0;
+
if (arg_node) {
if (context->empty == EMPTY_CREATE) {
_cleanup_close_ int fd = -EBADF;
return 1;
}
-static int determine_auto_size(Context *c) {
+static int determine_auto_size(Context *c, uint64_t *ret) {
uint64_t sum;
assert(c);
if (c->total != UINT64_MAX)
/* Image already allocated? Then show its size. */
- log_info("Automatically determined minimal disk image size as %s, current image size is %s.",
+ log_info("Automatically determined minimal disk image size as %s, current block device/image size is %s.",
FORMAT_BYTES(sum), FORMAT_BYTES(c->total));
else
/* If the image is being created right now, then it has no previous size, suppress any comment about it hence. */
log_info("Automatically determined minimal disk image size as %s.",
FORMAT_BYTES(sum));
- arg_size = sum;
+ if (ret)
+ *ret = sum;
return 0;
}
continue; /* Still no luck. Let's drop a priority and try again. */
/* No more priorities left to drop. This configuration just doesn't fit on this disk... */
- r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
- "Can't fit requested partitions into available free space (%s), refusing.",
- FORMAT_BYTES(largest_free_area));
- (void) determine_auto_size(context);
- return r;
+ return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+ "Can't fit requested partitions into available free space (%s), refusing.",
+ FORMAT_BYTES(largest_free_area));
}
LIST_FOREACH(partitions, p, context->partitions) {
if (!arg_root)
return log_oom();
- if (!arg_node) {
+ if (!arg_node && !arg_node_none) {
arg_node = strdup(loop_device->node);
if (!arg_node)
return log_oom();
if (r < 0)
return r;
- if (arg_size != UINT64_MAX) {
- r = resize_backing_fd(
- context->node,
- &context->backing_fd,
- node_is_our_loop ? arg_image : NULL,
- node_is_our_loop ? loop_device : NULL,
- context->sector_size);
- if (r < 0)
- return r;
- }
+ if (context->node) {
+ if (arg_size != UINT64_MAX) {
+ r = resize_backing_fd(
+ context->node,
+ &context->backing_fd,
+ node_is_our_loop ? arg_image : NULL,
+ node_is_our_loop ? loop_device : NULL,
+ context->sector_size);
+ if (r < 0)
+ return r;
+ }
- r = context_load_partition_table(context);
- if (r == -EHWPOISON)
- return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
- * really an error when called at boot. */
+ r = context_load_partition_table(context);
+ if (r == -EHWPOISON)
+ return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
+ * really an error when called at boot. */
+ } else
+ r = context_load_fallback_metrics(context);
if (r < 0)
return r;
context->from_scratch = r > 0; /* Starting from scratch */
if (r < 0)
return r;
+ if (arg_node_none) {
+ (void) determine_auto_size(context, /* ret= */ NULL);
+ return 0;
+ }
+
if (arg_size_auto) {
- r = determine_auto_size(context);
+ r = determine_auto_size(context, &arg_size);
if (r < 0)
return r;
}
r = context_ponder(context);
+ if (r == -ENOSPC) {
+ /* When we hit space issues, tell the user the minimal size. */
+ (void) determine_auto_size(context, /* ret= */ NULL);
+ return r;
+ }
if (r < 0)
return r;