From 9b4cf50fc498028e43ce008dacc7057a488bb65a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 1 Sep 2017 22:02:01 +0100 Subject: [PATCH] Fix corruption of an existing DCP when a new one is made with the same video asset (#1126). --- src/lib/reel_writer.cc | 20 ++++++++++++--- test/remake_id_test.cc | 55 ++++++++++++++++++++++++++++++++++++++++++ test/wscript | 1 + 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 test/remake_id_test.cc diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index fde977c3a..10d405961 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -197,14 +197,26 @@ ReelWriter::frame_info_position (Frame frame, Eyes eyes) const Frame ReelWriter::check_existing_picture_asset () { - /* Try to open the existing asset */ DCPOMATIC_ASSERT (_picture_asset->file()); - FILE* asset_file = fopen_boost (_picture_asset->file().get(), "rb"); + boost::filesystem::path asset = _picture_asset->file().get(); + + /* If there is an existing asset, break any hard links to it as we are about to change its contents + (if only by changing the IDs); see #1126. + */ + + if (boost::filesystem::exists(asset) && boost::filesystem::hard_link_count(asset) > 1) { + boost::filesystem::copy_file (asset, asset.string() + ".tmp"); + boost::filesystem::remove (asset); + boost::filesystem::rename (asset.string() + ".tmp", asset); + } + + /* Try to open the existing asset */ + FILE* asset_file = fopen_boost (asset, "rb"); if (!asset_file) { - LOG_GENERAL ("Could not open existing asset at %1 (errno=%2)", _picture_asset->file()->string(), errno); + LOG_GENERAL ("Could not open existing asset at %1 (errno=%2)", asset.string(), errno); return 0; } else { - LOG_GENERAL ("Opened existing asset at %1", _picture_asset->file()->string()); + LOG_GENERAL ("Opened existing asset at %1", asset.string()); } /* Offset of the last dcp::FrameInfo in the info file */ diff --git a/test/remake_id_test.cc b/test/remake_id_test.cc new file mode 100644 index 000000000..d8bb5df83 --- /dev/null +++ b/test/remake_id_test.cc @@ -0,0 +1,55 @@ +/* + Copyright (C) 2017 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic 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 2 of the License, or + (at your option) any later version. + + DCP-o-matic 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 DCP-o-matic. If not, see . + +*/ + +#include "lib/ffmpeg_content.h" +#include "lib/content_factory.h" +#include "lib/subtitle_content.h" +#include "lib/film.h" +#include "test.h" +#include + +using boost::shared_ptr; +using boost::dynamic_pointer_cast; + +/** Check for bug #1126 whereby making a new DCP using the same video asset as an old one + * corrupts the old one. + */ +BOOST_AUTO_TEST_CASE (remake_id_test) +{ + /* Make a DCP */ + shared_ptr film = new_test_film2 ("remake_id_test"); + shared_ptr content = content_factory(film, "test/data/flat_red.png").front(); + film->examine_and_add_content (content); + BOOST_REQUIRE (!wait_for_jobs ()); + film->make_dcp (); + BOOST_REQUIRE (!wait_for_jobs ()); + + /* Copy the video file */ + boost::filesystem::path first_video = video_file(film); + boost::filesystem::copy_file (first_video, first_video.string() + ".copy"); + + /* Make a new DCP with the same video file */ + film->set_name ("remake_id_test2"); + film->make_dcp (); + BOOST_REQUIRE (!wait_for_jobs ()); + + /* Check that the video in the first DCP hasn't changed */ + check_file (first_video, first_video.string() + ".copy"); +} diff --git a/test/wscript b/test/wscript index 6340627cd..ecbc32fab 100644 --- a/test/wscript +++ b/test/wscript @@ -87,6 +87,7 @@ def build(bld): rect_test.cc reels_test.cc required_disk_space_test.cc + remake_id_test.cc remake_with_subtitle_test.cc render_subtitles_test.cc scaling_test.cc -- 2.30.2