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