From 5c4426847602ab59475403901cf6f3ea3a3e6270 Mon Sep 17 00:00:00 2001 From: Bernhard Ehlers Date: Tue, 7 Apr 2020 14:11:00 +0200 Subject: [PATCH] QEMU config disk - preserve file timestamp on zip unpack --- gns3server/compute/qemu/qemu_vm.py | 9 ++-- gns3server/compute/qemu/utils/ziputils.py | 53 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 gns3server/compute/qemu/utils/ziputils.py diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index dd72adf0..8e515eba 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -37,6 +37,7 @@ from gns3server.utils import parse_version, shlex_quote from gns3server.utils.asyncio import subprocess_check_output, cancellable_wait_run_in_executor from .qemu_error import QemuError from .utils.qcow2 import Qcow2, Qcow2Error +from .utils.ziputils import pack_zip, unpack_zip from ..adapters.ethernet_adapter import EthernetAdapter from ..error import NodeError, ImageMissingError from ..nios.nio_udp import NIOUDP @@ -1657,8 +1658,8 @@ class QemuVM(BaseNode): os.mkdir(config_dir) if os.path.exists(zip_file): os.remove(zip_file) - if await self._mcopy("-s", "-m", "x:/", config_dir) == 0: - shutil.make_archive(zip_file[:-4], "zip", config_dir) + if await self._mcopy("-s", "-m", "-n", "--", "x:/", config_dir) == 0: + pack_zip(zip_file, config_dir) except OSError as e: log.error("Can't export config: {}".format(e)) finally: @@ -1674,12 +1675,12 @@ class QemuVM(BaseNode): try: shutil.rmtree(config_dir, ignore_errors=True) os.mkdir(config_dir) - shutil.unpack_archive(zip_file, config_dir) + unpack_zip(zip_file, config_dir) shutil.copyfile(getattr(self, "config_disk_image"), disk) config_files = [os.path.join(config_dir, fname) for fname in os.listdir(config_dir)] if config_files: - if await self._mcopy("-s", "-m", *config_files, "x:/") != 0: + if await self._mcopy("-s", "-m", "-o", "--", *config_files, "x:/") != 0: os.remove(disk) os.remove(zip_file) except OSError as e: diff --git a/gns3server/compute/qemu/utils/ziputils.py b/gns3server/compute/qemu/utils/ziputils.py new file mode 100644 index 00000000..3ff8c999 --- /dev/null +++ b/gns3server/compute/qemu/utils/ziputils.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import time +import shutil +import zipfile + +def pack_zip(filename, root_dir=None, base_dir=None): + """Create a zip archive""" + + if filename[-4:].lower() == ".zip": + filename = filename[:-4] + shutil.make_archive(filename, 'zip', root_dir, base_dir) + +def unpack_zip(filename, extract_dir=None): + """Unpack a zip archive""" + + dirs = [] + if not extract_dir: + extract_dir = os.getcwd() + + try: + with zipfile.ZipFile(filename, 'r') as zfile: + for zinfo in zfile.infolist(): + fname = os.path.join(extract_dir, zinfo.filename) + date_time = time.mktime(zinfo.date_time + (0, 0, -1)) + zfile.extract(zinfo, extract_dir) + + # update timestamp + if zinfo.is_dir(): + dirs.append((fname, date_time)) + else: + os.utime(fname, (date_time, date_time)) + # update timestamp of directories + for fname, date_time in reversed(dirs): + os.utime(fname, (date_time, date_time)) + except zipfile.BadZipFile: + raise shutil.ReadError("%s is not a zip file" % filename)