5 use File::Temp qw/ tempfile tempdir /;
7 my $SSLPATH = '/usr/bin/openssl';
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";
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";
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";
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";
43 GetOptions("h" => \$options{help},
44 "r!" => \$options{recurse},
45 "d!" => \$options{diff},
46 "i!" => \$options{ignore},
47 "m!" => \$options{md5sum},
55 if ($options{recurse}) {
56 recurse_diff(shift || die "Need a ZFS snapshot name\n");
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);
67 print "Recursive diff on $zfssnap\n" if $verbose;
69 $zfssnap =~ /(.+)\@(.+)/i;
70 if(($1 eq "") || ($2 eq "")) { die "-E- Invalid snapshot name\n"; }
71 my $fsname = "/" . $1;
73 if(! -d $fsname && $fsname =~ /\/\S+?(\/.*)/) { $fsname = $1; }
74 elsif(! -d $fsname && $fsname =~ /\/\S+?/) { $fsname = "/"; }
75 print "Filesystem: $fsname, Snapshot: $snapname\n" if $verbose;
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;
83 my $dir = tempdir( CLEANUP => 0 );
84 my ($fh, $filename) = tempfile( DIR => $dir );
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";
90 my $num_files = `cat $filename | wc | awk '{print \$1}'`;
92 foreach my $file (<$fh>) {
94 $file =~ /(.*)\/(.*)/;
96 $file =~ /$fspath(.*)/;
97 my $diff = $snappath . $1;
99 print "$file does not exist in snapshot\n" if !$options{ignore};
103 if($options{md5sum}) {
105 my $orig = `$SSLPATH md5 $file`;
106 my $snap = `$SSLPATH md5 $diff`;
107 $orig =~ /[\s\S]+= (.+)/;
109 $snap =~ /[\s\S]+= (.+)/;
111 if ($sum1 ne $sum2) {
112 print "** $file is different\n";
113 print "** $orig** $snap" if $verbose;
116 my $differ = system("diff \"$file\" \"$diff\" > /dev/null 2>&1");
117 if($differ) { print "** $file is different\n"; }
119 if ($options{diff}) {
120 system("diff \"$file\" \"$diff\"");
125 sub diff_single_file {
127 my $filename = shift;
128 print "Single-file diff on $zfssnap, file: $filename\n" if $verbose;
130 $zfssnap =~ /(.+)\@(.+)/i;
131 my $fsname = "/" . $1 . "/";
133 if(! -d $fsname && $fsname =~ /\/\S+?(\/.*)/) { $fsname = $1; }
134 print "Filesystem: $fsname, Snapshot: $snapname\n" if $verbose;
137 if($filename !~ /^\//) { $fspath = $ENV{'PWD'} . "/" . $filename; }
138 else { $fspath = $filename; }
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; }
149 if($options{md5sum}) {
150 my $orig = `$SSLPATH md5 $fspath`;
151 my $snap = `$SSLPATH md5 $snappath`;
152 $orig =~ /[\s\S]+= (.+)/;
154 $snap =~ /[\s\S]+= (.+)/;
156 if ($sum1 ne $sum2) {
157 print "** Files are different\n";
158 print "** $orig** $snap" if $verbose;
161 my $differ = system("diff \"$fspath\" \"$snappath\" > /dev/null 2>&1");
162 if($differ) { print "** Files are different\n"; }
164 if ($options{diff}) {
165 system("diff \"$fspath\" \"$snappath\"");