1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright (c) 2014, Intel Corporation. 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# DESCRIPTION 7*4882a593Smuzhiyun# This implements the 'rootfs' source plugin class for 'wic' 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun# AUTHORS 10*4882a593Smuzhiyun# Tom Zanussi <tom.zanussi (at] linux.intel.com> 11*4882a593Smuzhiyun# Joao Henrique Ferreira de Freitas <joaohf (at] gmail.com> 12*4882a593Smuzhiyun# 13*4882a593Smuzhiyun 14*4882a593Smuzhiyunimport logging 15*4882a593Smuzhiyunimport os 16*4882a593Smuzhiyunimport shutil 17*4882a593Smuzhiyunimport sys 18*4882a593Smuzhiyun 19*4882a593Smuzhiyunfrom oe.path import copyhardlinktree 20*4882a593Smuzhiyunfrom pathlib import Path 21*4882a593Smuzhiyun 22*4882a593Smuzhiyunfrom wic import WicError 23*4882a593Smuzhiyunfrom wic.pluginbase import SourcePlugin 24*4882a593Smuzhiyunfrom wic.misc import get_bitbake_var, exec_native_cmd 25*4882a593Smuzhiyun 26*4882a593Smuzhiyunlogger = logging.getLogger('wic') 27*4882a593Smuzhiyun 28*4882a593Smuzhiyunclass RootfsPlugin(SourcePlugin): 29*4882a593Smuzhiyun """ 30*4882a593Smuzhiyun Populate partition content from a rootfs directory. 31*4882a593Smuzhiyun """ 32*4882a593Smuzhiyun 33*4882a593Smuzhiyun name = 'rootfs' 34*4882a593Smuzhiyun 35*4882a593Smuzhiyun @staticmethod 36*4882a593Smuzhiyun def __validate_path(cmd, rootfs_dir, path): 37*4882a593Smuzhiyun if os.path.isabs(path): 38*4882a593Smuzhiyun logger.error("%s: Must be relative: %s" % (cmd, path)) 39*4882a593Smuzhiyun sys.exit(1) 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun # Disallow climbing outside of parent directory using '..', 42*4882a593Smuzhiyun # because doing so could be quite disastrous (we will delete the 43*4882a593Smuzhiyun # directory, or modify a directory outside OpenEmbedded). 44*4882a593Smuzhiyun full_path = os.path.realpath(os.path.join(rootfs_dir, path)) 45*4882a593Smuzhiyun if not full_path.startswith(os.path.realpath(rootfs_dir)): 46*4882a593Smuzhiyun logger.error("%s: Must point inside the rootfs:" % (cmd, path)) 47*4882a593Smuzhiyun sys.exit(1) 48*4882a593Smuzhiyun 49*4882a593Smuzhiyun return full_path 50*4882a593Smuzhiyun 51*4882a593Smuzhiyun @staticmethod 52*4882a593Smuzhiyun def __get_rootfs_dir(rootfs_dir): 53*4882a593Smuzhiyun if rootfs_dir and os.path.isdir(rootfs_dir): 54*4882a593Smuzhiyun return os.path.realpath(rootfs_dir) 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir) 57*4882a593Smuzhiyun if not os.path.isdir(image_rootfs_dir): 58*4882a593Smuzhiyun raise WicError("No valid artifact IMAGE_ROOTFS from image " 59*4882a593Smuzhiyun "named %s has been found at %s, exiting." % 60*4882a593Smuzhiyun (rootfs_dir, image_rootfs_dir)) 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun return os.path.realpath(image_rootfs_dir) 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun @staticmethod 65*4882a593Smuzhiyun def __get_pseudo(native_sysroot, rootfs, pseudo_dir): 66*4882a593Smuzhiyun pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot 67*4882a593Smuzhiyun pseudo += "export PSEUDO_LOCALSTATEDIR=%s;" % pseudo_dir 68*4882a593Smuzhiyun pseudo += "export PSEUDO_PASSWD=%s;" % rootfs 69*4882a593Smuzhiyun pseudo += "export PSEUDO_NOSYMLINKEXP=1;" 70*4882a593Smuzhiyun pseudo += "%s " % get_bitbake_var("FAKEROOTCMD") 71*4882a593Smuzhiyun return pseudo 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun @classmethod 74*4882a593Smuzhiyun def do_prepare_partition(cls, part, source_params, cr, cr_workdir, 75*4882a593Smuzhiyun oe_builddir, bootimg_dir, kernel_dir, 76*4882a593Smuzhiyun krootfs_dir, native_sysroot): 77*4882a593Smuzhiyun """ 78*4882a593Smuzhiyun Called to do the actual content population for a partition i.e. it 79*4882a593Smuzhiyun 'prepares' the partition to be incorporated into the image. 80*4882a593Smuzhiyun In this case, prepare content for legacy bios boot partition. 81*4882a593Smuzhiyun """ 82*4882a593Smuzhiyun if part.rootfs_dir is None: 83*4882a593Smuzhiyun if not 'ROOTFS_DIR' in krootfs_dir: 84*4882a593Smuzhiyun raise WicError("Couldn't find --rootfs-dir, exiting") 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun rootfs_dir = krootfs_dir['ROOTFS_DIR'] 87*4882a593Smuzhiyun else: 88*4882a593Smuzhiyun if part.rootfs_dir in krootfs_dir: 89*4882a593Smuzhiyun rootfs_dir = krootfs_dir[part.rootfs_dir] 90*4882a593Smuzhiyun elif part.rootfs_dir: 91*4882a593Smuzhiyun rootfs_dir = part.rootfs_dir 92*4882a593Smuzhiyun else: 93*4882a593Smuzhiyun raise WicError("Couldn't find --rootfs-dir=%s connection or " 94*4882a593Smuzhiyun "it is not a valid path, exiting" % part.rootfs_dir) 95*4882a593Smuzhiyun 96*4882a593Smuzhiyun part.rootfs_dir = cls.__get_rootfs_dir(rootfs_dir) 97*4882a593Smuzhiyun part.has_fstab = os.path.exists(os.path.join(part.rootfs_dir, "etc/fstab")) 98*4882a593Smuzhiyun pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo") 99*4882a593Smuzhiyun if not os.path.lexists(pseudo_dir): 100*4882a593Smuzhiyun pseudo_dir = os.path.join(cls.__get_rootfs_dir(None), '../pseudo') 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun if not os.path.lexists(pseudo_dir): 103*4882a593Smuzhiyun logger.warn("%s folder does not exist. " 104*4882a593Smuzhiyun "Usernames and permissions will be invalid " % pseudo_dir) 105*4882a593Smuzhiyun pseudo_dir = None 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun new_rootfs = None 108*4882a593Smuzhiyun new_pseudo = None 109*4882a593Smuzhiyun # Handle excluded paths. 110*4882a593Smuzhiyun if part.exclude_path or part.include_path or part.change_directory or part.update_fstab_in_rootfs: 111*4882a593Smuzhiyun # We need a new rootfs directory we can safely modify without 112*4882a593Smuzhiyun # interfering with other tasks. Copy to workdir. 113*4882a593Smuzhiyun new_rootfs = os.path.realpath(os.path.join(cr_workdir, "rootfs%d" % part.lineno)) 114*4882a593Smuzhiyun 115*4882a593Smuzhiyun if os.path.lexists(new_rootfs): 116*4882a593Smuzhiyun shutil.rmtree(os.path.join(new_rootfs)) 117*4882a593Smuzhiyun 118*4882a593Smuzhiyun if part.change_directory: 119*4882a593Smuzhiyun cd = part.change_directory 120*4882a593Smuzhiyun if cd[-1] == '/': 121*4882a593Smuzhiyun cd = cd[:-1] 122*4882a593Smuzhiyun orig_dir = cls.__validate_path("--change-directory", part.rootfs_dir, cd) 123*4882a593Smuzhiyun else: 124*4882a593Smuzhiyun orig_dir = part.rootfs_dir 125*4882a593Smuzhiyun copyhardlinktree(orig_dir, new_rootfs) 126*4882a593Smuzhiyun 127*4882a593Smuzhiyun # Convert the pseudo directory to its new location 128*4882a593Smuzhiyun if (pseudo_dir): 129*4882a593Smuzhiyun new_pseudo = os.path.realpath( 130*4882a593Smuzhiyun os.path.join(cr_workdir, "pseudo%d" % part.lineno)) 131*4882a593Smuzhiyun if os.path.lexists(new_pseudo): 132*4882a593Smuzhiyun shutil.rmtree(new_pseudo) 133*4882a593Smuzhiyun os.mkdir(new_pseudo) 134*4882a593Smuzhiyun shutil.copy(os.path.join(pseudo_dir, "files.db"), 135*4882a593Smuzhiyun os.path.join(new_pseudo, "files.db")) 136*4882a593Smuzhiyun 137*4882a593Smuzhiyun pseudo_cmd = "%s -B -m %s -M %s" % (cls.__get_pseudo(native_sysroot, 138*4882a593Smuzhiyun new_rootfs, 139*4882a593Smuzhiyun new_pseudo), 140*4882a593Smuzhiyun orig_dir, new_rootfs) 141*4882a593Smuzhiyun exec_native_cmd(pseudo_cmd, native_sysroot) 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun for in_path in part.include_path or []: 144*4882a593Smuzhiyun #parse arguments 145*4882a593Smuzhiyun include_path = in_path[0] 146*4882a593Smuzhiyun if len(in_path) > 2: 147*4882a593Smuzhiyun logger.error("'Invalid number of arguments for include-path") 148*4882a593Smuzhiyun sys.exit(1) 149*4882a593Smuzhiyun if len(in_path) == 2: 150*4882a593Smuzhiyun path = in_path[1] 151*4882a593Smuzhiyun else: 152*4882a593Smuzhiyun path = None 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun # Pack files to be included into a tar file. 155*4882a593Smuzhiyun # We need to create a tar file, because that way we can keep the 156*4882a593Smuzhiyun # permissions from the files even when they belong to different 157*4882a593Smuzhiyun # pseudo enviroments. 158*4882a593Smuzhiyun # If we simply copy files using copyhardlinktree/copytree... the 159*4882a593Smuzhiyun # copied files will belong to the user running wic. 160*4882a593Smuzhiyun tar_file = os.path.realpath( 161*4882a593Smuzhiyun os.path.join(cr_workdir, "include-path%d.tar" % part.lineno)) 162*4882a593Smuzhiyun if os.path.isfile(include_path): 163*4882a593Smuzhiyun parent = os.path.dirname(os.path.realpath(include_path)) 164*4882a593Smuzhiyun tar_cmd = "tar c --owner=root --group=root -f %s -C %s %s" % ( 165*4882a593Smuzhiyun tar_file, parent, os.path.relpath(include_path, parent)) 166*4882a593Smuzhiyun exec_native_cmd(tar_cmd, native_sysroot) 167*4882a593Smuzhiyun else: 168*4882a593Smuzhiyun if include_path in krootfs_dir: 169*4882a593Smuzhiyun include_path = krootfs_dir[include_path] 170*4882a593Smuzhiyun include_path = cls.__get_rootfs_dir(include_path) 171*4882a593Smuzhiyun include_pseudo = os.path.join(include_path, "../pseudo") 172*4882a593Smuzhiyun if os.path.lexists(include_pseudo): 173*4882a593Smuzhiyun pseudo = cls.__get_pseudo(native_sysroot, include_path, 174*4882a593Smuzhiyun include_pseudo) 175*4882a593Smuzhiyun tar_cmd = "tar cf %s -C %s ." % (tar_file, include_path) 176*4882a593Smuzhiyun else: 177*4882a593Smuzhiyun pseudo = None 178*4882a593Smuzhiyun tar_cmd = "tar c --owner=root --group=root -f %s -C %s ." % ( 179*4882a593Smuzhiyun tar_file, include_path) 180*4882a593Smuzhiyun exec_native_cmd(tar_cmd, native_sysroot, pseudo) 181*4882a593Smuzhiyun 182*4882a593Smuzhiyun #create destination 183*4882a593Smuzhiyun if path: 184*4882a593Smuzhiyun destination = cls.__validate_path("--include-path", new_rootfs, path) 185*4882a593Smuzhiyun Path(destination).mkdir(parents=True, exist_ok=True) 186*4882a593Smuzhiyun else: 187*4882a593Smuzhiyun destination = new_rootfs 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun #extract destination 190*4882a593Smuzhiyun untar_cmd = "tar xf %s -C %s" % (tar_file, destination) 191*4882a593Smuzhiyun if new_pseudo: 192*4882a593Smuzhiyun pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) 193*4882a593Smuzhiyun else: 194*4882a593Smuzhiyun pseudo = None 195*4882a593Smuzhiyun exec_native_cmd(untar_cmd, native_sysroot, pseudo) 196*4882a593Smuzhiyun os.remove(tar_file) 197*4882a593Smuzhiyun 198*4882a593Smuzhiyun for orig_path in part.exclude_path or []: 199*4882a593Smuzhiyun path = orig_path 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun full_path = cls.__validate_path("--exclude-path", new_rootfs, path) 202*4882a593Smuzhiyun 203*4882a593Smuzhiyun if not os.path.lexists(full_path): 204*4882a593Smuzhiyun continue 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun if new_pseudo: 207*4882a593Smuzhiyun pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) 208*4882a593Smuzhiyun else: 209*4882a593Smuzhiyun pseudo = None 210*4882a593Smuzhiyun if path.endswith(os.sep): 211*4882a593Smuzhiyun # Delete content only. 212*4882a593Smuzhiyun for entry in os.listdir(full_path): 213*4882a593Smuzhiyun full_entry = os.path.join(full_path, entry) 214*4882a593Smuzhiyun rm_cmd = "rm -rf %s" % (full_entry) 215*4882a593Smuzhiyun exec_native_cmd(rm_cmd, native_sysroot, pseudo) 216*4882a593Smuzhiyun else: 217*4882a593Smuzhiyun # Delete whole directory. 218*4882a593Smuzhiyun rm_cmd = "rm -rf %s" % (full_path) 219*4882a593Smuzhiyun exec_native_cmd(rm_cmd, native_sysroot, pseudo) 220*4882a593Smuzhiyun 221*4882a593Smuzhiyun # Update part.has_fstab here as fstab may have been added or 222*4882a593Smuzhiyun # removed by the above modifications. 223*4882a593Smuzhiyun part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab")) 224*4882a593Smuzhiyun if part.update_fstab_in_rootfs and part.has_fstab and not part.no_fstab_update: 225*4882a593Smuzhiyun fstab_path = os.path.join(new_rootfs, "etc/fstab") 226*4882a593Smuzhiyun # Assume that fstab should always be owned by root with fixed permissions 227*4882a593Smuzhiyun install_cmd = "install -m 0644 -p %s %s" % (part.updated_fstab_path, fstab_path) 228*4882a593Smuzhiyun if new_pseudo: 229*4882a593Smuzhiyun pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) 230*4882a593Smuzhiyun else: 231*4882a593Smuzhiyun pseudo = None 232*4882a593Smuzhiyun exec_native_cmd(install_cmd, native_sysroot, pseudo) 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun part.prepare_rootfs(cr_workdir, oe_builddir, 235*4882a593Smuzhiyun new_rootfs or part.rootfs_dir, native_sysroot, 236*4882a593Smuzhiyun pseudo_dir = new_pseudo or pseudo_dir) 237