summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels Thykier <niels@thykier.net>2016-01-10 10:19:29 +0000
committerNiels Thykier <niels@thykier.net>2016-01-10 10:47:26 +0000
commit1566865e67d2712c45462794cc29d89f75c011d1 (patch)
tree017ca3bd9b5e2ae6f7d467686c9d631829f958ce
parentbca06f8d784fb0d6ac6f1358fc1ea2366fbb774e (diff)
downloaddebhelper-1566865e67d2712c45462794cc29d89f75c011d1.tar.gz
Dh_Lib: Add restore_file_on_clean
Signed-off-by: Niels Thykier <niels@thykier.net>
-rw-r--r--Debian/Debhelper/Dh_Lib.pm78
-rwxr-xr-xdh_clean10
-rw-r--r--doc/PROGRAMMING9
3 files changed, 95 insertions, 2 deletions
diff --git a/Debian/Debhelper/Dh_Lib.pm b/Debian/Debhelper/Dh_Lib.pm
index 3d846b6e..da20b76b 100644
--- a/Debian/Debhelper/Dh_Lib.pm
+++ b/Debian/Debhelper/Dh_Lib.pm
@@ -35,6 +35,7 @@ use vars qw(@EXPORT %dh);
&install_file &install_prog &install_lib &install_dir
&get_source_date_epoch &is_cross_compiling
&generated_file &autotrigger &package_section
+ &restore_file_on_clean &restore_all_files
);
my $max_compat=10;
@@ -1322,6 +1323,83 @@ sub install_dh_config_file {
return 1;
}
+sub restore_file_on_clean {
+ my ($file) = @_;
+ my $bucket_index = 'debian/.debhelper/bucket/index';
+ my $bucket_dir = 'debian/.debhelper/bucket/files';
+ my $checksum;
+ if (not -d $bucket_dir) {
+ install_dir($bucket_dir);
+ }
+ if ($file =~ m{^/}) {
+ error("restore_file_on_clean requires a path relative to the package dir");
+ }
+ $file =~ s{^\./}{}g;
+ $file =~ s{//++}{}g;
+ if ($file =~ m{^\.} or $file =~ m{/CVS/} or $file =~ m{/\.svn/}) {
+ # We do not want to smash a Vcs repository by accident.
+ warning("Attempt to store $file, which looks like a VCS file or");
+ warning("a hidden package file (like quilt's \".pc\" directory");
+ error("This tool probably contains a bug.");
+ }
+ if (-l $file or not -f _) {
+ error("Cannot store $file, which is a non-file (incl. a symlink)");
+ }
+ require Digest::SHA;
+
+ $checksum = Digest::SHA->new('256')->addfile($file, 'b')->hexdigest;
+
+ if (not $dh{NO_ACT}) {
+ my ($in_index);
+ open(my $fd, '+>>', $bucket_index)
+ or error("open($bucket_index, a+) failed: $!");
+ seek($fd, 0, 0);
+ while (my $line = <$fd>) {
+ my ($cs, $stored_file);
+ chomp($line);
+ ($cs, $stored_file) = split(m/ /, $line, 2);
+ next if ($stored_file ne $file);
+ $in_index = 1;
+ }
+ if (not $in_index) {
+ # Copy and then rename so we always have the full copy of
+ # the file in the correct place (if any at all).
+ doit('cp', '-an', '--reflink=auto', $file, "${bucket_dir}/${checksum}.tmp");
+ doit('mv', '-f', "${bucket_dir}/${checksum}.tmp", "${bucket_dir}/${checksum}");
+ print {$fd} "${checksum} ${file}\n";
+ }
+ close($fd) or error("close($bucket_index) failed: $!");
+ }
+
+ return 1;
+}
+
+sub restore_all_files {
+ my $bucket_index = 'debian/.debhelper/bucket/index';
+ my $bucket_dir = 'debian/.debhelper/bucket/files';
+
+ return if not -f $bucket_index;
+ open(my $fd, '<', $bucket_index)
+ or error("open($bucket_index) failed: $!");
+
+ while (my $line = <$fd>) {
+ my ($cs, $stored_file, $bucket_file);
+ chomp($line);
+ ($cs, $stored_file) = split(m/ /, $line, 2);
+ $bucket_file = "${bucket_dir}/${cs}";
+ # Restore by copy and then rename. This ensures that:
+ # 1) If dh_clean is interrupted, we can always do a full restore again
+ # (otherwise, we would be missing some of the files and have to handle
+ # that with scary warnings)
+ # 2) The file is always fully restored or in its "pre-restore" state.
+ doit('cp', '-an', '--reflink=auto', $bucket_file, "${bucket_file}.tmp");
+ doit('mv', '-Tf', "${bucket_file}.tmp", $stored_file);
+ }
+ close($fd);
+ return;
+}
+
+
1
# Local Variables:
diff --git a/dh_clean b/dh_clean
index 666766d9..f5fc8adc 100755
--- a/dh_clean
+++ b/dh_clean
@@ -113,8 +113,14 @@ foreach my $package (@{$dh{DOPACKAGES}}) {
unless excludefile($tmp);
}
-# Remove internal state data
-doit('rm', '-rf', 'debian/.debhelper/') if not $dh{D_FLAG};
+
+if (not $dh{D_FLAG}) {
+ # Restore all files in our bucket (before we delete said bucket)
+ restore_all_files();
+
+ # Remove internal state data
+ doit('rm', '-rf', 'debian/.debhelper/');
+}
# Remove all debhelper logs.
diff --git a/doc/PROGRAMMING b/doc/PROGRAMMING
index e5b9a726..1f03bdfb 100644
--- a/doc/PROGRAMMING
+++ b/doc/PROGRAMMING
@@ -290,6 +290,15 @@ load_log($package, $hashref)
write_log($cmd, $package ...)
Writes the log files for the specified package(s), adding
the cmd to the end.
+restore_file_on_clean($file)
+ Store a copy of $file, which will be restored by dh_clean.
+ The $file *must* be a relative path to the package root and
+ *must* be a real regular file. Dirs, devices and symlinks
+ (and everything else) *cannot* be restored by this.
+ If $file is passed multiple times (e.g. from different programs)
+ only the first version is stored.
+ CAVEAT: This *cannot* undo arbitrary "rm -fr"'ing. The dir,
+ which is/was in $file, must be present when dh_clean is called.
make_symlink($src, $dest, $tmp)
Creates a Policy compliant sytem link called $dest pointing to
$src. If $tmp is given, then $tmp will be prefixed to $dest when