From e3a3959dcdb461886ed2d5fa4bcd3e345e750e7a Mon Sep 17 00:00:00 2001 From: cbarratt Date: Sun, 3 Dec 2006 02:43:42 +0000 Subject: [PATCH] * Removed default paths from conf/config.pl so configure.pl will determine the correct ones at install time. Avoids problem of the config editor complaining about bad executable paths the first time you use it. * Changed first byte of compressed files with rsync checksums appended to 0xd7 to allow correct protocol_version >= 27 md4 checksums to be written. Old cached checksum files have a first byte 0xd6 and are now considered to be uncached. They will be automatically updated as needed. * BackupPC_tarPCCopy now handles all file types correctly. Reported by George Avrunin. * Fixes for rsync restore where hardlink is to file outside of the top-level restore directory. Reported by George Avrunin, who helped with debugging. * Fixes for checksum mismatch on restore for certain file sizes. Reported by George Avrunin and others. --- ChangeLog | 25 ++++- bin/BackupPC_tarCreate | 2 +- bin/BackupPC_tarPCCopy | 162 ++++++++++++++++--------------- conf/config.pl | 29 +++--- configure.pl | 6 +- lib/BackupPC/CGI/EditConfig.pm | 116 +++++++++++----------- lib/BackupPC/FileZIO.pm | 6 +- lib/BackupPC/Xfer/RsyncDigest.pm | 31 ++++-- lib/BackupPC/Xfer/RsyncFileIO.pm | 37 ++++--- 9 files changed, 229 insertions(+), 185 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7dccc15..366ba11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21,11 +21,34 @@ # Version __VERSION__, __RELEASEDATE__ #------------------------------------------------------------------------ +* Removed default paths from conf/config.pl so configure.pl will + determine the correct ones at install time. Avoids problem of + the config editor complaining about bad executable paths the + first time you use it. + +* Changed first byte of compressed files with rsync checksums appended + to 0xd7 to allow correct protocol_version >= 27 md4 checksums to be + written. Old cached checksum files have a first byte 0xd6 and are + now considered to be uncached. They will be automatically updated + as needed. + +* BackupPC_tarPCCopy now handles all file types correctly. Reported + by George Avrunin. + +* Fixes for rsync restore where hardlink is to file outside of the + top-level restore directory. Reported by George Avrunin, who helped + with debugging. + +* Fixes for checksum mismatch on restore for certain file sizes. + Reported by George Avrunin and others. + * Fix for config.pl writing code to handle multi-line expressions. Reported by David Relson and others. * Fix for CGI editor when deleting hash entries whose keys are - non alphanumeric. Report by David Relson. + non alphanumeric. Report by David Relson and Aaron Ciarlotta. + +* Two fixes to configure.pl from Andreas Vögele. #------------------------------------------------------------------------ # Version 3.0.0beta2, 18 Nov 2006 diff --git a/bin/BackupPC_tarCreate b/bin/BackupPC_tarCreate index e0fd0fb..45eb1cb 100755 --- a/bin/BackupPC_tarCreate +++ b/bin/BackupPC_tarCreate @@ -502,7 +502,7 @@ sub TarWriteFile $arg = "/" if ( $arg eq "." ); $arg =~ s{^\./+}{/}; $arg =~ s{/+$}{}; - $done = 1 if ( $name eq $arg || $name =~ /^\Q$arg\// ); + $done = 1 if ( $name eq $arg || $name =~ /^\Q$arg\// || $arg eq "" ); } } if ( $done ) { diff --git a/bin/BackupPC_tarPCCopy b/bin/BackupPC_tarPCCopy index c703528..53f3981 100755 --- a/bin/BackupPC_tarPCCopy +++ b/bin/BackupPC_tarPCCopy @@ -175,8 +175,7 @@ sub archiveFile my @s = stat($_); # - # We just handle directories and files; no symlinks or - # char/block special files. + # Default type - we'll update later if it is a symlink, hardlink etc # $hdr->{type} = -d _ ? BPC_FTYPE_DIR : -f _ ? BPC_FTYPE_FILE @@ -230,8 +229,7 @@ sub archiveFile } } $hdr->{compress} = $ClientBkupCompress; - if ( $hdr->{type} == BPC_FTYPE_FILE && $hdr->{nlink} > 1 - && $hdr->{name} =~ /^f/ ) { + if ( $hdr->{type} == BPC_FTYPE_FILE && $hdr->{name} =~ /^f/ ) { (my $dir = $hdr->{fullPath}) =~ s{(.*)/.*}{$1}; if ( $ClientDir ne $dir ) { $ClientDir = $dir; @@ -247,8 +245,12 @@ sub archiveFile my $name = $hdr->{name}; $name = $bpc->fileNameUnmangle($name) if ( $ClientBkupMangle ); my $attr = $ClientDirAttr->get($name); - $hdr->{realSize} = $attr->{size} if ( defined($attr) ); - #print STDERR "$hdr->{fullPath} has real size $hdr->{realSize}\n"; + if ( defined($attr) ) { + $hdr->{type} = $attr->{type}; + $hdr->{realSize} = $attr->{size} + if ( $attr->{type} == BPC_FTYPE_FILE ); + } + #print STDERR "$hdr->{fullPath} has type $hdr->{type} and real size $hdr->{realSize}\n"; } } } else { @@ -425,91 +427,97 @@ sub TarWriteFile $hdr->{name} .= "/" if ( $hdr->{name} !~ m{/$} ); TarWriteFileInfo($fh, $hdr); $DirCnt++; - } elsif ( $hdr->{type} == BPC_FTYPE_FILE ) { + } elsif ( $hdr->{type} == BPC_FTYPE_FILE + || $hdr->{type} == BPC_FTYPE_HARDLINK + || $hdr->{type} == BPC_FTYPE_SYMLINK + || $hdr->{type} == BPC_FTYPE_CHARDEV + || $hdr->{type} == BPC_FTYPE_BLOCKDEV + || $hdr->{type} == BPC_FTYPE_FIFO + || $hdr->{type} == BPC_FTYPE_SOCKET ) { # - # Regular file: write the header and file + # Underlying file is a regular file: write the header and file # my($data, $dataMD5, $size, $linkName); - if ( $hdr->{type} == BPC_FTYPE_FILE && $hdr->{nlink} > 1 ) { - if ( defined($Inode2Path{$hdr->{inode}}) ) { - $linkName = $Inode2Path{$hdr->{inode}}; - #print STDERR "Got cache hit for $linkName\n"; - } else { - my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, - $hdr->{compress}); - if ( !defined($f) ) { - print(STDERR "Unable to open file $hdr->{fullPath}\n"); - $ErrorCnt++; - return; - } + if ( defined($Inode2Path{$hdr->{inode}}) ) { + $linkName = $Inode2Path{$hdr->{inode}}; + #print STDERR "Got cache hit for $linkName\n"; + } else { + my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, + $hdr->{compress}); + if ( !defined($f) ) { + print(STDERR "Unable to open file $hdr->{fullPath}\n"); + $ErrorCnt++; + return; + } + # + # Try to find the hardlink it points to by computing + # the pool file digest. + # + $f->read(\$dataMD5, $BufSize); + if ( !defined($hdr->{realSize}) ) { # - # Try to find the hardlink it points to by computing - # the pool file digest. + # Need to get the real size # - $f->read(\$dataMD5, $BufSize); - if ( !defined($hdr->{realSize}) ) { - # - # Need to get the real size - # - $size = length($dataMD5); - while ( $f->read(\$data, $BufSize) > 0 ) { - $size += length($data); - } - $hdr->{realSize} = $size; + $size = length($dataMD5); + while ( $f->read(\$data, $BufSize) > 0 ) { + $size += length($data); } - $f->close(); - my $md5 = Digest::MD5->new; - my $len = length($dataMD5); - $hdr->{realSize} = $len if ( $hdr->{type} != BPC_FTYPE_FILE ); - if ( $hdr->{realSize} < 1048576 - && length($dataMD5) != $hdr->{realSize} ) { - print(STDERR "File $hdr->{fullPath} has bad size" - . " (expect $hdr->{realSize}, got $len)\n"); - } else { - my $digest = $bpc->Buffer2MD5($md5, $hdr->{realSize}, - \$dataMD5); - my $path = $bpc->MD52Path($digest, $hdr->{compress}); - my $i = -1; - - # print(STDERR "Looking up $hdr->{fullPath} at $path\n"); - while ( 1 ) { - my $testPath = $path; - $testPath .= "_$i" if ( $i >= 0 ); - last if ( !-f $testPath ); - my $inode = (stat(_))[1]; - if ( $inode == $hdr->{inode} ) { - # - # Found it! Just emit a tar hardlink - # - $testPath =~ s{\Q$TopDir\E}{..}; - $linkName = $testPath; - last; - } - $i++; + $hdr->{realSize} = $size; + } + $f->close(); + my $md5 = Digest::MD5->new; + my $len = length($dataMD5); + if ( $hdr->{realSize} < 1048576 + && length($dataMD5) != $hdr->{realSize} ) { + print(STDERR "File $hdr->{fullPath} has bad size" + . " (expect $hdr->{realSize}, got $len)\n"); + } else { + my $digest = $bpc->Buffer2MD5($md5, $hdr->{realSize}, + \$dataMD5); + my $path = $bpc->MD52Path($digest, $hdr->{compress}); + my $i = -1; + + # print(STDERR "Looking up $hdr->{fullPath} at $path\n"); + while ( 1 ) { + my $testPath = $path; + $testPath .= "_$i" if ( $i >= 0 ); + last if ( !-f $testPath ); + my $inode = (stat(_))[1]; + if ( $inode == $hdr->{inode} ) { + # + # Found it! Just emit a tar hardlink + # + $testPath =~ s{\Q$TopDir\E}{..}; + $linkName = $testPath; + last; } + $i++; } } - if ( defined($linkName) ) { - $hdr->{type} = BPC_FTYPE_HARDLINK; - $hdr->{linkname} = $linkName; - TarWriteFileInfo($fh, $hdr); - $HLinkCnt++; - #print STDERR "$hdr->{relPath} matches $testPath\n"; - if ( !$opts{c} && $hdr->{nlink} > 2 ) { - # - # add it to the cache if there are more - # than 2 links (pool + current file), - # since there are more to go - # - $Inode2Path{$hdr->{inode}} = $linkName; - } - return; + } + if ( defined($linkName) ) { + $hdr->{type} = BPC_FTYPE_HARDLINK; + $hdr->{linkname} = $linkName; + TarWriteFileInfo($fh, $hdr); + $HLinkCnt++; + #print STDERR "$hdr->{relPath} matches $testPath\n"; + if ( !$opts{c} && $hdr->{nlink} > 2 ) { + # + # add it to the cache if there are more + # than 2 links (pool + current file), + # since there are more to go + # + $Inode2Path{$hdr->{inode}} = $linkName; } - $size = 0; + return; + } + $size = 0; + if ( $hdr->{nlink} > 1 ) { print STDERR "Can't find $hdr->{relPath} in pool, will copy file\n"; $ErrorCnt++; } + $hdr->{type} = BPC_FTYPE_FILE; my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, 0); if ( !defined($f) ) { diff --git a/conf/config.pl b/conf/config.pl index 6caece2..9d0c276 100644 --- a/conf/config.pl +++ b/conf/config.pl @@ -201,7 +201,7 @@ $Conf{MaxOldLogFiles} = 14; # Full path to the df command. Security caution: normal users # should not allowed to write to this file or directory. # -$Conf{DfPath} = '/bin/df'; +$Conf{DfPath} = ''; # # Command to run df. The following variables are substituted at run-time: @@ -214,12 +214,11 @@ $Conf{DfCmd} = '$dfPath $topDir'; # # Full path to various commands for archiving # - -$Conf{SplitPath} = '/usr/bin/split'; -$Conf{ParPath} = '/usr/bin/par2'; -$Conf{CatPath} = '/bin/cat'; -$Conf{GzipPath} = '/bin/gzip'; -$Conf{Bzip2Path} = '/usr/bin/bzip2'; +$Conf{SplitPath} = ''; +$Conf{ParPath} = ''; +$Conf{CatPath} = ''; +$Conf{GzipPath} = ''; +$Conf{Bzip2Path} = ''; # # Maximum threshold for disk utilization on the __TOPDIR__ filesystem. @@ -912,7 +911,7 @@ $Conf{SmbSharePasswd} = ''; # # This setting only matters if $Conf{XferMethod} = 'smb'. # -$Conf{SmbClientPath} = '/usr/bin/smbclient'; +$Conf{SmbClientPath} = ''; # # Command to run smbclient for a full dump. @@ -1091,12 +1090,12 @@ $Conf{TarClientRestoreCmd} = '$sshPath -q -x -l root $host' # # This setting only matters if $Conf{XferMethod} = 'tar'. # -$Conf{TarClientPath} = '/bin/tar'; +$Conf{TarClientPath} = ''; # # Path to rsync executable on the client # -$Conf{RsyncClientPath} = '/bin/rsync'; +$Conf{RsyncClientPath} = ''; # # Full command to run rsync on the client machine. The following variables @@ -1297,7 +1296,7 @@ $Conf{BackupPCdShareName} = '/'; # # Path to backuppcd executable on the server # -$Conf{BackupPCdPath} = '/usr/bin/backuppcd'; +$Conf{BackupPCdPath} = ''; # # Full command to run backuppcd on the server to backup a given @@ -1413,7 +1412,7 @@ $Conf{ArchiveClientCmd} = '$Installdir/bin/BackupPC_archiveHost' # Full path for ssh. Security caution: normal users should not # allowed to write to this file or directory. # -$Conf{SshPath} = '/usr/bin/ssh'; +$Conf{SshPath} = ''; # # Full path for nmblookup. Security caution: normal users should not @@ -1422,7 +1421,7 @@ $Conf{SshPath} = '/usr/bin/ssh'; # nmblookup is from the Samba distribution. nmblookup is used to get the # netbios name, necessary for DHCP hosts. # -$Conf{NmbLookupPath} = '/usr/bin/nmblookup'; +$Conf{NmbLookupPath} = ''; # # NmbLookup command. Given an IP address, does an nmblookup on that @@ -1482,7 +1481,7 @@ $Conf{FixedIPNetBiosNameCheck} = 0; # # $Conf{PingPath} = '/bin/echo'; # -$Conf{PingPath} = '/bin/ping'; +$Conf{PingPath} = ''; # # Ping command. The following variables are substituted at run-time: @@ -1699,7 +1698,7 @@ $Conf{ClientNameAlias} = undef; # Full path to the sendmail command. Security caution: normal users # should not allowed to write to this file or directory. # -$Conf{SendmailPath} = '/usr/sbin/sendmail'; +$Conf{SendmailPath} = ''; # # Minimum period between consecutive emails to a single user. diff --git a/configure.pl b/configure.pl index 5a03034..03317be 100755 --- a/configure.pl +++ b/configure.pl @@ -333,6 +333,8 @@ check the name and verify that this user is in the passwd file. EOF exit(1) if ( $opts{batch} ); + } else { + last; } } @@ -539,7 +541,7 @@ foreach my $dir ( qw(bin doc # Create CGI image directory # foreach my $dir ( ($Conf{CgiImageDir}) ) { - next if ( $dir eq "" || -d $dir ); + next if ( $dir eq "" || -d "$DestDir$dir" ); mkpath("$DestDir$dir", 0, 0755); if ( !-d "$DestDir$dir" || !my_chown($Uid, $Gid, "$DestDir$dir") ) { die("Failed to create or chown $DestDir$dir"); @@ -846,6 +848,8 @@ will need to do: - Verify that the CGI script BackupPC_Admin runs correctly. You might need to change the permissions or group ownership of BackupPC_Admin. + If this is an upgrade and you are using mod_perl, you will need + to restart Apache. Otherwise it will have stale code. - BackupPC should be ready to start. Don't forget to run it as user $Conf{BackupPCUser}! The installation also contains an diff --git a/lib/BackupPC/CGI/EditConfig.pm b/lib/BackupPC/CGI/EditConfig.pm index f8f0add..351f429 100644 --- a/lib/BackupPC/CGI/EditConfig.pm +++ b/lib/BackupPC/CGI/EditConfig.pm @@ -373,8 +373,8 @@ sub action # #print STDERR Dumper(\%In); foreach my $v ( keys(%In) ) { - next if ( $v !~ /^v_z_(\Q$var\E(_z_.*|$))/ ); - delete($In{$v}) if ( !defined($In{"orig_z_$1"}) ); + next if ( $v !~ /^v_zZ_(\Q$var\E(_zZ_.*|$))/ ); + delete($In{$v}) if ( !defined($In{"orig_zZ_$1"}) ); } delete($In{"vflds.$var"}); } @@ -572,8 +572,8 @@ EOF return false; } var allVars = {}; - var varRE = new RegExp("^v_z_(" + varName + ".*)"); - var origRE = new RegExp("^orig_z_(" + varName + ".*)"); + var varRE = new RegExp("^v_zZ_(" + varName + ".*)"); + var origRE = new RegExp("^orig_zZ_(" + varName + ".*)"); for ( var i = 0 ; i < document.editForm.elements.length ; i++ ) { var e = document.editForm.elements[i]; var re; @@ -582,7 +582,7 @@ EOF allVars[re[1]] = 0; } allVars[re[1]]++; - //debugMsg("found v_z_ match with " + re[1]); + //debugMsg("found v_zZ_ match with " + re[1]); //debugMsg("allVars[" + re[1] + "] = " + allVars[re[1]]); } else if ( (re = origRE.exec(e.name)) != null ) { if ( allVars[re[1]] == null ) { @@ -600,7 +600,7 @@ EOF } else { // copy the original variable values //debugMsg("setting " + v); - eval("document.editForm.v_z_" + v + ".value = document.editForm.orig_z_" + v + ".value"); + eval("document.editForm.v_zZ_" + v + ".value = document.editForm.orig_zZ_" + v + ".value"); } } if ( sameShape ) { @@ -661,7 +661,7 @@ EOF if ( $In{deleteVar} ne "" && %$errors > 0 ) { my $matchAll = 1; foreach my $v ( keys(%$errors) ) { - if ( $v ne $In{deleteVar} && $v !~ /^\Q$In{deleteVar}_z_/ ) { + if ( $v ne $In{deleteVar} && $v !~ /^\Q$In{deleteVar}_zZ_/ ) { $matchAll = 0; last; } @@ -848,7 +848,7 @@ EOF if ( defined($In{menu}) || $In{saveAction} eq "Save" ) { if ( $In{saveAction} eq "Save" && !$userHost ) { # - # Emit the new settings as orig_z_ parameters + # Emit the new settings as orig_zZ_ parameters # $doneParam = {}; foreach my $param ( keys(%ConfigMeta) ) { @@ -867,10 +867,10 @@ EOF } } else { # - # Just switching menus: copy all the orig_z_ input parameters + # Just switching menus: copy all the orig_zZ_ input parameters # foreach my $var ( keys(%In) ) { - next if ( $var !~ /^orig_z_/ ); + next if ( $var !~ /^orig_zZ_/ ); my $val = decode_utf8($In{$var}); $contentHidden .= < @@ -920,7 +920,7 @@ sub fieldHiddenBuild $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" ); for ( my $i = 0 ; $i < @$varValue ; $i++ ) { - $content .= fieldHiddenBuild($type->{child}, "${varName}_z_$i", + $content .= fieldHiddenBuild($type->{child}, "${varName}_zZ_$i", $varValue->[$i], $prefix); } } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) { @@ -948,18 +948,18 @@ sub fieldHiddenBuild EOF } - $content .= fieldHiddenBuild($childType, "${varName}_z_$fld", + $content .= fieldHiddenBuild($childType, "${varName}_zZ_$fld", $varValue->{$fld}, $prefix); } } elsif ( $type->{type} eq "shortlist" ) { $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" ); $varValue = join(", ", @$varValue); $content .= < + EOF } else { $content .= < + EOF } return $content; @@ -990,9 +990,9 @@ sub fieldEditBuild EOF if ( defined($overrideVar) ) { my $override_checked = ""; - if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_z_/ - || !$isError && $In{insertVar} =~ /^\Q${varName}\E(_z_|$)/ - || !$isError && $In{addVar} =~ /^\Q${varName}\E(_z_|$)/ ) { + if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_zZ_/ + || !$isError && $In{insertVar} =~ /^\Q${varName}\E(_zZ_|$)/ + || !$isError && $In{addVar} =~ /^\Q${varName}\E(_zZ_|$)/ ) { $overrideSet = 1; } if ( $overrideSet ) { @@ -1009,7 +1009,7 @@ EOF $content .= "\n"; $varValue = [] if ( !defined($varValue) ); $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" ); - if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_z_\E(\d+)$/ + if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_zZ_\E(\d+)$/ && $1 < @$varValue ) { # # User deleted entry in this array @@ -1017,7 +1017,7 @@ EOF splice(@$varValue, $1, 1) if ( @$varValue > 1 || $type->{emptyOk} ); $In{deleteVar} = ""; } - if ( !$isError && $In{insertVar} =~ /^\Q${varName}_z_\E(\d+)$/ + if ( !$isError && $In{insertVar} =~ /^\Q${varName}_zZ_\E(\d+)$/ && $1 < @$varValue ) { # # User inserted entry in this array @@ -1054,12 +1054,12 @@ EOF if ( @$varValue > 1 || $type->{emptyOk} ) { $content .= < - + EOF } - $content .= fieldEditBuild($type->{child}, "${varName}_z_$i", + $content .= fieldEditBuild($type->{child}, "${varName}_zZ_$i", $varValue->[$i], $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); @@ -1069,17 +1069,17 @@ EOF for ( my $i = 0 ; $i < @$varValue ; $i++ ) { $content .= < - + EOF if ( @$varValue > 1 || $type->{emptyOk} ) { $content .= < + EOF } $content .= "\n"; - $content .= fieldEditBuild($type->{child}, "${varName}_z_$i", + $content .= fieldEditBuild($type->{child}, "${varName}_zZ_$i", $varValue->[$i], $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); @@ -1099,8 +1099,8 @@ EOF $varValue = {} if ( ref($varValue) ne "HASH" ); if ( !$isError && !$type->{noKeyEdit} - && $In{deleteVar} !~ /^\Q${varName}_z_\E.*_z_/ - && $In{deleteVar} =~ /^\Q${varName}_z_\E(.*)$/ ) { + && $In{deleteVar} !~ /^\Q${varName}_zZ_\E.*_zZ_/ + && $In{deleteVar} =~ /^\Q${varName}_zZ_\E(.*)$/ ) { # # User deleted entry in this hash # @@ -1134,8 +1134,8 @@ EOF if ( !$type->{noKeyEdit} && (keys(%$varValue) > 1 || $type->{emptyOk}) ) { $content .= < + EOF } if ( defined($type->{child}) ) { @@ -1151,7 +1151,7 @@ EOF EOF } $content .= "\n"; - $content .= fieldEditBuild($childType, "${varName}_z_$fld", + $content .= fieldEditBuild($childType, "${varName}_zZ_$fld", $varValue->{$fld}, $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); @@ -1193,7 +1193,7 @@ EOF EOF } - $content .= fieldEditBuild($childType, "${varName}_z_$fld", + $content .= fieldEditBuild($childType, "${varName}_zZ_$fld", $varValue->{$fld}, $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); @@ -1206,7 +1206,7 @@ EOF # in %In, rather than the parsed values in $varValue. # This is so that the user's erroneous input is preserved. # - $varValue = $In{"v_z_$varName"} if ( defined($In{"v_z_$varName"}) ); + $varValue = $In{"v_zZ_$varName"} if ( defined($In{"v_zZ_$varName"}) ); } if ( defined($errors->{$varName}) ) { $content .= < + EOF } elsif ( $type->{type} eq "boolean" ) { # checkbox my $checked = "checked" if ( $varValue ); $content .= < + EOF } elsif ( $type->{type} eq "select" ) { $content .= < + EOF } $content .= "\n"; @@ -1286,7 +1286,7 @@ sub fieldErrorCheck if ( $type->{type} eq "list" ) { for ( my $i = 0 ; ; $i++ ) { - last if ( fieldErrorCheck($type->{child}, "${varName}_z_$i", $errors) ); + last if ( fieldErrorCheck($type->{child}, "${varName}_zZ_$i", $errors) ); } } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) { my(@order, $childType); @@ -1305,31 +1305,31 @@ sub fieldErrorCheck } else { $childType = $type->{childType}; } - $ret ||= fieldErrorCheck($childType, "${varName}_z_$fld", $errors); + $ret ||= fieldErrorCheck($childType, "${varName}_zZ_$fld", $errors); } return $ret; } else { - $In{"v_z_$varName"} = "0" if ( $type->{type} eq "boolean" - && $In{"v_z_$varName"} eq "" ); + $In{"v_zZ_$varName"} = "0" if ( $type->{type} eq "boolean" + && $In{"v_zZ_$varName"} eq "" ); - return 1 if ( !exists($In{"v_z_$varName"}) ); + return 1 if ( !exists($In{"v_zZ_$varName"}) ); - (my $var = $varName) =~ s/_z_/./g; + (my $var = $varName) =~ s/_zZ_/./g; if ( $type->{type} eq "integer" || $type->{type} eq "boolean" ) { - if ( $In{"v_z_$varName"} !~ /^-?\d+\s*$/s - && $In{"v_z_$varName"} ne "" ) { + if ( $In{"v_zZ_$varName"} !~ /^-?\d+\s*$/s + && $In{"v_zZ_$varName"} ne "" ) { $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_an_integer}}"); } } elsif ( $type->{type} eq "float" ) { - if ( $In{"v_z_$varName"} !~ /^-?\d*(\.\d*)?\s*$/s - && $In{"v_z_$varName"} ne "" ) { + if ( $In{"v_zZ_$varName"} !~ /^-?\d*(\.\d*)?\s*$/s + && $In{"v_zZ_$varName"} ne "" ) { $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_real_valued_number}}"); } } elsif ( $type->{type} eq "shortlist" ) { - my @vals = split(/[,\s]+/, $In{"v_z_$varName"}); + my @vals = split(/[,\s]+/, $In{"v_zZ_$varName"}); for ( my $i = 0 ; $i < @vals ; $i++ ) { if ( $type->{child} eq "integer" && $vals[$i] !~ /^-?\d+\s*$/s @@ -1346,7 +1346,7 @@ sub fieldErrorCheck } elsif ( $type->{type} eq "select" ) { my $match = 0; foreach my $option ( @{$type->{values}} ) { - if ( $In{"v_z_$varName"} eq $option ) { + if ( $In{"v_zZ_$varName"} eq $option ) { $match = 1; last; } @@ -1354,7 +1354,7 @@ sub fieldErrorCheck $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_valid_option}}") if ( !$match ); } elsif ( $type->{type} eq "execPath" ) { - if ( $In{"v_z_$varName"} ne "" && !-x $In{"v_z_$varName"} ) { + if ( $In{"v_zZ_$varName"} ne "" && !-x $In{"v_zZ_$varName"} ) { $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_executable_program}}"); } } else { @@ -1395,7 +1395,7 @@ sub fieldInputParse $$value = []; for ( my $i = 0 ; ; $i++ ) { my $val; - last if ( fieldInputParse($type->{child}, "${varName}_z_$i", \$val) ); + last if ( fieldInputParse($type->{child}, "${varName}_zZ_$i", \$val) ); push(@$$value, $val); } $$value = undef if ( $type->{undefIfEmpty} && @$$value == 0 ); @@ -1419,19 +1419,19 @@ sub fieldInputParse } else { $childType = $type->{childType}; } - $ret ||= fieldInputParse($childType, "${varName}_z_$fld", \$val); + $ret ||= fieldInputParse($childType, "${varName}_zZ_$fld", \$val); last if ( $ret ); $$value->{$fld} = $val; } return $ret; } else { if ( $type->{type} eq "boolean" ) { - $$value = 0 + $In{"v_z_$varName"}; - } elsif ( !exists($In{"v_z_$varName"}) ) { + $$value = 0 + $In{"v_zZ_$varName"}; + } elsif ( !exists($In{"v_zZ_$varName"}) ) { return 1; } - my $v = $In{"v_z_$varName"}; + my $v = $In{"v_zZ_$varName"}; if ( $type->{type} eq "integer" ) { if ( $v =~ /^-?\d+\s*$/s || $v eq "" ) { @@ -1464,7 +1464,7 @@ sub fieldInputParse } } } else { - $$value = decode_utf8($In{"v_z_$varName"}); + $$value = decode_utf8($In{"v_zZ_$varName"}); $$value =~ s/\r\n/\n/g; } $$value = undef if ( $type->{undefIfEmpty} && $$value eq "" ); diff --git a/lib/BackupPC/FileZIO.pm b/lib/BackupPC/FileZIO.pm index c520725..ab30bae 100644 --- a/lib/BackupPC/FileZIO.pm +++ b/lib/BackupPC/FileZIO.pm @@ -173,13 +173,13 @@ sub read my $chr = substr($self->{dataIn}, 0, 1); $self->{inflateStart} = 0; - if ( $chr eq chr(0xd6) ) { + if ( $chr eq chr(0xd6) || $chr eq chr(0xd7) ) { # - # Flag 0xd6 means this is a compressed file with + # Flag 0xd6 or 0xd7 means this is a compressed file with # appended md4 block checksums for rsync. Change # the first byte back to 0x78 and proceed. # - ##print("Got 0xd6 block: normal\n"); + ##print("Got 0xd6/0xd7 block: normal\n"); substr($self->{dataIn}, 0, 1) = chr(0x78); } elsif ( $chr eq chr(0xb3) ) { # diff --git a/lib/BackupPC/Xfer/RsyncDigest.pm b/lib/BackupPC/Xfer/RsyncDigest.pm index b2d5f50..2790346 100644 --- a/lib/BackupPC/Xfer/RsyncDigest.pm +++ b/lib/BackupPC/Xfer/RsyncDigest.pm @@ -105,7 +105,7 @@ sub fileDigestIsCached binmode($fh); return -2 if ( sysread($fh, $data, 1) != 1 ); close($fh); - return $data eq chr(0xd6) ? 1 : 0; + return $data eq chr(0xd7) ? 1 : 0; } # @@ -121,7 +121,8 @@ sub fileDigestIsCached # sub digestAdd { - my($class, $file, $blockSize, $checksumSeed, $verify) = @_; + my($class, $file, $blockSize, $checksumSeed, $verify, + $protocol_version) = @_; my $retValue = 0; # @@ -140,6 +141,8 @@ sub digestAdd return -101 if ( !$RsyncLibOK ); my $digest = File::RsyncP::Digest->new; + $digest->protocol($protocol_version) + if ( defined($protocol_version) ); $digest->add(pack("V", $checksumSeed)) if ( $checksumSeed ); return -102 if ( !defined(my $fh = BackupPC::FileZIO->open($file, 0, 1)) ); @@ -171,7 +174,7 @@ sub digestAdd sysopen(my $fh2, $file, O_RDWR) || return -103; binmode($fh2); return -104 if ( sysread($fh2, $data, 1) != 1 ); - if ( $data ne chr(0x78) && $data ne chr(0xd6) ) { + if ( $data ne chr(0x78) && $data ne chr(0xd6) && $data ne chr(0xd7) ) { &$Log(sprintf("digestAdd: $file has unexpected first char 0x%x", ord($data))); return -105; @@ -183,7 +186,7 @@ sub digestAdd # # Verify the cached checksums # - return -107 if ( $data ne chr(0xd6) ); + return -107 if ( $data ne chr(0xd7) ); return -108 if ( sysread($fh2, $data3, length($data2) + 1) < 0 ); if ( $data2 eq $data3 ) { return 1; @@ -220,7 +223,7 @@ sub digestAdd } } return -113 if ( !defined(sysseek($fh2, 0, 0)) ); - return -114 if ( syswrite($fh2, chr(0xd6)) != 1 ); + return -114 if ( syswrite($fh2, chr(0xd7)) != 1 ); close($fh2); return $retValue; } @@ -254,9 +257,11 @@ sub digestStart name => $fileName, needMD4 => $needMD4, digest => File::RsyncP::Digest->new, + protocol_version => $protocol_version, }, $class; - $dg->{digest}->protocol($protocol_version); + $dg->{digest}->protocol($dg->{protocol_version}) + if ( defined($dg->{protocol_version}) ); if ( $fileSize > 0 && $compress && $doCache >= 0 ) { open(my $fh, "<", $fileName) || return -2; @@ -282,7 +287,7 @@ sub digestStart $blockSize || BackupPC::Xfer::RsyncDigest->blockSize( $fileSize, $defBlkSize), - $checksumSeed); + $checksumSeed, $dg->{protocol_version}); if ( $ret < 0 ) { &$Log("digestAdd($fileName) failed ($ret)"); } @@ -293,7 +298,7 @@ sub digestStart binmode($fh); return -5 if ( read($fh, $data, 1) != 1 ); } - if ( $ret >= 0 && $data eq chr(0xd6) ) { + if ( $ret >= 0 && $data eq chr(0xd7) ) { # # Looks like this file has cached checksums # Read the last 48 bytes: that's 2 file MD4s (32 bytes) @@ -343,6 +348,8 @@ sub digestStart return -9 if ( !defined($dg->{fh}) ); if ( $needMD4) { $dg->{csumDigest} = File::RsyncP::Digest->new; + $dg->{csumDigest}->protocol($dg->{protocol_version}) + if ( defined($dg->{protocol_version}) ); $dg->{csumDigest}->add(pack("V", $dg->{checksumSeed})); } } @@ -387,7 +394,13 @@ sub digestEnd if ( $dg->{cached} ) { close($dg->{fh}); - return $dg->{md4DigestOld} if ( $dg->{needMD4} ); + if ( $dg->{needMD4} ) { + if ( $dg->{protocol_version} <= 26 ) { + return $dg->{md4DigestOld}; + } else { + return $dg->{md4Digest}; + } + } } else { # # make sure we read the entire file for the file MD4 digest diff --git a/lib/BackupPC/Xfer/RsyncFileIO.pm b/lib/BackupPC/Xfer/RsyncFileIO.pm index 2d2f0b2..6a1460c 100644 --- a/lib/BackupPC/Xfer/RsyncFileIO.pm +++ b/lib/BackupPC/Xfer/RsyncFileIO.pm @@ -170,7 +170,8 @@ sub csumStart if ( $isCached || $isInvalid ) { my $ret = BackupPC::Xfer::RsyncDigest->digestAdd( $attr->{fullPath}, $blkSize, - $fio->{checksumSeed}, 1 # verify + $fio->{checksumSeed}, 1, # verify + $fio->{protocol_version} ); if ( $ret != 1 ) { $fio->log("Bad cached digest for $attr->{fullPath} ($ret);" @@ -186,19 +187,18 @@ sub csumStart (my $err, $fio->{csum}, my $blkSize) = BackupPC::Xfer::RsyncDigest->digestStart($attr->{fullPath}, $attr->{size}, 0, $defBlkSize, $fio->{checksumSeed}, - $needMD4, $attr->{compress}, 1, - $fio->{protocol_version}); - if ( $fio->{logLevel} >= 5 ) { - my($isCached, $invalid) = $fio->{csum}->isCached; - $fio->log("$attr->{fullPath} cache = $isCached," - . " invalid = $invalid, phase = $phase"); - } + $needMD4, $attr->{compress}, 1, $fio->{protocol_version}); if ( $err ) { $fio->log("Can't get rsync digests from $attr->{fullPath}" . " (err=$err, name=$f->{name})"); $fio->{stats}{errorCnt}++; return -1; } + if ( $fio->{logLevel} >= 5 ) { + my($isCached, $invalid) = $fio->{csum}->isCached; + $fio->log("$attr->{fullPath} cache = $isCached," + . " invalid = $invalid, phase = $phase"); + } return $blkSize; } @@ -323,12 +323,14 @@ sub viewCacheDir sub attribGetWhere { - my($fio, $f, $noCache) = @_; - my($dir, $fname, $share, $shareM, $partial, $attr); + my($fio, $f, $noCache, $fname) = @_; + my($dir, $share, $shareM, $partial, $attr); - $fname = $f->{name}; - $fname = "$fio->{xfer}{pathHdrSrc}/$fname" + if ( !defined($fname) ) { + $fname = $f->{name}; + $fname = "$fio->{xfer}{pathHdrSrc}/$fname" if ( defined($fio->{xfer}{pathHdrSrc}) ); + } $fname =~ s{//+}{/}g; if ( $fname =~ m{(.*)/(.*)}s ) { $shareM = $fio->{shareM}; @@ -388,15 +390,10 @@ sub attribGet return $attr; } $target = "/$target" if ( $target !~ /^\// ); - $fio->log("$attr->{fullPath}: redirecting to $target (will trim " - . "$fio->{xfer}{pathHdrSrc})") if ( $fio->{logLevel} >= 4 ); - $target =~ s/^\Q$fio->{xfer}{pathHdrSrc}//; + $fio->log("$attr->{fullPath}: redirecting to $target") + if ( $fio->{logLevel} >= 4 ); $target =~ s{^/+}{}; - # - # Note: overwrites name to point to real file - # - $f->{name} = $target; - ($attr) = $fio->attribGetWhere($f, 1); + ($attr) = $fio->attribGetWhere($f, 1, $target); $fio->log(" ... now got $attr->{fullPath}") if ( $fio->{logLevel} >= 4 ); } -- 2.20.1