#!/usr/bin/perl -w
#
# Copyright (c) 2005 - 2007 Miek Gieben; Mark J Hewitt
# See LICENSE for the license
# Hardlink script

use strict;

use Getopt::Long qw(:config no_ignore_case bundling);
use Sys::Syslog;
use File::Basename;
use File::Path;
use File::Copy;
use File::Copy::Recursive qw(dircopy rcopy fcopy);

# common functions
my $prefix="/usr";
require "/usr/share/rdup/shared.pl" or die "** Require \`shared.pl' failed";

my $ts = time;
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($ts);
my $progName = basename $0;
my ($help, $version, $verbose, $attr, $remote, $backupDir);

GetOptions("h" => \$help,
        "V" => \$version,
        "a" => \$attr, # extended attr support
        "c" => \$remote,
        "v" => \$verbose,
        "b=s" => \$backupDir);

usage() if $help;
version($progName) if $version;
die "** Need a -b option" if !$backupDir;
my $hostname = `hostname`; chomp $hostname;
my $attr_there = check_attr() if $attr;

# Statistics
my $ftsize = 0;        # Total file size
my $ireg = 0;        # Number of regular files
my $idir = 0;        # Number of directories
my $ilnk = 0;        # Number of symbolic links
my $irm  = 0;        # Number of files removed
my ($t, $bits, $uid, $gid, $psize, $fsize, $path);

syslog_open($progName);
if (!exist($backupDir)) {
        mkpath($backupDir) or die "** Cannot create `$backupDir': $!";
}

if ($remote) {
        syslog_log("%s %s", "Commencing remote backup to:" ,$backupDir);
        while (($_ = <STDIN>)) {
                chomp;
                ($t, $bits, $uid, $gid, $psize, $fsize) = split(" ", $_, 6);
                _mirror($backupDir);
        }
} else {
        syslog_log("%s %s", "Commencing local backup to:", $backupDir);
        while (($_ = <STDIN>)) {
                chomp;
                ($t, $bits, $uid, $gid, $psize, $fsize, $path) = split(" ", $_, 7);
                _mirror($backupDir);
        }
}
my $te = time;

syslog_log("Successfully finished backup to %s in %.2f m, %.2f MB", $backupDir,
        (($te - $ts) / 60), ($ftsize/1024/1024));
closelog;
printf STDERR "** #REG FILES  : %d\n", $ireg;
printf STDERR "** #DIRECTORIES: %d\n", $idir;
printf STDERR "** #LINKS      : %d\n", $ilnk;
printf STDERR "** #(RE)MOVED  : %d\n", $irm;
printf STDERR "** FILE SIZE   : %.1f MB\n", $ftsize/1024.0/1024.0;
printf STDERR "** STORED IN   : %s\n", $backupDir;
printf STDERR "** ELAPSED     : %.2f m\n", ($te - $ts) / 60.00;
rchmod(755, $backupDir);
exit 0;

sub _mirror {
        my $dir = $_[0];
        my $dump = substr($t, 0, 1);
        my $type = substr($t, 1, 1);
	my $target;
	my $ret;

        sanity_check($dump, $bits, $psize, $fsize, $uid, $gid);
        if ($remote) {
                $path = "";
                read STDIN, $path, $psize;
        }
        die "** Empty path"  if ($path eq "");
        print STDERR "$path\n" if $verbose;

        if ($dump eq '+') {        # add
                $target = "$dir/$path";
                if ($type eq '-') {        # REG
                        if (exist($target) == 1) {
                                if (-d _) {
                                        rmtree $target or warn "** Cannot remove: $target: $!";
                                } else {
                                        unlink $target or warn "** Cannot unlink: $target: $!";
                                }
                        }
                        if ($remote) {
                                open FILE, ">$target" or warn "** Cannot create: $target: $!";
                                if ($fsize != 0) {
                                        copyout($fsize, *FILE);
                                }
                                rchown($uid, $gid, $target);
                                chown_attr($attr_there, $uid, $gid, $target);
                                rchmod($bits, $target);
                                close FILE or warn "** Failed to close: $!";
                        } else {
                                $ret = copy($path, $target) or warn "** Copy failed: $! $path $target";
				if ($ret == 1) {
					rchown($uid, $gid, $target);
					chown_attr($attr_there, $uid, $gid, $target);
					rchmod($bits, $target);
				}
                        }
                        $ftsize += $fsize;
                        $ireg++;
                } elsif ($type eq 'd') {        # DIR
                        exist($target);  # side effects Go!
                        if (-f _ || -l _) {      # Type of entiry has changed
                                unlink $target or warn "** Cannot unlink: $target: $!";
                        }
                        mkpath($target) unless -d _;
                        rchown($uid, $gid, $target);
                        chown_attr($attr_there, $uid, $gid, $target);
                        if ($> != 0) {
                                if (length($bits) == 3 and substr($bits, 0, 1) ne "7") {
                                        print STDERR "** Chmod $target u+rxw\n";
                                        $bits = "7" . substr($bits, 1, 2);
                                }
                                if (length($bits) == 4 and substr($bits, 1, 1) ne "7") {
                                        print STDERR "** Chmod $target u+rxw\n";
                                        $bits = substr($bits, 0, 1) . "7" . substr($bits, 2, 2);
                                }
                        }
                        rchmod($bits, $target);
                        $idir++;
                } elsif ($type eq 'l') {        # LNK; target id the content
                        if (exist($target) == 1) {
                                if (-d _) {
                                        rmtree $target or warn "** Cannot remove: $target: $!";
                                } else {
                                        unlink $target or warn "** Cannot unlink: $target: $!";
                                }
                        }
                        if ($remote) {
                                my $linkTarget = "";
                                read STDIN, $linkTarget, $fsize;
                                symlink $linkTarget, $target or warn "** Cannot create link: $target -> $linkTarget: $!";
                        } else {
                                symlink(readlink($path), $target) or warn "** Cannot create link: $target -> $path: $!";
                        }
                        rchown($uid, $gid, $target);
                        chown_attr($attr_there, $uid, $gid, $target);
                        $ftsize += $fsize;
                        $ilnk++;
                }
        } else {        # Remove
                $target = "$dir/$path";
                if (exist($target)) {
                        if (-d _) {
                                rmtree $target or warn "** Cannot remove: $target: $!";
                        } else {
                                unlink $target or warn "** Cannot unlink: $target: $!";
                        }
                }
                $irm++;
        }
}

sub usage {
        print "$progName -b DIR [OPTIONS]\n\n";
        print "Update hardlinks according to the filelist of rdup\n\n";
        print "OPTIONS\n";
        print " -b DIR  use DIR as the backup directory\n";
        print " -a      write extended attributes r_uid/r_gid with uid/gid\n";
        print " -c      process the file content also (rdup -c), for remote backups\n";
        print " -v      print the files processed to standard error\n";
        print " -h      this help\n";
        print " -V      print version\n";
        exit 0;
}
