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 remove_backup(filename): """ Removes the backup file for files like /etc/passwd. """ backup_filename = filename + '-' if os.path.exists(backup_filename): os.unlink(backup_filename) def sort_passwd(sysconfdir): """ Sorts passwd and group files in a rootfs /etc directory by ID. Backup files are sometimes are inconsistent and then cannot be sorted (YOCTO #11043), and more importantly, are not needed in the initial rootfs, so they get deleted. """ for main, shadow in (('passwd', 'shadow'), ('group', 'gshadow')): filename = os.path.join(sysconfdir, main) remove_backup(filename) if os.path.exists(filename): mapping = sort_file(filename, None) filename = os.path.join(sysconfdir, shadow) remove_backup(filename) if os.path.exists(filename): sort_file(filename, mapping)