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