Added an already running check
[zfs-nexenta/.git] / zfs-diff
1 #!/usr/bin/perl
2
3 use warnings;
4 use strict;
5 use File::Temp qw/ tempfile tempdir /;
6
7 my $SSLPATH = '/usr/bin/openssl';
8
9 #Usage
10 sub print_usage {
11   print "ZFS Snapshot diff\n";
12   print "\t$0 [-dhirv] <zfs shapshot name> [filename]\n\n";
13   print " -d  Display the lines that are different (diff output)\n";
14   print " -h  Display this usage\n";
15   print " -m  Use md5sum when checking for file differences";
16   print " -i  Ignore files that don't exist in the snapshot (only necessary for recursing)\n";
17   print " -r  Recursively diff every file in the snapshot (filename not required)\n";
18   print " -v  Verbose mode\n\n";
19
20   print " [filename] is the filename RELATIVE to the ZFS snapshot root. For example, if\n";
21   print " I had a filesystem snapshot called pool/data/zone\@initial. The filename '/etc/passwd'\n";
22   print " would refer to the filename /pool/data/zone/etc/passwd in the filesystem and filename\n";
23   print " /pool/data/zone/.zfs/snapshot/initial/etc/passwd in the snapshot.\n\n";
24
25   print " A couple of examples:\n";
26   print "\t$0 -v -r -i pool/zones/lava2019\@Fri\n";
27   print "\t\tChecks the current pool/zones/lava2019 filesystem against the snapshot\n";
28   print "\t\treturning the md5sum difference of any files (ignore files that don't\n";
29   print "\t\texist in the snapshot). With verbose mode\n\n";
30
31   print "\t$0 -d pool/zones/lava2019\@Mon /root/etc/passwd\n";
32   print "\t\tCheck the md5sum for /pool/zones/lava2019/root/etc/passwd and compare\n";
33   print "\t\tit to /pool/zones/lava2019/.zfs/snapshot/Mon/root/etc/passwd. Display\n";
34   print "\t\tthe lines that are different also.\n\n";
35
36   exit(0);
37 }
38
39 use Getopt::Long;
40 my %options = ();
41 my $verbose;
42
43 GetOptions("h" => \$options{help},
44            "r!" => \$options{recurse},
45            "d!" => \$options{diff},
46            "i!" => \$options{ignore},
47            "m!" => \$options{md5sum},
48            "v!" => \$verbose
49            );
50
51 if ($options{help}) {
52   print_usage();
53 }
54
55 if ($options{recurse}) {
56   recurse_diff(shift || die "Need a ZFS snapshot name\n");
57 } else {
58   my $zfsname = shift || die "Need a ZFS snapshot name\n";
59   my $file = shift || die "Need a filename\n";
60   diff_single_file($zfsname,$file);
61 }
62
63 exit(0);
64
65 sub recurse_diff {
66   my $zfssnap = shift;
67   print "Recursive diff on $zfssnap\n" if $verbose;
68
69   $zfssnap =~ /(.+)\@(.+)/i;
70   if(($1 eq "") || ($2 eq "")) { die "-E- Invalid snapshot name\n"; }
71   my $fsname = "/" . $1;
72   my $snapname = $2;
73   if(! -d $fsname && $fsname =~ /\/\S+?(\/.*)/) { $fsname = $1; }
74   elsif(! -d $fsname && $fsname =~ /\/\S+?/) { $fsname = "/"; }
75   print "Filesystem: $fsname, Snapshot: $snapname\n" if $verbose;
76
77   my $snappath = $fsname . "/.zfs/snapshot/" . $snapname . "/";
78   my $fspath = $fsname . "/";
79   $fspath =~ s/\/\//\//gi;
80   $snappath =~ s/\/\//\//gi;
81   print "Comparing: $fspath\nto: $snappath\n" if $verbose;
82
83   my $dir = tempdir( CLEANUP => 0 );
84   my ($fh, $filename) = tempfile( DIR => $dir );
85
86   print "-> Finding files in $fspath to compare against files in $snappath\n";
87   `find $fspath -name "*" -type f > $filename`;
88   print "-> Performing a diff operation on each file found\n";
89
90   my $num_files = `cat $filename | wc | awk '{print \$1}'`;
91   
92   foreach my $file (<$fh>) {
93     chomp($file);
94     $file =~ /(.*)\/(.*)/;
95     my $shortname = $2;
96     $file =~ /$fspath(.*)/;
97     my $diff = $snappath . $1;
98     if (!-e $diff) {
99       print "$file does not exist in snapshot\n" if !$options{ignore};
100       next;
101     }
102
103     if($options{md5sum}) { 
104         # do the md5 sums
105         my $orig = `$SSLPATH md5 $file`;
106         my $snap = `$SSLPATH md5 $diff`;
107         $orig =~ /[\s\S]+= (.+)/;
108         my $sum1 = $1;
109         $snap =~ /[\s\S]+= (.+)/;
110         my $sum2 = $1;
111         if ($sum1 ne $sum2) {
112             print "** $file is different\n";
113             print "** $orig** $snap" if $verbose;
114         }
115     } else {
116         my $differ = system("diff \"$file\" \"$diff\" > /dev/null 2>&1");
117         if($differ) { print "** $file is different\n"; }
118     }
119     if ($options{diff}) {
120       system("diff \"$file\" \"$diff\"");
121     }
122   }
123 }
124
125 sub diff_single_file {
126   my $zfssnap = shift;
127   my $filename = shift;
128   print "Single-file diff on $zfssnap, file: $filename\n" if $verbose;
129
130   $zfssnap =~ /(.+)\@(.+)/i;
131   my $fsname = "/" . $1 . "/";
132   my $snapname = $2;
133   if(! -d $fsname && $fsname =~ /\/\S+?(\/.*)/) { $fsname = $1; }
134   print "Filesystem: $fsname, Snapshot: $snapname\n" if $verbose;
135
136   my $fspath;
137   if($filename !~ /^\//) { $fspath = $ENV{'PWD'} . "/" . $filename; }
138   else { $fspath = $filename; }
139
140   my $snapfspath = $fspath;
141   $snapfspath =~ s/$fsname//g;
142   my $snappath = $fsname . "/.zfs/snapshot/" . $snapname . "/" . $snapfspath;
143   $fspath =~ s/\/\//\//gi;
144   $snappath =~ s/\/\//\//gi;
145   print "Comparing: $fspath\nto: $snappath\n" if $verbose;
146   if(! -f $fspath) { print "-E- Cannot find source file: $fspath\n"; exit 1; }
147   if(! -f $snappath) { print "-E- Cannot find source file: $snappath\n"; exit 1; }
148
149   if($options{md5sum}) { 
150       my $orig = `$SSLPATH md5 $fspath`;
151       my $snap = `$SSLPATH md5 $snappath`;
152       $orig =~ /[\s\S]+= (.+)/;
153       my $sum1 = $1;
154       $snap =~ /[\s\S]+= (.+)/;
155       my $sum2 = $1;
156       if ($sum1 ne $sum2) {
157           print "** Files are different\n";
158           print "** $orig** $snap" if $verbose;
159       }
160   } else {
161       my $differ = system("diff \"$fspath\" \"$snappath\" > /dev/null 2>&1");
162       if($differ) { print "** Files are different\n"; }
163   }
164   if ($options{diff}) {
165     system("diff \"$fspath\" \"$snappath\"");
166   }
167 }