xref: /OK3568_Linux_fs/yocto/poky/scripts/lib/wic/plugins/source/rootfs.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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