diff --git a/gns3server/controller/export_project.py b/gns3server/controller/export_project.py index df727574..b8cd5a0c 100644 --- a/gns3server/controller/export_project.py +++ b/gns3server/controller/export_project.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import os +import sys import json import asyncio import aiohttp @@ -23,6 +24,7 @@ import zipfile import tempfile import zipstream +from datetime import datetime import logging log = logging.getLogger(__name__) @@ -77,6 +79,7 @@ def export_project(project, temporary_dir, include_images=False, keep_compute_id # ignore the .gns3 file if file.endswith(".gns3"): continue + _patch_mtime(path) zstream.write(path, os.path.relpath(path, project._path), compress_type=zipfile.ZIP_DEFLATED) # Export files from remote computes @@ -99,12 +102,29 @@ def export_project(project, temporary_dir, include_images=False, keep_compute_id f.write(data) response.close() f.close() + _patch_mtime(temp_path) zstream.write(temp_path, arcname=compute_file["path"], compress_type=zipfile.ZIP_DEFLATED) downloaded_files.add(compute_file['path']) return zstream +def _patch_mtime(path): + """ + Patch the file mtime because ZIP does not support timestamps before 1980 + + :param path: file path + """ + + if sys.platform.startswith("win"): + # only UNIX type platforms + return + st = os.stat(path) + file_date = datetime.fromtimestamp(st.st_mtime) + if file_date.year < 1980: + new_mtime = file_date.replace(year=1980).timestamp() + os.utime(path, (st.st_atime, new_mtime)) + def _is_exportable(path): """ :returns: True if file should not be included in the final archive @@ -228,6 +248,7 @@ def _export_local_image(image, zstream): if os.path.exists(path): arcname = os.path.join("images", directory, os.path.basename(image)) + _patch_mtime(path) zstream.write(path, arcname) return diff --git a/gns3server/controller/snapshot.py b/gns3server/controller/snapshot.py index 70c3d173..cbae0429 100644 --- a/gns3server/controller/snapshot.py +++ b/gns3server/controller/snapshot.py @@ -101,7 +101,7 @@ class Snapshot: with tempfile.TemporaryDirectory() as tmpdir: zipstream = yield from export_project(self._project, tmpdir, keep_compute_id=True, allow_all_nodes=True) yield from wait_run_in_executor(self._create_snapshot_file, zipstream) - except OSError as e: + except (ValueError, OSError, RuntimeError) as e: raise aiohttp.web.HTTPConflict(text="Could not create snapshot file '{}': {}".format(self.path, e)) @asyncio.coroutine diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py index 46e17661..37137ca2 100644 --- a/gns3server/handlers/api/controller/project_handler.py +++ b/gns3server/handlers/api/controller/project_handler.py @@ -304,8 +304,8 @@ class ProjectHandler: yield from response.write_eof() # Will be raise if you have no space left or permission issue on your temporary directory # RuntimeError: something was wrong during the zip process - except (OSError, RuntimeError) as e: - raise aiohttp.web.HTTPNotFound(text="Can't export project: {}".format(str(e))) + except (ValueError, OSError, RuntimeError) as e: + raise aiohttp.web.HTTPNotFound(text="Cannot export project: {}".format(str(e))) @Route.post( r"/projects/{project_id}/import",