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