1# 2# SPDX-License-Identifier: GPL-2.0-only 3# 4 5import os 6 7def sort_file(filename, mapping): 8 """ 9 Sorts a passwd or group file based on the numeric ID in the third column. 10 If a mapping is given, the name from the first column is mapped via that 11 dictionary instead (necessary for /etc/shadow and /etc/gshadow). If not, 12 a new mapping is created on the fly and returned. 13 """ 14 new_mapping = {} 15 with open(filename, 'rb+') as f: 16 lines = f.readlines() 17 # No explicit error checking for the sake of simplicity. /etc 18 # files are assumed to be well-formed, causing exceptions if 19 # not. 20 for line in lines: 21 entries = line.split(b':') 22 name = entries[0] 23 if mapping is None: 24 id = int(entries[2]) 25 else: 26 id = mapping[name] 27 new_mapping[name] = id 28 # Sort by numeric id first, with entire line as secondary key 29 # (just in case that there is more than one entry for the same id). 30 lines.sort(key=lambda line: (new_mapping[line.split(b':')[0]], line)) 31 # We overwrite the entire file, i.e. no truncate() necessary. 32 f.seek(0) 33 f.write(b''.join(lines)) 34 return new_mapping 35 36def remove_backup(filename): 37 """ 38 Removes the backup file for files like /etc/passwd. 39 """ 40 backup_filename = filename + '-' 41 if os.path.exists(backup_filename): 42 os.unlink(backup_filename) 43 44def sort_passwd(sysconfdir): 45 """ 46 Sorts passwd and group files in a rootfs /etc directory by ID. 47 Backup files are sometimes are inconsistent and then cannot be 48 sorted (YOCTO #11043), and more importantly, are not needed in 49 the initial rootfs, so they get deleted. 50 """ 51 for main, shadow in (('passwd', 'shadow'), 52 ('group', 'gshadow')): 53 filename = os.path.join(sysconfdir, main) 54 remove_backup(filename) 55 if os.path.exists(filename): 56 mapping = sort_file(filename, None) 57 filename = os.path.join(sysconfdir, shadow) 58 remove_backup(filename) 59 if os.path.exists(filename): 60 sort_file(filename, mapping) 61 # Drop other known backup shadow-utils. 62 for filename in ( 63 'subgid', 64 'subuid', 65 ): 66 filepath = os.path.join(sysconfdir, filename) 67 remove_backup(filepath) 68