Compare commits

..

11 Commits

Author SHA1 Message Date
Lennart Poettering 5c236388c3 update TODO 2020-06-30 17:13:38 +02:00
Zbigniew Jędrzejewski-Szmek 6c3a19d5b3
Merge pull request #16313 from yuwata/network-move-ipv6ll-gen-mode
network: move IPv6LinkLocalAddressGenerationMode= to [Network] section
2020-06-30 17:13:02 +02:00
Luca Boccassi 7233e91af0 core: store timestamps of unit load attempts
When the system is under heavy load, it can happen that the unit cache
is refreshed for an unrelated reason (in the test I simulate this by
attempting to start a non-existing unit). The new unit is found and
accounted for in the cache, but it's ignored since we are loading
something else.
When we actually look for it, by attempting to start it, the cache is
up to date so no refresh happens, and starting fails although we have
it loaded in the cache.

When the unit state is set to UNIT_NOT_FOUND, mark the timestamp in
u->fragment_loadtime. Then when attempting to load again we can check
both if the cache itself needs a refresh, OR if it was refreshed AFTER
the last failed attempt that resulted in the state being
UNIT_NOT_FOUND.

Update the test so that this issue reproduces more often.
2020-06-30 16:50:00 +02:00
Zbigniew Jędrzejewski-Szmek f93dd4b940
Merge pull request #16282 from poettering/repart-copy-blocks
repart: add new CopyBlocks= setting
2020-06-30 15:26:06 +02:00
Amos Bird 7aee1e7304
hwdb: adjust touchpad edge detection for X1Tab3 (#16310) 2020-06-30 19:04:08 +09:00
Yu Watanabe aa0b850bca NEWS: minor fixes 2020-06-30 19:00:18 +09:00
Yu Watanabe 6f6296b93f network: move IPv6LinkLocalAddressGenerationMode= to [Network] section
This also moves and renames variables and functions.

Follow-ups for a6f1848a23.
2020-06-30 18:58:52 +09:00
Yu Watanabe 3ea58e010d NEWS: add several entries about networkd 2020-06-30 11:29:14 +02:00
Zbigniew Jędrzejewski-Szmek 26b0fda9b7 test-repart: allow the test to pass on non-amd64 architectures
We specified type=root and expected to always get root-x86-64, which obviously
cannot work.
2020-06-30 11:06:48 +02:00
Lennart Poettering 7caaf44777 update TODO 2020-06-25 18:53:16 +02:00
Lennart Poettering 757bc2e4c1 repart: add new CopyBlocks= setting
This allows copying in arbitrary file systems on the block level into
newly created partitions.

Usecase: simple replicating OS installers or OS image builders.
2020-06-25 18:52:52 +02:00
18 changed files with 319 additions and 75 deletions

19
NEWS
View File

@ -144,6 +144,9 @@ CHANGES WITH 246 in spe:
RxMiniBufferSize= and RxJumboBufferSize= in order to configure jumbo
frame ring buffer sizes.
* networkd.conf gained a new boolean setting ManageForeignRoutes=. If
enabled systemd-networkd manages all routes configured by other tools.
* systemd-networkd's [IPv6Prefix] section in .network files gained a
new boolean setting Assign=. If enabled an address from the prefix is
automatically assigned to the interface.
@ -152,6 +155,10 @@ CHANGES WITH 246 in spe:
IPv6PDSubnetId= that allows explicit configuration of the preferred
subnet that networkd's Prefix Delegation logic assigns to interfaces.
* systemd-networkd's [Network] section gained a new setting
IPv4AcceptLocal=. If enabled the interface accepts packets with local
source addresses.
* systemd-networkd gained support for configuring the HTB queuing
discipline in the [HierarchyTokenBucket] and
[HierarchyTokenBucketClass] sections. Similar the "pfifo" qdisc may
@ -161,7 +168,7 @@ CHANGES WITH 246 in spe:
[DeficitRoundRobinSchedulerClass], "BFIFO" in [BFIFO],
"PFIFOHeadDrop" in [PFIFOHeadDrop], "PFIFOFast" in [PFIFOFast], "HHF"
in [HeavyHitterFilter], "ETS" in [EnhancedTransmissionSelection] and
"QFQ" in [QuickFairQueueingClass].
"QFQ" in [QuickFairQueueing] and [QuickFairQueueingClass].
* systemd-networkd gained support for a new Termination= setting in the
[CAN] section for configuring the termination resistor. It also
@ -205,6 +212,10 @@ CHANGES WITH 246 in spe:
* systemd-networkd supports a new Group= setting in the [Link] section
of the .network files, to control the link group.
* systemd-networkd's [Network] section gained a new
IPv6LinkLocalAddressGenerationMode= setting, which specifies how IPv6
link local address is generated.
* A new default .network file is now shipped that matches TUN/TAP
devices that begin with "vt-" in their name. Such interfaces will
have IP routing onto the host links set up automatically. This is
@ -217,6 +228,12 @@ CHANGES WITH 246 in spe:
the default) an address from any acquired delegated prefix is
automatically chosen and assigned to the interface.
* systemd-networkd's [DHCPv6] section gained a new setting RouteMetric=
which sets the route priority for routes specified by the DHCP server.
* systemd-networkd's [DHCPv6] section gained a new setting VendorClass=
which configures the vendor class information sent to DHCP server.
* The BlackList= settings in .network files' [DHCPv4] and
[IPv6AcceptRA] sections have been renamed DenyList=. The old names
are still understood to provide compatibility.

