]> git.sur5r.net Git - bacula/bacula/blob - regress/scripts/diff.pl
regress: add new diff tool in debug mode
[bacula/bacula] / regress / scripts / diff.pl
1 #!/usr/bin/perl -w
2
3 =head1 NAME
4
5     diff.pl -- Helper to diff files (rights, acl and content)
6
7 =head2 USAGE
8
9     diff.pl -s source -d dest [--acl | --attr | --wattr]
10
11 =cut
12
13 use strict;
14 use Cwd 'chdir';
15 use File::Find;
16 no warnings 'File::Find';
17 use Digest::MD5;
18 use Getopt::Long ;
19 use Pod::Usage;
20 use Data::Dumper;
21 use Cwd;
22 use POSIX qw/strftime/;
23
24 my ($src, $dst, $help, $acl, $attr, $wattr, $dest_attrib, $src_attrib);
25 my %src_attr; 
26 my %dst_attr;
27 my $hash;
28 my $ret=0;
29
30 GetOptions("src=s"   => \$src,  # source directory
31            "dst=s"   => \$dst,  # dest directory
32            "acl"     => \$acl,  # acl test
33            "attr"    => \$attr, # attributes test
34            "wattr"   => \$wattr,# windows attributes
35            "help"    => \$help,
36     ) or pod2usage(-verbose => 1, 
37                    -exitval => 1);
38 if (!$src or !$dst) {
39    pod2usage(-verbose => 1, 
40              -exitval => 1); 
41 }
42
43 if ($help) {
44     pod2usage(-verbose => 2, 
45               -exitval => 0);
46 }
47 my $md5 = Digest::MD5->new;
48
49 my $dir = getcwd;
50
51 chdir($src) or die "E: Can't access to $src";
52 $hash = \%src_attr;
53 find(\&wanted_src, '.');
54
55 if ($wattr) {    
56     $src_attrib = `attrib /D /S`;
57     $src_attrib = strip_base($src_attrib, $src);
58 }
59
60 chdir ($dir);
61
62 chdir($dst) or die "E: Can't access to $dst";
63 $hash = \%dst_attr;
64 find(\&wanted_src, '.');
65
66 if ($wattr) {    
67     $dest_attrib = `attrib /D /S`;
68     $dest_attrib = strip_base($dest_attrib, $dst);
69
70     if (lc($src_attrib) ne lc($dest_attrib)) {
71         $ret++;
72         print "E: Differences between windows attributes\n",
73               "$src_attrib\n=========\n$dest_attrib\n";
74     } 
75 }
76
77 #print Data::Dumper::Dumper(\%src_attr);
78 #print Data::Dumper::Dumper(\%dst_attr);
79
80 foreach my $f (keys %src_attr)
81 {
82     if (!defined $dst_attr{$f}) {
83         $ret++;
84         print "E: Can't find $f in dst\n";
85
86     } else {
87         compare($src_attr{$f}, $dst_attr{$f});
88     }
89     delete $src_attr{$f};
90     delete $dst_attr{$f};
91 }
92
93 foreach my $f (keys %dst_attr)
94 {
95     $ret++;
96     print "E: Can't find $f in src\n";
97 }
98
99 if ($ret) {
100     print "ERROR: found $ret error(s)\n";
101 }
102
103 exit $ret;
104
105 # convert \ to / and strip the path
106 sub strip_base
107 {
108     my ($data, $path) = @_;
109     $data =~ s!\\!/!sg;
110     $data =~ s!\Q$path!!sig;
111     return $data;
112 }
113
114 sub compare
115 {
116     my ($h1, $h2) = @_;
117     my ($f1, $f2) = ($h1->{file}, $h2->{file});
118     my %attr = %$h2;
119     foreach my $k (keys %$h1) {
120         if (!exists $h2->{$k}) {
121             $ret++;
122             print "E: Can't find $k for dest $f2 ($k=$h1->{$k})\n";
123         }
124         if (!defined $h2->{$k}) {
125             $ret++;
126             print "E: $k not found in destination ", $h1->{file}, "\n";
127             print Data::Dumper::Dumper($h1, $h2);
128         } elsif ($h2->{$k} ne $h1->{$k}) {
129             $ret++;
130             my ($val1, $val2) = ($h1->{$k}, $h2->{$k});
131             if ($k =~ /time/) {
132                 ($val1, $val2) = 
133                     (map { strftime('%F %T', localtime($_)) } ($val1, $val2));
134             }
135             print "E: src and dst $f2 differ on $k ($val1 != $val2)\n";
136         }
137         delete $attr{$k};
138     }
139
140     foreach my $k (keys %attr) {
141         $ret++;
142         print "E: Found $k on dst file and not on src ($k=$h2->{$k})\n";
143     }
144 }
145
146 sub wanted_src
147 {
148     my $f = $_;
149     if (-l $f) {
150         my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
151             $atime,$mtime,$ctime,$blksize,$blocks) = lstat($f);
152  
153         my $target = readlink($f);
154         $hash->{$File::Find::name} = {
155             nlink => $nlink,
156             uid => $uid,
157             gid => $gid,
158             mtime => 0,
159             target => $target,
160             type => 'l',
161             file => $File::Find::name,
162         };
163         return;
164     }
165
166     my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
167         $atime,$mtime,$ctime,$blksize,$blocks) = stat($f);
168     
169     if (-f $f)  {
170         $hash->{$File::Find::name} = {
171             mode => $mode,
172             nlink => $nlink,
173             uid => $uid,
174             gid => $gid,
175             size => $size,
176             mtime => $mtime,
177             type => 'f',
178             file => $File::Find::name,
179         };
180         $md5->reset;
181         open(FILE, '<', $f) or die "Can't open '$f': $!";
182         binmode(FILE);
183         $hash->{$File::Find::name}->{md5} = $md5->addfile(*FILE)->hexdigest;
184         close(FILE);
185         
186         if ($acl) {
187             $hash->{$File::Find::name}->{acl} = `getfacl "$f" 2>/dev/null`;
188         }
189         if ($attr) {
190             $hash->{$File::Find::name}->{attr} = `getfattr "$f" 2>/dev/null`;
191         }
192         
193     } elsif (-d $f) {
194         $hash->{$File::Find::name} = {
195             mode => $mode,
196             uid => $uid,
197             gid => $gid,
198             mtime => $mtime,
199             type => 'd',
200             file =>  $File::Find::name,
201         };
202         if ($acl) {
203             $hash->{$File::Find::name}->{acl} = `getfacl "$f" 2>/dev/null`;
204         }
205         if ($attr) {
206             $hash->{$File::Find::name}->{attr} = `getfattr "$f" 2>/dev/null`;
207         }
208         
209     } else {                # other than file and directory
210             
211     }
212 }