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, orig_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 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 pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo") 98 if not os.path.lexists(pseudo_dir): 99 logger.warn("%s folder does not exist. " 100 "Usernames and permissions will be invalid " % pseudo_dir) 101 pseudo_dir = None 102 103 new_rootfs = None 104 new_pseudo = None 105 # Handle excluded paths. 106 if part.exclude_path or part.include_path or part.change_directory: 107 # We need a new rootfs directory we can delete files from. Copy to 108 # workdir. 109 new_rootfs = os.path.realpath(os.path.join(cr_workdir, "rootfs%d" % part.lineno)) 110 111 if os.path.lexists(new_rootfs): 112 shutil.rmtree(os.path.join(new_rootfs)) 113 114 if part.change_directory: 115 cd = part.change_directory 116 if cd[-1] == '/': 117 cd = cd[:-1] 118 orig_dir = cls.__validate_path("--change-directory", part.rootfs_dir, cd) 119 else: 120 orig_dir = part.rootfs_dir 121 copyhardlinktree(orig_dir, new_rootfs) 122 123 # Convert the pseudo directory to its new location 124 if (pseudo_dir): 125 new_pseudo = os.path.realpath( 126 os.path.join(cr_workdir, "pseudo%d" % part.lineno)) 127 if os.path.lexists(new_pseudo): 128 shutil.rmtree(new_pseudo) 129 os.mkdir(new_pseudo) 130 shutil.copy(os.path.join(pseudo_dir, "files.db"), 131 os.path.join(new_pseudo, "files.db")) 132 133 pseudo_cmd = "%s -B -m %s -M %s" % (cls.__get_pseudo(native_sysroot, 134 new_rootfs, 135 new_pseudo), 136 orig_dir, new_rootfs) 137 exec_native_cmd(pseudo_cmd, native_sysroot) 138 139 for in_path in part.include_path or []: 140 #parse arguments 141 include_path = in_path[0] 142 if len(in_path) > 2: 143 logger.error("'Invalid number of arguments for include-path") 144 sys.exit(1) 145 if len(in_path) == 2: 146 path = in_path[1] 147 else: 148 path = None 149 150 # Pack files to be included into a tar file. 151 # We need to create a tar file, because that way we can keep the 152 # permissions from the files even when they belong to different 153 # pseudo enviroments. 154 # If we simply copy files using copyhardlinktree/copytree... the 155 # copied files will belong to the user running wic. 156 tar_file = os.path.realpath( 157 os.path.join(cr_workdir, "include-path%d.tar" % part.lineno)) 158 if os.path.isfile(include_path): 159 parent = os.path.dirname(os.path.realpath(include_path)) 160 tar_cmd = "tar c --owner=root --group=root -f %s -C %s %s" % ( 161 tar_file, parent, os.path.relpath(include_path, parent)) 162 exec_native_cmd(tar_cmd, native_sysroot) 163 else: 164 if include_path in krootfs_dir: 165 include_path = krootfs_dir[include_path] 166 include_path = cls.__get_rootfs_dir(include_path) 167 include_pseudo = os.path.join(include_path, "../pseudo") 168 if os.path.lexists(include_pseudo): 169 pseudo = cls.__get_pseudo(native_sysroot, include_path, 170 include_pseudo) 171 tar_cmd = "tar cf %s -C %s ." % (tar_file, include_path) 172 else: 173 pseudo = None 174 tar_cmd = "tar c --owner=root --group=root -f %s -C %s ." % ( 175 tar_file, include_path) 176 exec_native_cmd(tar_cmd, native_sysroot, pseudo) 177 178 #create destination 179 if path: 180 destination = cls.__validate_path("--include-path", new_rootfs, path) 181 Path(destination).mkdir(parents=True, exist_ok=True) 182 else: 183 destination = new_rootfs 184 185 #extract destination 186 untar_cmd = "tar xf %s -C %s" % (tar_file, destination) 187 if new_pseudo: 188 pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) 189 else: 190 pseudo = None 191 exec_native_cmd(untar_cmd, native_sysroot, pseudo) 192 os.remove(tar_file) 193 194 for orig_path in part.exclude_path or []: 195 path = orig_path 196 197 full_path = cls.__validate_path("--exclude-path", new_rootfs, path) 198 199 if not os.path.lexists(full_path): 200 continue 201 202 if path.endswith(os.sep): 203 # Delete content only. 204 for entry in os.listdir(full_path): 205 full_entry = os.path.join(full_path, entry) 206 if os.path.isdir(full_entry) and not os.path.islink(full_entry): 207 shutil.rmtree(full_entry) 208 else: 209 os.remove(full_entry) 210 else: 211 # Delete whole directory. 212 shutil.rmtree(full_path) 213 214 part.prepare_rootfs(cr_workdir, oe_builddir, 215 new_rootfs or part.rootfs_dir, native_sysroot, 216 pseudo_dir = new_pseudo or pseudo_dir) 217