1# SPDX-License-Identifier: GPL-2.0
2# Copyright (C) 2020 Bootlin
3# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
4
5import os
6import shutil
7import subprocess
8
9""" standard test images table: Each table item is a key:value pair
10representing the output image name and its respective mksquashfs options.
11This table should be modified only when adding support for new compression
12algorithms. The 'default' case takes no options but the input and output
13names, so it must be assigned with an empty string.
14"""
15STANDARD_TABLE = {
16        'default' : '',
17        'lzo_comp_frag' : '',
18        'lzo_frag' : '',
19        'lzo_no_frag' : '',
20        'zstd_comp_frag' : '',
21        'zstd_frag' : '',
22        'zstd_no_frag' : '',
23        'gzip_comp_frag' : '',
24        'gzip_frag' : '',
25        'gzip_no_frag' : ''
26}
27
28""" EXTRA_TABLE: Set this table's keys and values if you want to make squashfs
29images with your own customized options.
30"""
31EXTRA_TABLE = {}
32
33# path to source directory used to make squashfs test images
34SQFS_SRC_DIR = 'sqfs_src_dir'
35
36def get_opts_list():
37    """ Combines fragmentation and compression options into a list of strings.
38
39    opts_list's firts item is an empty string as STANDARD_TABLE's first item is
40    the 'default' case.
41
42    Returns:
43        A list of strings whose items are formed by a compression and a
44        fragmentation option joined by a whitespace.
45    """
46    # supported compression options only
47    comp_opts = ['-comp lzo', '-comp zstd', '-comp gzip']
48    # file fragmentation options
49    frag_opts = ['-always-use-fragments', '-always-use-fragments -noF', '-no-fragments']
50
51    opts_list = [' ']
52    for comp_opt in comp_opts:
53        for frag_opt in frag_opts:
54            opts_list.append(' '.join([comp_opt, frag_opt]))
55
56    return opts_list
57
58def init_standard_table():
59    """ Initializes STANDARD_TABLE values.
60
61    STANDARD_TABLE's keys are pre-defined, and init_standard_table() assigns
62    the right value for each one of them.
63    """
64    opts_list = get_opts_list()
65
66    for key, value in zip(STANDARD_TABLE.keys(), opts_list):
67        STANDARD_TABLE[key] = value
68
69def generate_file(file_name, file_size):
70    """ Generates a file filled with 'x'.
71
72    Args:
73        file_name: the file's name.
74        file_size: the content's length and therefore the file size.
75    """
76    content = 'x' * file_size
77
78    file = open(file_name, 'w')
79    file.write(content)
80    file.close()
81
82def generate_sqfs_src_dir(build_dir):
83    """ Generates the source directory used to make the SquashFS images.
84
85    The source directory is generated at build_dir, and it has the following
86    structure:
87    sqfs_src_dir/
88    ├── empty-dir/
89    ├── f1000
90    ├── f4096
91    ├── f5096
92    ├── subdir/
93    │   └── subdir-file
94    └── sym -> subdir
95
96    3 directories, 4 files
97
98    The files in the root dir. are prefixed with an 'f' followed by its size.
99
100    Args:
101        build_dir: u-boot's build-sandbox directory.
102    """
103
104    root = os.path.join(build_dir, SQFS_SRC_DIR)
105    # make root directory
106    os.makedirs(root)
107
108    # 4096: minimum block size
109    file_name = 'f4096'
110    generate_file(os.path.join(root, file_name), 4096)
111
112    # 5096: minimum block size + 1000 chars (fragment)
113    file_name = 'f5096'
114    generate_file(os.path.join(root, file_name), 5096)
115
116    # 1000: less than minimum block size (fragment only)
117    file_name = 'f1000'
118    generate_file(os.path.join(root, file_name), 1000)
119
120    # sub-directory with a single file inside
121    subdir_path = os.path.join(root, 'subdir')
122    os.makedirs(subdir_path)
123    generate_file(os.path.join(subdir_path, 'subdir-file'), 100)
124
125    # symlink (target: sub-directory)
126    os.symlink('subdir', os.path.join(root, 'sym'))
127
128    # empty directory
129    os.makedirs(os.path.join(root, 'empty-dir'))
130
131def mksquashfs(args):
132    """ Runs mksquashfs command.
133
134    Args:
135        args: mksquashfs options (e.g.: compression and fragmentation).
136    """
137    subprocess.run(['mksquashfs ' + args], shell=True, check=True,
138                   stdout=subprocess.DEVNULL)
139
140def get_mksquashfs_version():
141    """ Parses the output of mksquashfs -version.
142
143    Returns:
144        mksquashfs's version as a float.
145    """
146    out = subprocess.run(['mksquashfs -version'], shell=True, check=True,
147                         capture_output=True, text=True)
148    # 'out' is: mksquashfs version X (yyyy/mm/dd) ...
149    return out.stdout.split()[2].split('.')[0:2]
150
151def check_mksquashfs_version():
152    """ Checks if mksquashfs meets the required version. """
153
154    version = get_mksquashfs_version();
155    if int(version[0]) < 4 or int(version[0]) == 4 and int(version[1]) < 4 :
156        print('Error: mksquashfs is too old, required version: 4.4')
157        raise AssertionError
158
159def make_all_images(build_dir):
160    """ Makes the SquashFS images used in the test suite.
161
162    The image names and respective mksquashfs options are defined in STANDARD_TABLE
163    and EXTRA_TABLE. The destination is defined by 'build_dir'.
164
165    Args:
166        build_dir: u-boot's build-sandbox directory.
167    """
168
169    init_standard_table()
170    input_path = os.path.join(build_dir, SQFS_SRC_DIR)
171
172    # make squashfs images according to STANDARD_TABLE
173    for out, opts in zip(STANDARD_TABLE.keys(), STANDARD_TABLE.values()):
174        output_path = os.path.join(build_dir, out)
175        mksquashfs(' '.join([input_path, output_path, opts]))
176
177    # make squashfs images according to EXTRA_TABLE
178    for out, opts in zip(EXTRA_TABLE.keys(), EXTRA_TABLE.values()):
179        output_path = os.path.join(build_dir, out)
180        mksquashfs(' '.join([input_path, output_path, opts]))
181
182def clean_all_images(build_dir):
183    """ Deletes the SquashFS images at build_dir.
184
185    Args:
186        build_dir: u-boot's build-sandbox directory.
187    """
188
189    for image_name in STANDARD_TABLE:
190        image_path = os.path.join(build_dir, image_name)
191        os.remove(image_path)
192
193    for image_name in EXTRA_TABLE:
194        image_path = os.path.join(build_dir, image_name)
195        os.remove(image_path)
196
197def clean_sqfs_src_dir(build_dir):
198    """ Deletes the source directory at build_dir.
199
200    Args:
201        build_dir: u-boot's build-sandbox directory.
202    """
203    path = os.path.join(build_dir, SQFS_SRC_DIR)
204    shutil.rmtree(path)
205