16
TODO
View File

@ -24,6 +24,9 @@ Features:
* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
* add loud warning to the logs (with catalog entry) if systemd-udev-settle is
pulled into the boot process
* build short web pages out of each catalog entry, build them along with man
pages, and include hyperlinks to them in the journal output
@ -206,13 +209,6 @@ Features:
end), in order to maximize dd'ability. Requires libfdisk work, see
https://github.com/karelzak/util-linux/issues/907
* systemd-repart: optionally, allow specifying a path to initialize new
partitions from, i.e. an fs image file or a source device node. This would
then turn systemd-repart into a simple installer: with a few .repart files
you could replicate the host system on another device. a full installer would
then be: "systemd-repart /dev/sda && bootctl install /dev/sda &&
systemd-firstboot --image= …"
* systemd-repart: MBR partition table support. Care needs to be taken regarding
Type=, so that partition definitions can sanely apply to both the GPT and the
MBR case. Idea: accept syntax "Type=gpt:home mbr:0x83" for setting the types
@ -248,15 +244,9 @@ Features:
* systemd-repart: allow disabling growing of specific partitions, or making
them (think ESP: we don't ever want to grow it, since we cannot resize vfat)
* systemd-repart: add specifier expansion, add especifier that refers to root
device node of current system, /usr device node, and matching verity, so that
an installer can be made a "copy" installer of the booted OS
* systemd-repart: make it a static checker during early boot for existence and
absence of other partitions for trusted boot environments
* systemd-repart: allow config of partition uuid
* userdb: allow username prefix searches in varlink API, allow realname and
realname substr searches in varlink API

View File

@ -434,6 +434,13 @@ evdev:name:Synaptics TM3289-002:dmi:*svnLENOVO*:pvrThinkPadX1Carbon5th*
EVDEV_ABS_35=::19
EVDEV_ABS_36=::19
# Lenovo Thinkpad X1 Tablet Gen3
evdev:input:b0003v17EFp60B5*
EVDEV_ABS_00=::12
EVDEV_ABS_01=::11
EVDEV_ABS_35=::12
EVDEV_ABS_36=::11
# Lenovo T460
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*T460*
EVDEV_ABS_00=1266:5677:44

View File

@ -300,13 +300,36 @@
<term><varname>PaddingMinBytes=</varname></term>
<term><varname>PaddingMaxBytes=</varname></term>
<listitem><para>Specifies minimum and maximum size constrains in bytes for the free space after the
<listitem><para>Specifies minimum and maximum size constraints in bytes for the free space after the
partition (the "padding"). Semantics are similar to <varname>SizeMinBytes=</varname> and
<varname>SizeMaxBytes=</varname>, except that unlike partition sizes free space can be shrunk and can
be as small as zero. By default no size constraints on padding are set, so that only
<varname>PaddingWeight=</varname> determines the size of the padding applied.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>CopyBlocks=</varname></term>
<listitem><para>Takes a path to a regular file, block device node or directory. If specified and the
partition is newly created the data from the specified path is written to the newly created
partition, on the block level. If a directory is specified the backing block device of the file
system the directory is on is determined and the data read directly from that. This option is useful
to efficiently replicate existing file systems on the block level on a new partition, for example to
build a simple OS installer or OS image builder.</para>
<para>The file specified here must have a size that is a multiple of the basic block size 512 and not
be empty. If this option is used, the size allocation algorithm is slightly altered: the partition is
created as least as big as required to fit the data in, i.e. the data size is an additional minimum
size value taken into consideration for the allocation algorithm, similar to and in addition to the
<varname>SizeMin=</varname> value configured above.</para>
<para>This option has no effect if the partition it is declared for already exists, i.e. existing
data is never overwritten. Note that the data is copied in before the partition table is updated,
i.e. before the partition actually is persistently created. This provides robustness: it is
guaranteed that the partition either doesn't exist or exists fully populated; it is not possible that
the partition exists but is not or only partially populated.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>FactoryReset=</varname></term>

View File

@ -193,16 +193,6 @@
This happens when multicast routing is enabled.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6LinkLocalAddressGenerationMode=</varname></term>
<listitem>
<para>Specifies how IPv6 link local address is generated. Takes one of <literal>eui64</literal>,
<literal>none</literal>, <literal>stable-privacy</literal> and <literal>random</literal>.
When unset, the kernel's default will be used. Note that if <varname>LinkLocalAdressing=</varname>
not configured as <literal>ipv6</literal> then <varname>IPv6LinkLocalAddressGenerationMode=</varname>
is ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Unmanaged=</varname></term>
<listitem>
@ -308,6 +298,16 @@
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6LinkLocalAddressGenerationMode=</varname></term>
<listitem>
<para>Specifies how IPv6 link local address is generated. Takes one of <literal>eui64</literal>,
<literal>none</literal>, <literal>stable-privacy</literal> and <literal>random</literal>.
When unset, the kernel's default will be used. Note that if <varname>LinkLocalAdressing=</varname>
not configured as <literal>ipv6</literal> then <varname>IPv6LinkLocalAddressGenerationMode=</varname>
is ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv4LLRoute=</varname></term>
<listitem>

View File

@ -1932,10 +1932,11 @@ unsigned manager_dispatch_load_queue(Manager *m) {
return n;
}
static bool manager_unit_cache_needs_refresh(Manager *m) {
static bool manager_unit_cache_needs_refresh(Manager *m, Unit *u) {
assert(m);
return m->unit_cache_mtime > 0 && !lookup_paths_mtime_good(&m->lookup_paths, m->unit_cache_mtime);
return m->unit_cache_mtime > 0 &&
(m->unit_cache_mtime > u->fragment_loadtime || !lookup_paths_mtime_good(&m->lookup_paths, m->unit_cache_mtime));
}
int manager_load_unit_prepare(
@ -1982,8 +1983,12 @@ int manager_load_unit_prepare(
* but if they are already referenced (because of dependencies or ordering)
* then we have to force a load of the fragment. As an optimization, check
* first if anything in the usual paths was modified since the last time
* the cache was loaded. */
if (ret->load_state == UNIT_NOT_FOUND && manager_unit_cache_needs_refresh(m))
* the cache was loaded. Also check if the last time an attempt to load the
* unit was made was before the most recent cache refresh, so that we know
* we need to try again - even if the cache is current, it might have been
* updated in a different context before we had a chance to retry loading
* this particular unit. */
if (ret->load_state == UNIT_NOT_FOUND && manager_unit_cache_needs_refresh(m, ret))
ret->load_state = UNIT_STUB;
else {
*_ret = ret;

View File

@ -1682,6 +1682,11 @@ fail:
UNIT_ERROR;
u->load_error = r;
/* Record the last time we tried to load the unit, so that if the cache gets updated between now
* and the next time an attempt is made to load this unit, we know we need to check again */
if (u->load_state == UNIT_NOT_FOUND)
u->fragment_loadtime = now(CLOCK_REALTIME);
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);

View File

@ -136,6 +136,7 @@ typedef struct Unit {
char *source_path; /* if converted, the source file */
char **dropin_paths;
usec_t fragment_loadtime;
usec_t fragment_mtime;
usec_t source_mtime;
usec_t dropin_mtime;

View File

@ -1698,7 +1698,7 @@ static int link_configure_addrgen_mode(Link *link) {
if (!link_ipv6ll_enabled(link))
ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE;
else if (link->network->ipv6_address_gen_mode < 0) {
else if (link->network->ipv6ll_address_gen_mode < 0) {
r = sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL);
if (r < 0) {
/* The file may not exist. And even if it exists, when stable_secret is unset,
@ -1709,7 +1709,7 @@ static int link_configure_addrgen_mode(Link *link) {
} else
ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
} else
ipv6ll_mode = link->network->ipv6_address_gen_mode;
ipv6ll_mode = link->network->ipv6ll_address_gen_mode;
r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
if (r < 0)
@ -4427,16 +4427,6 @@ void link_clean(Link *link) {
link_unref(set_remove(link->manager->dirty_links, link));
}
static const char* const link_ipv6_address_gen_mode_table[_LINK_IPV6_ADDRESS_GEN_MODE_MAX] = {
[LINK_IPV6_ADDRESSS_GEN_MODE_EUI64] = "eui64",
[LINK_IPV6_ADDRESSS_GEN_MODE_NONE] = "none",
[LINK_IPV6_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
[LINK_IPV6_ADDRESSS_GEN_MODE_RANDOM] = "random",
};
DEFINE_STRING_TABLE_LOOKUP(link_ipv6_address_gen_mode, LinkIPv6AddressGenMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_link_ipv6_address_gen_mode, link_ipv6_address_gen_mode, LinkIPv6AddressGenMode, "Failed to parse link IPv6 address generation mode");
static const char* const link_state_table[_LINK_STATE_MAX] = {
[LINK_STATE_PENDING] = "pending",
[LINK_STATE_INITIALIZED] = "initialized",

View File

@ -35,15 +35,6 @@ typedef enum LinkState {
_LINK_STATE_INVALID = -1
} LinkState;
typedef enum LinkIPv6AddressGenMode {
LINK_IPV6_ADDRESSS_GEN_MODE_EUI64 = IN6_ADDR_GEN_MODE_EUI64,
LINK_IPV6_ADDRESSS_GEN_MODE_NONE = IN6_ADDR_GEN_MODE_NONE,
LINK_IPV6_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
LINK_IPV6_ADDRESSS_GEN_MODE_RANDOM = IN6_ADDR_GEN_MODE_RANDOM,
_LINK_IPV6_ADDRESS_GEN_MODE_MAX,
_LINK_IPV6_ADDRESS_GEN_MODE_INVALID = -1
} LinkIPv6AddressGenMode;
typedef struct Manager Manager;
typedef struct Network Network;
typedef struct Address Address;
@ -220,9 +211,6 @@ int link_stop_clients(Link *link, bool may_keep_dhcp);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
const char* link_ipv6_address_gen_mode_to_string(LinkIPv6AddressGenMode s) _const_;
LinkIPv6AddressGenMode link_ipv6_address_gen_mode_from_string(const char *s) _pure_;
uint32_t link_get_vrf_table(Link *link);
uint32_t link_get_dhcp_route_table(Link *link);
uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
@ -232,8 +220,6 @@ int link_request_set_nexthop(Link *link);
int link_reconfigure(Link *link, bool force);
int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_link_ipv6_address_gen_mode);
int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg);
#define log_link_message_error_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_ERR, err, msg)
#define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg)

View File

@ -52,7 +52,6 @@ Link.ARP, config_parse_tristate,
Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast)
Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Link.IPv6LinkLocalAddressGenerationMode, config_parse_link_ipv6_address_gen_mode, 0, offsetof(Network, ipv6_address_gen_mode)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
@ -71,6 +70,7 @@ Network.VRF, config_parse_ifname,
Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server)
Network.LinkLocalAddressing, config_parse_link_local_address_family, 0, offsetof(Network, link_local)
Network.IPv6LinkLocalAddressGenerationMode, config_parse_ipv6_link_local_address_gen_mode, 0, offsetof(Network, ipv6ll_address_gen_mode)
Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
Network.DefaultRouteOnDevice, config_parse_bool, 0, offsetof(Network, default_route_on_device)
Network.IPv6Token, config_parse_address_generation_type, 0, 0

View File

@ -451,6 +451,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
/* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
.link_local = _ADDRESS_FAMILY_INVALID,
.ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
.ipv4_accept_local = -1,
@ -474,7 +475,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.configure_without_carrier = false,
.ignore_carrier_loss = -1,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
.ipv6_address_gen_mode = _LINK_IPV6_ADDRESS_GEN_MODE_INVALID,
.can_triple_sampling = -1,
.can_termination = -1,
.ip_service_type = -1,
@ -1392,3 +1392,13 @@ static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
[IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64",
[IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none",
[IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
[IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random",
};
DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode");

View File

@ -52,6 +52,15 @@ typedef enum KeepConfiguration {
_KEEP_CONFIGURATION_INVALID = -1,
} KeepConfiguration;
typedef enum IPv6LinkLocalAddressGenMode {
IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64 = IN6_ADDR_GEN_MODE_EUI64,
IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE = IN6_ADDR_GEN_MODE_NONE,
IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM = IN6_ADDR_GEN_MODE_RANDOM,
_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX,
_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -1
} IPv6LinkLocalAddressGenMode;
typedef struct Manager Manager;
typedef struct NetworkDHCPServerEmitAddress {
@ -163,8 +172,9 @@ struct Network {
uint32_t dhcp_server_pool_offset;
uint32_t dhcp_server_pool_size;
/* IPV4LL Support */
/* link local addressing support */
AddressFamily link_local;
IPv6LinkLocalAddressGenMode ipv6ll_address_gen_mode;
bool ipv4ll_route;
bool default_route_on_device;
@ -256,7 +266,6 @@ struct Network {
bool configure_without_carrier;
int ignore_carrier_loss;
KeepConfiguration keep_configuration;
LinkIPv6AddressGenMode ipv6_address_gen_mode;
uint32_t iaid;
DUID duid;
@ -351,6 +360,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
@ -359,3 +369,6 @@ IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
const char* keep_configuration_to_string(KeepConfiguration i) _const_;
KeepConfiguration keep_configuration_from_string(const char *s) _pure_;
const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_;
IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_;

View File

@ -114,6 +114,10 @@ struct Partition {
FreeArea *padding_area;
FreeArea *allocated_to_area;
char *copy_blocks_path;
int copy_blocks_fd;
uint64_t copy_blocks_size;
LIST_FIELDS(Partition, partitions);
};
@ -174,6 +178,8 @@ static Partition *partition_new(void) {
.padding_max = UINT64_MAX,
.partno = UINT64_MAX,
.offset = UINT64_MAX,
.copy_blocks_fd = -1,
.copy_blocks_size = UINT64_MAX,
};
return p;
@ -192,6 +198,9 @@ static Partition* partition_free(Partition *p) {
if (p->new_partition)
fdisk_unref_partition(p->new_partition);
free(p->copy_blocks_path);
safe_close(p->copy_blocks_fd);
return mfree(p);
}
@ -339,7 +348,11 @@ static uint64_t partition_min_size(const Partition *p) {
}
sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE;
return MAX(p->size_min == UINT64_MAX ? DEFAULT_MIN_SIZE : p->size_min, sz);
if (p->copy_blocks_size != UINT64_MAX)
sz = MAX(p->copy_blocks_size, sz);
return MAX(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, sz);
}
static uint64_t partition_max_size(const Partition *p) {
@ -997,6 +1010,7 @@ static int partition_read_definition(Partition *p, const char *path) {
{ "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
{ "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
{ "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
{ "Partition", "CopyBlocks", config_parse_path, 0, &p->copy_blocks_path },
{}
};
int r;
@ -2126,6 +2140,48 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
return 0;
}
static int context_copy_blocks(Context *context) {
Partition *p;
int fd = -1, r;
assert(context);
/* Copy in file systems on the block level */
LIST_FOREACH(partitions, p, context->partitions) {
char buf[FORMAT_BYTES_MAX];
if (p->copy_blocks_fd < 0)
continue;
if (p->dropped)
continue;
if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
continue;
assert(p->new_size != UINT64_MAX);
assert(p->copy_blocks_size != UINT64_MAX);
assert(p->new_size >= p->copy_blocks_size);
if (fd < 0)
assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1)
return log_error_errno(errno, "Failed to seek to partition offset: %m");
log_info("Copying in '%s' (%s) on block level into partition %" PRIu64 ".", p->copy_blocks_path, format_bytes(buf, sizeof(buf), p->copy_blocks_size), p->partno);
r = copy_bytes_full(p->copy_blocks_fd, fd, p->copy_blocks_size, 0, NULL, NULL, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
}
return 0;
}
static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
struct {
sd_id128_t type_uuid;
@ -2388,6 +2444,10 @@ static int context_write_partition_table(
if (r < 0)
return r;
r = context_copy_blocks(context);
if (r < 0)
return r;
LIST_FOREACH(partitions, p, context->partitions) {
if (p->dropped)
continue;
@ -2633,6 +2693,87 @@ static int context_can_factory_reset(Context *context) {
return false;
}
static int context_open_copy_block_paths(Context *context) {
Partition *p;
int r;
assert(context);
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_close_ int source_fd = -1;
uint64_t size;
struct stat st;
assert(p->copy_blocks_fd < 0);
assert(p->copy_blocks_size == UINT64_MAX);
if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
continue;
if (!p->copy_blocks_path)
continue;
source_fd = open(p->copy_blocks_path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (source_fd < 0)
return log_error_errno(errno, "Failed to open block copy file '%s': %m", p->copy_blocks_path);
if (fstat(source_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat block copy file '%s': %m", p->copy_blocks_path);
if (S_ISDIR(st.st_mode)) {
_cleanup_free_ char *bdev = NULL;
/* If the file is a directory, automatically find the backing block device */
if (major(st.st_dev) != 0)
r = device_path_make_major_minor(S_IFBLK, st.st_dev, &bdev);
else {
dev_t devt;
/* Special support for btrfs */
r = btrfs_get_block_device_fd(source_fd, &devt);
if (r < 0)
return log_error_errno(r, "Unable to determine backing block device of '%s': %m", p->copy_blocks_path);
r = device_path_make_major_minor(S_IFBLK, devt, &bdev);
}
if (r < 0)
return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", p->copy_blocks_path);
safe_close(source_fd);
source_fd = open(bdev, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (source_fd < 0)
return log_error_errno(errno, "Failed to open block device '%s': %m", bdev);
if (fstat(source_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
if (!S_ISBLK(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Block device '%s' is not actually a block device, refusing.", bdev);
}
if (S_ISREG(st.st_mode))
size = st.st_size;
else if (S_ISBLK(st.st_mode)) {
if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", p->copy_blocks_path);
if (size <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", p->copy_blocks_path);
if (size % 512 != 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", p->copy_blocks_path);
p->copy_blocks_fd = TAKE_FD(source_fd);
p->copy_blocks_size = size;
}
return 0;
}
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@ -3213,6 +3354,11 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
/* Open all files to copy blocks from now, since we want to take their size into consideration */
r = context_open_copy_block_paths(context);
if (r < 0)
return r;
/* First try to fit new partitions in, dropping by priority until it fits */
for (;;) {
if (context_allocate_partitions(context))

View File

@ -25,7 +25,7 @@ EOF
cat >$D/definitions/root.conf <<EOF
[Partition]
Type=root
Type=root-x86-64
EOF
ln -s root.conf $D/definitions/root2.conf
@ -113,3 +113,34 @@ $D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FB
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
EOF
dd if=/dev/urandom of=$D/block-copy bs=4096 count=10240
cat >$D/definitions/extra2.conf <<EOF
[Partition]
Type=linux-generic
Label=block-copy
UUID=2a1d97e1d0a346cca26eadc643926617
CopyBlocks=$D/block-copy
EOF
$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions
sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated4
cmp $D/populated4 - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
unit: sectors
first-lba: 2048
last-lba: 6291422
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-$UNAME"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
$D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy"
EOF
cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) $D/block-copy $D/zzz

View File

@ -38,7 +38,6 @@ MTUBytes=
Multicast=
MACAddress=
Group=
IPv6LinkLocalAddressGenerationMode=
[BridgeFDB]
VLANId=
MACAddress=
@ -187,6 +186,7 @@ VXLAN=
L2TP=
MACsec=
LinkLocalAddressing=
IPv6LinkLocalAddressGenerationMode=
ConfigureWithoutCarrier=
NTP=
DHCP=

View File

@ -21,18 +21,38 @@ systemctl start testservice-48.target
# May 07 23:12:20 systemd-testsuite testsuite-48.sh[52]: -rw-r--r-- 1 root root 50 2020-05-07 23:12:20.000000000 +0100 /
# May 07 23:12:20 systemd-testsuite testsuite-48.sh[30]: + stat -f --format=%t /etc/systemd/system/testservice-48.servic
# May 07 23:12:20 systemd-testsuite testsuite-48.sh[53]: ef53
sleep 1.1
sleep 3.1
cat > /run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
Type=exec
EOF
systemctl start testservice-48.service
systemctl is-active testservice-48.service
# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/15992
systemctl stop testservice-48.service
rm -f /run/systemd/system/testservice-48.service
systemctl daemon-reload
sleep 3.1
cat > /run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
EOF
# Start a non-existing unit first, so that the cache is reloaded for an unrelated
# reason. Starting the existing unit later should still work thanks to the check
# for the last load attempt vs cache timestamp.
systemctl start testservice-48-nonexistent.service || true
systemctl start testservice-48.service
systemctl is-active testservice-48.service
echo OK > /testok
exit 0