lumia

Archive checksum manager
git clone git://lumidify.org/git/lumia.git
Log | Files | Refs

commit a964a391d65fce7963a55c9964bbbe6a5520a2ff
parent 3d69b3355b084a09aaa2a64a4b7d47e9296d4b01
Author: lumidify <nobody@lumidify.org>
Date:   Mon, 23 Mar 2020 15:33:23 +0100

Fixes; allow to check only individual files

Diffstat:
Mlumia.pl | 143+++++++++++++++++++++++++++++++++++++++++++------------------------------------
1 file changed, 78 insertions(+), 65 deletions(-)

diff --git a/lumia.pl b/lumia.pl @@ -1,25 +1,16 @@ #!/usr/bin/env perl -# FIXME: add ckold to check what files don't exist anymore and remove them from cksums -# FIXME: write cksums as we go along during check_add_new, not just at end # FIXME: some way to avoid writing .lumidify* in dirs but still index them? e.g. Code/CMSG # FIXME: cksum don't create malformed line if permission denied -# FIXME: dirs in ignore still created when extracting # FIXME: make generic function to traverse dirs and call other function on each dir -# FIXME: extract - actually copy files, don't write again and regen cksums.cksum -# FIXME: check cksums.cksum when reading files -# FIXME: check if .lumidify* are writable first so cksums aren't generated uselessly # FIXME: handle rm, etc. on .lumidify* files -# FIXME: handle files with newline? # FIXME: ignore all except for a certain file/folder -# FIXME: store modified date and recksum filed with changed date +# FIXME: store modified date and checksum filed with changed date # FIXME: sha256 works with leading zeroes, maybe switch? # FIXME: allow different hash types # FIXME: don't write anything if cksum fails (will create malformed line) # FIXME: add option to just check dir structure or maybe check if everything exists -# WARNING: convert regenerates cksums.cksum # FIXME: add option to compare cksums of two dirs -# FIXME: add option to check only individual files use strict; use warnings; @@ -34,12 +25,21 @@ use Data::Dumper; use Scalar::Util qw(looks_like_number); use Getopt::Long; +# the file used to store checksums for files +my $CKSUM_FILE = ".lumidify_archive_cksums"; +# the file used to store directory names +my $DIR_FILE = ".lumidify_archive_dirs"; +# the file read to ignore files or directories +my $IGNORE_FILE = ".lumidify_archive_ignore"; +# the file containing checksums of $CKSUM_FILE and $DIR_FILE +my $DOUBLE_CKSUM_FILE = ".lumidify_archive_cksums.cksum"; + my $CKSUM_CMD = 'cksum -q'; my %SPECIAL_FILES = ( - ".lumidify_archive_cksums" => 1, - ".lumidify_archive_cksums.cksum" => 1, - ".lumidify_archive_ignore" => 1, - ".lumidify_archive_dirs" => 1 + $CKSUM_FILE => 1, + $DIR_FILE => 1, + $IGNORE_FILE => 1, + $DOUBLE_CKSUM_FILE => 1 ); # escape a filename for writing into the checksum files @@ -86,18 +86,14 @@ sub make_file_iter_basic { }, @files; } -# make a basic directory iterator, which only returns all directories -sub make_dir_iter { - make_file_iter_basic sub {-d $_[0]}, @_; -} - # make an interator that only returns the directories which are present -# in the .lumidify_archive_dirs files +# in the $DIR_FILE files, in addition to the files and directories that +# were originally passed as arguments # note: this returns nonexistent directories if those are still # specified in the lumia files sub make_lumia_iter { make_file_iter sub {1}, sub { - my $path = "$_[0]/.lumidify_archive_dirs"; + my $path = "$_[0]/$DIR_FILE"; return [] if !-f $path; my $dirs = read_file($path, {}); return if !defined $dirs; @@ -117,10 +113,7 @@ sub make_lumia_iter { # remove all special lumia files from the given directory sub clean_files { my $dir = shift; - my $match_lumia_files = sub { - exists $SPECIAL_FILES{basename $_[0]}; - }; - my $iter = make_file_iter $match_lumia_files, $dir; + my $iter = make_file_iter_basic sub {exists $SPECIAL_FILES{basename $_[0]};}, $dir; while (my $file = $iter->()) { if (!unlink $file) { warn "WARNING: Unable to remove file \"$file\"!"; @@ -202,9 +195,9 @@ sub read_cksum_file { # read the checksums and directory names in $dir sub read_cksums { my $dir = shift; - my $cksums = read_cksum_file("$dir/.lumidify_archive_cksums", {}); + my $cksums = read_cksum_file("$dir/$CKSUM_FILE", {}); return undef if !defined $cksums; - $cksums = read_file("$dir/.lumidify_archive_dirs", $cksums); + $cksums = read_file("$dir/$DIR_FILE", $cksums); return undef if !defined $cksums; return $cksums; } @@ -246,19 +239,40 @@ sub check_cksums { # check the checksums of all files in $top_dir sub check_files { - my $top_dir = shift; - my $iter = make_lumia_iter $top_dir; - while (my $dir = $iter->()) { - check_cksums $dir, ".lumidify_archive_cksums.cksum"; - check_cksums $dir, ".lumidify_archive_cksums"; + my $iter = make_lumia_iter @_; + while (my $file = $iter->()) { + if (-d $file) { + check_cksums $file, "$DOUBLE_CKSUM_FILE"; + check_cksums $file, "$CKSUM_FILE"; + } else { + my $dir = dirname $file; + my $base = basename $file; + if (exists $SPECIAL_FILES{$base}) { + warn "ERROR: File is reserved for lumia.pl: $file\n"; + next; + } + my $cksums = read_cksum_file("$dir/$CKSUM_FILE"); + next if !defined $cksums; + if (!exists $cksums->{$base}) { + warn "ERROR: File doesn't exist in checksums: $file\n"; + next; + } + my $output = get_cksum "$file"; + next if !defined $output; + if ($output eq $cksums->{$base}) { + print "OK $file\n"; + } else { + print "FAILED $file\n"; + } + } } } # write the checksums of the special lumia files given as arguments -# to ".lumidify_archive_cksums.cksum" in $dir +# to "$DOUBLE_CKSUM_FILE" in $dir sub write_special_cksums { my ($dir, @files) = @_; - my $cksum_file = "$dir/.lumidify_archive_cksums.cksum"; + my $cksum_file = "$dir/$DOUBLE_CKSUM_FILE"; my $cksums = {}; if (-f $cksum_file) { $cksums = read_cksum_file $cksum_file, {}; @@ -276,7 +290,7 @@ sub write_special_cksums { # - if $file_func is set, it is called for each new file # - if $before_dir_func is set, it is called before processing the # files in each directory that has new files OR if a directory -# is entirely new (well, it only checks if ".lumidify_archive_cksums.cksum" exists) +# is entirely new (well, it only checks if "$DOUBLE_CKSUM_FILE" exists) # - if $after_dir_func is set, it is called after processing the # files in each directory that has new files sub check_new_files { @@ -294,9 +308,9 @@ sub check_new_files { } return {}; }; - my $ignore = $read_file_noerror->("$dir/.lumidify_archive_ignore", \&read_file); - my $lumia_dirs = $read_file_noerror->("$dir/.lumidify_archive_dirs", \&read_file); - my $lumia_files = $read_file_noerror->("$dir/.lumidify_archive_cksums", \&read_cksum_file); + my $ignore = $read_file_noerror->("$dir/$IGNORE_FILE", \&read_file); + my $lumia_dirs = $read_file_noerror->("$dir/$DIR_FILE", \&read_file); + my $lumia_files = $read_file_noerror->("$dir/$CKSUM_FILE", \&read_cksum_file); my @dirs; my $found = 0; while (my $file = readdir $dh) { @@ -317,7 +331,7 @@ sub check_new_files { } closedir $dh; # also call $before_dir_func if the directory has not been initialized yet - if (!$found && !-f "$dir/.lumidify_archive_cksums.cksum" && defined $before_dir_func) { + if (!$found && !-f "$dir/$DOUBLE_CKSUM_FILE" && defined $before_dir_func) { $before_dir_func->($dir); } if ($found && defined $after_dir_func) { @@ -338,7 +352,7 @@ sub check_add_new_files { my ($dir, $file) = @_; my $fullpath = "$dir/$file"; if (-d $fullpath) { - my $dir_file = "$dir/.lumidify_archive_dirs"; + my $dir_file = "$dir/$DIR_FILE"; my $fh; if (!open $fh, ">>", $dir_file) { warn "ERROR: Unable to append to file \"$dir_file\"!"; @@ -350,7 +364,7 @@ sub check_add_new_files { } else { my $cksum_output = get_cksum $fullpath; return if !defined $cksum_output; - my $cksum_file = "$dir/.lumidify_archive_cksums"; + my $cksum_file = "$dir/$CKSUM_FILE"; my $fh; if (!open $fh, ">>", $cksum_file) { warn "ERROR: Unable to append to file \"$cksum_file\"!"; @@ -362,8 +376,8 @@ sub check_add_new_files { } print "Added \"$fullpath\"\n"; }, sub { - if (-f "$_[0]/.lumidify_archive_cksums.cksum") { - if (!check_cksums $_[0], ".lumidify_archive_cksums.cksum", 1) { + if (-f "$_[0]/$DOUBLE_CKSUM_FILE") { + if (!check_cksums $_[0], "$DOUBLE_CKSUM_FILE", 1) { warn "Checksum files corrupt in \"$_[0]\", not adding new checksums!\n"; return 0; } @@ -373,11 +387,11 @@ sub check_add_new_files { return 1; }, sub { if ($changed_dirs) { - write_special_cksums $_[0], ".lumidify_archive_dirs"; + write_special_cksums $_[0], "$DIR_FILE"; $changed_dirs = 0; } if ($changed_files) { - write_special_cksums $_[0], ".lumidify_archive_cksums"; + write_special_cksums $_[0], "$CKSUM_FILE"; $changed_files = 0; } }; @@ -417,13 +431,13 @@ sub write_cksums { # No, this isn't efficient... if ($files_modified) { my %file_cksums = map {$_ => $contents->{$_}} grep({defined $contents->{$_}} keys %$contents); - write_cksum_file("$dir/.lumidify_archive_cksums", \%file_cksums); - write_special_cksums $dir, ".lumidify_archive_cksums"; + write_cksum_file("$dir/$CKSUM_FILE", \%file_cksums); + write_special_cksums $dir, "$CKSUM_FILE"; } if ($dirs_modified) { my %dir_cksums = map {$_ => undef} grep({!defined $contents->{$_}} keys %$contents); - write_file "$dir/.lumidify_archive_dirs", \%dir_cksums; - write_special_cksums $dir, ".lumidify_archive_dirs"; + write_file "$dir/$DIR_FILE", \%dir_cksums; + write_special_cksums $dir, "$DIR_FILE"; } } @@ -434,7 +448,7 @@ sub check_old_files { while (my $dir = $iter->()) { # if $dir doesn't exist, the iterator already issued a warning if (-e $dir) { - my $cksums = read_cksum_file("$dir/.lumidify_archive_cksums", {}) // {}; + my $cksums = read_cksum_file("$dir/$CKSUM_FILE", {}) // {}; foreach my $file (keys %$cksums) { if (!-e "$dir/$file") { warn "Nonexistent file: \"$dir/$file\"!\n"; @@ -453,26 +467,26 @@ sub remove_old_files { if (!-e $dir) { my $parent = dirname $dir; my $child = basename $dir; - my $lumia_dirs = read_file("$parent/.lumidify_archive_dirs", {}) // {}; + my $lumia_dirs = read_file("$parent/$DIR_FILE", {}) // {}; if (exists $lumia_dirs->{$child}) { delete $lumia_dirs->{$child}; - write_file "$parent/.lumidify_archive_dirs", $lumia_dirs; - print "Removed \"$dir\" from \"$parent/.lumidify_archive_dirs\"\n"; - write_special_cksums $parent, ".lumidify_archive_dirs"; + write_file "$parent/$DIR_FILE", $lumia_dirs; + print "Removed \"$dir\" from \"$parent/$DIR_FILE\"\n"; + write_special_cksums $parent, "$DIR_FILE"; } } else { - my $cksums = read_cksum_file("$dir/.lumidify_archive_cksums", {}) // {}; + my $cksums = read_cksum_file("$dir/$CKSUM_FILE", {}) // {}; my $found = 0; foreach my $file (keys %$cksums) { if (!-e "$dir/$file") { delete $cksums->{$file}; - print "Removed \"$dir/$file\" from \"$dir/.lumidify_archive_cksums\"\n"; + print "Removed \"$dir/$file\" from \"$dir/$CKSUM_FILE\"\n"; $found = 1; } } if ($found) { - write_cksum_file "$dir/.lumidify_archive_cksums", $cksums; - write_special_cksums $dir, ".lumidify_archive_cksums"; + write_cksum_file "$dir/$CKSUM_FILE", $cksums; + write_special_cksums $dir, "$CKSUM_FILE"; } } } @@ -775,13 +789,13 @@ sub make_dirs { push(@{$dirs{$parent}}, basename($dir)); } foreach my $parent (keys %dirs) { - my $parent_dirs = read_file "$parent/.lumidify_archive_dirs", {}; + my $parent_dirs = read_file "$parent/$DIR_FILE", {}; next if !defined $parent_dirs; foreach my $dir (@{$dirs{$parent}}) { $parent_dirs->{$dir} = ""; } - write_file "$parent/.lumidify_archive_dirs", $parent_dirs; - write_special_cksums $parent, ".lumidify_archive_dirs"; + write_file "$parent/$DIR_FILE", $parent_dirs; + write_special_cksums $parent, "$DIR_FILE"; } } @@ -842,11 +856,10 @@ if ($ARGV[0] eq "mv") { } remove_old_files $dir; } elsif ($ARGV[0] eq "check") { - my $dir = "."; - if ($#ARGV > 0) { - $dir = $ARGV[1]; + if ($#ARGV < 1) { + die "At least one path required"; } - check_files $dir; + check_files @ARGV[1..$#ARGV]; } elsif ($ARGV[0] eq "clean") { my $dir = "."; if ($#ARGV > 0) {