xref: /qemu/tests/functional/qemu_test/archive.py (revision 37e9b19c34d9500164e33ccf377e1830e956bca0)
1cfcb4484SDaniel P. Berrangé# SPDX-License-Identifier: GPL-2.0-or-later
2cfcb4484SDaniel P. Berrangé#
3cfcb4484SDaniel P. Berrangé# Utilities for python-based QEMU tests
4cfcb4484SDaniel P. Berrangé#
5cfcb4484SDaniel P. Berrangé# Copyright 2024 Red Hat, Inc.
6cfcb4484SDaniel P. Berrangé#
7cfcb4484SDaniel P. Berrangé# Authors:
8cfcb4484SDaniel P. Berrangé#  Thomas Huth <thuth@redhat.com>
9cfcb4484SDaniel P. Berrangé
10cfcb4484SDaniel P. Berrangéimport os
11c055f1d2SDaniel P. Berrangéfrom subprocess import check_call, run, DEVNULL
12cfcb4484SDaniel P. Berrangéimport tarfile
13c283afbfSDaniel P. Berrangéfrom urllib.parse import urlparse
14379ee839SDaniel P. Berrangéimport zipfile
15cfcb4484SDaniel P. Berrangé
16c283afbfSDaniel P. Berrangéfrom .asset import Asset
17512fe088SDaniel P. Berrangé
18cfcb4484SDaniel P. Berrangé
19cfcb4484SDaniel P. Berrangédef tar_extract(archive, dest_dir, member=None):
20cfcb4484SDaniel P. Berrangé    with tarfile.open(archive) as tf:
21cfcb4484SDaniel P. Berrangé        if hasattr(tarfile, 'data_filter'):
22cfcb4484SDaniel P. Berrangé            tf.extraction_filter = getattr(tarfile, 'data_filter',
23cfcb4484SDaniel P. Berrangé                                           (lambda member, path: member))
24cfcb4484SDaniel P. Berrangé        if member:
25cfcb4484SDaniel P. Berrangé            tf.extract(member=member, path=dest_dir)
26cfcb4484SDaniel P. Berrangé        else:
27cfcb4484SDaniel P. Berrangé            tf.extractall(path=dest_dir)
28cfcb4484SDaniel P. Berrangé
29c055f1d2SDaniel P. Berrangédef cpio_extract(archive, output_path):
30cfcb4484SDaniel P. Berrangé    cwd = os.getcwd()
31cfcb4484SDaniel P. Berrangé    os.chdir(output_path)
32c055f1d2SDaniel P. Berrangé    # Not passing 'check=True' as cpio exits with non-zero
33c055f1d2SDaniel P. Berrangé    # status if the archive contains any device nodes :-(
34c055f1d2SDaniel P. Berrangé    if type(archive) == str:
35c055f1d2SDaniel P. Berrangé        run(['cpio', '-i', '-F', archive],
36c055f1d2SDaniel P. Berrangé            stdout=DEVNULL, stderr=DEVNULL)
37c055f1d2SDaniel P. Berrangé    else:
38c055f1d2SDaniel P. Berrangé        run(['cpio', '-i'],
39c055f1d2SDaniel P. Berrangé            input=archive.read(),
40c055f1d2SDaniel P. Berrangé            stdout=DEVNULL, stderr=DEVNULL)
41cfcb4484SDaniel P. Berrangé    os.chdir(cwd)
42379ee839SDaniel P. Berrangé
43379ee839SDaniel P. Berrangédef zip_extract(archive, dest_dir, member=None):
44379ee839SDaniel P. Berrangé    with zipfile.ZipFile(archive, 'r') as zf:
45379ee839SDaniel P. Berrangé        if member:
46379ee839SDaniel P. Berrangé            zf.extract(member=member, path=dest_dir)
47379ee839SDaniel P. Berrangé        else:
48379ee839SDaniel P. Berrangé            zf.extractall(path=dest_dir)
49512fe088SDaniel P. Berrangé
50512fe088SDaniel P. Berrangédef deb_extract(archive, dest_dir, member=None):
51512fe088SDaniel P. Berrangé    cwd = os.getcwd()
52512fe088SDaniel P. Berrangé    os.chdir(dest_dir)
53512fe088SDaniel P. Berrangé    try:
54*37e9b19cSDaniel P. Berrangé        proc = run(['ar', 't', archive],
55*37e9b19cSDaniel P. Berrangé                   check=True, capture_output=True, encoding='utf8')
56*37e9b19cSDaniel P. Berrangé        file_path = proc.stdout.split()[2]
57*37e9b19cSDaniel P. Berrangé        check_call(['ar', 'x', archive, file_path],
58*37e9b19cSDaniel P. Berrangé                   stdout=DEVNULL, stderr=DEVNULL)
59512fe088SDaniel P. Berrangé        tar_extract(file_path, dest_dir, member)
60512fe088SDaniel P. Berrangé    finally:
61512fe088SDaniel P. Berrangé        os.chdir(cwd)
62c283afbfSDaniel P. Berrangé
63c283afbfSDaniel P. Berrangé'''
64c283afbfSDaniel P. Berrangé@params archive: filename, Asset, or file-like object to extract
65c283afbfSDaniel P. Berrangé@params dest_dir: target directory to extract into
66c283afbfSDaniel P. Berrangé@params member: optional member file to limit extraction to
67c283afbfSDaniel P. Berrangé
68c283afbfSDaniel P. BerrangéExtracts @archive into @dest_dir. All files are extracted
69c283afbfSDaniel P. Berrangéunless @member specifies a limit.
70c283afbfSDaniel P. Berrangé
71c283afbfSDaniel P. BerrangéIf @format is None, heuristics will be applied to guess the format
72c283afbfSDaniel P. Berrangéfrom the filename or Asset URL. @format must be non-None if @archive
73c283afbfSDaniel P. Berrangéis a file-like object.
74c283afbfSDaniel P. Berrangé'''
75c283afbfSDaniel P. Berrangédef archive_extract(archive, dest_dir, format=None, member=None):
76c283afbfSDaniel P. Berrangé    if format is None:
77c283afbfSDaniel P. Berrangé        format = guess_archive_format(archive)
78c283afbfSDaniel P. Berrangé    if type(archive) == Asset:
79c283afbfSDaniel P. Berrangé        archive = str(archive)
80c283afbfSDaniel P. Berrangé
81c283afbfSDaniel P. Berrangé    if format == "tar":
82c283afbfSDaniel P. Berrangé        tar_extract(archive, dest_dir, member)
83c283afbfSDaniel P. Berrangé    elif format == "zip":
84c283afbfSDaniel P. Berrangé        zip_extract(archive, dest_dir, member)
85c283afbfSDaniel P. Berrangé    elif format == "cpio":
86c283afbfSDaniel P. Berrangé        if member is not None:
87c283afbfSDaniel P. Berrangé            raise Exception("Unable to filter cpio extraction")
88c283afbfSDaniel P. Berrangé        cpio_extract(archive, dest_dir)
89c283afbfSDaniel P. Berrangé    elif format == "deb":
90c283afbfSDaniel P. Berrangé        if type(archive) != str:
91c283afbfSDaniel P. Berrangé            raise Exception("Unable to use file-like object with deb archives")
92c283afbfSDaniel P. Berrangé        deb_extract(archive, dest_dir, "./" + member)
93c283afbfSDaniel P. Berrangé    else:
94c283afbfSDaniel P. Berrangé        raise Exception(f"Unknown archive format {format}")
95c283afbfSDaniel P. Berrangé
96c283afbfSDaniel P. Berrangé'''
97c283afbfSDaniel P. Berrangé@params archive: filename, or Asset to guess
98c283afbfSDaniel P. Berrangé
99c283afbfSDaniel P. BerrangéGuess the format of @compressed, raising an exception if
100c283afbfSDaniel P. Berrangéno format can be determined
101c283afbfSDaniel P. Berrangé'''
102c283afbfSDaniel P. Berrangédef guess_archive_format(archive):
103c283afbfSDaniel P. Berrangé    if type(archive) == Asset:
104c283afbfSDaniel P. Berrangé        archive = urlparse(archive.url).path
105c283afbfSDaniel P. Berrangé    elif type(archive) != str:
106c283afbfSDaniel P. Berrangé        raise Exception(f"Unable to guess archive format for {archive}")
107c283afbfSDaniel P. Berrangé
108c283afbfSDaniel P. Berrangé    if ".tar." in archive or archive.endswith("tgz"):
109c283afbfSDaniel P. Berrangé        return "tar"
110c283afbfSDaniel P. Berrangé    elif archive.endswith(".zip"):
111c283afbfSDaniel P. Berrangé        return "zip"
112c283afbfSDaniel P. Berrangé    elif archive.endswith(".cpio"):
113c283afbfSDaniel P. Berrangé        return "cpio"
114c283afbfSDaniel P. Berrangé    elif archive.endswith(".deb") or archive.endswith(".udeb"):
115c283afbfSDaniel P. Berrangé        return "deb"
116c283afbfSDaniel P. Berrangé    else:
117c283afbfSDaniel P. Berrangé        raise Exception(f"Unknown archive format for {archive}")
118