diff options
-rw-r--r-- | meta/classes/rootfs-postcommands.bbclass | 22 | ||||
-rw-r--r-- | meta/lib/rootfspostcommands.py | 44 |
2 files changed, 66 insertions, 0 deletions
diff --git a/meta/classes/rootfs-postcommands.bbclass b/meta/classes/rootfs-postcommands.bbclass index 8d48a2d1d9..53a4fda4b1 100644 --- a/meta/classes/rootfs-postcommands.bbclass +++ b/meta/classes/rootfs-postcommands.bbclass @@ -30,6 +30,23 @@ ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;' SSH_DISABLE_DNS_LOOKUP ?= " ssh_disable_dns_lookup ; " ROOTFS_POSTPROCESS_COMMAND_append_qemuall = "${SSH_DISABLE_DNS_LOOKUP}" +# Sort the user and group entries in /etc by ID in order to make the content +# deterministic. Package installs are not deterministic, causing the ordering +# of entries to change between builds. In case that this isn't desired, +# the command can be overridden. +# +# Note that useradd-staticids.bbclass has to be used to ensure that +# the numeric IDs of dynamically created entries remain stable. +# +# We want this to run as late as possible, in particular after +# systemd_sysusers_create and set_user_group. Using _append is not +# enough for that, set_user_group is added that way and would end +# up running after us. +SORT_PASSWD_POSTPROCESS_COMMAND ??= " sort_passwd; " +python () { + d.appendVar('ROOTFS_POSTPROCESS_COMMAND', '${SORT_PASSWD_POSTPROCESS_COMMAND}') +} + systemd_create_users () { for conffile in ${IMAGE_ROOTFS}/usr/lib/sysusers.d/systemd.conf ${IMAGE_ROOTFS}/usr/lib/sysusers.d/systemd-remote.conf; do [ -e $conffile ] || continue @@ -146,6 +163,11 @@ ssh_disable_dns_lookup () { fi } +python sort_passwd () { + import rootfspostcommands + rootfspostcommands.sort_passwd(d.expand('${IMAGE_ROOTFS}${sysconfdir}')) +} + # # Enable postinst logging if debug-tweaks is enabled # diff --git a/meta/lib/rootfspostcommands.py b/meta/lib/rootfspostcommands.py new file mode 100644 index 0000000000..6a9b8b47b7 --- /dev/null +++ b/meta/lib/rootfspostcommands.py @@ -0,0 +1,44 @@ +import os + +def sort_file(filename, mapping): + """ + Sorts a passwd or group file based on the numeric ID in the third column. + If a mapping is given, the name from the first column is mapped via that + dictionary instead (necessary for /etc/shadow and /etc/gshadow). If not, + a new mapping is created on the fly and returned. + """ + new_mapping = {} + with open(filename, 'rb+') as f: + lines = f.readlines() + # No explicit error checking for the sake of simplicity. /etc + # files are assumed to be well-formed, causing exceptions if + # not. + for line in lines: + entries = line.split(b':') + name = entries[0] + if mapping is None: + id = int(entries[2]) + else: + id = mapping[name] + new_mapping[name] = id + # Sort by numeric id first, with entire line as secondary key + # (just in case that there is more than one entry for the same id). + lines.sort(key=lambda line: (new_mapping[line.split(b':')[0]], line)) + # We overwrite the entire file, i.e. no truncate() necessary. + f.seek(0) + f.write(b''.join(lines)) + return new_mapping + +def sort_passwd(sysconfdir): + """ + Sorts passwd and group files in a rootfs /etc directory by ID. + """ + for suffix in '', '-': + for main, shadow in (('passwd', 'shadow'), + ('group', 'gshadow')): + filename = os.path.join(sysconfdir, main + suffix) + if os.path.exists(filename): + mapping = sort_file(filename, None) + filename = os.path.join(sysconfdir, shadow + suffix) + if os.path.exists(filename): + sort_file(filename, mapping) |