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