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