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