1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
#!/usr/bin/perl
# this software is licensed for use under the Free Software Foundation's GPL v3.0 license, as retrieved
# from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this
# project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE.
use strict;
use warnings;
my $zfs = '/sbin/zfs';
my %args = getargs(@ARGV);
my $progversion = '1.4.7';
if ($args{'version'}) { print "$progversion\n"; exit 0; }
my $dataset = getdataset($args{'path'});
my %versions = getversions($args{'path'}, $dataset);
foreach my $version (sort { $versions{$a}{'mtime'} <=> $versions{$b}{'mtime'} } keys %versions) {
my $disptime = localtime($versions{$version}{'mtime'});
my $dispsize = humansize($versions{$version}{'size'});
print "$disptime\t$dispsize\t$version\n";
}
exit 0;
###################################################################
###################################################################
###################################################################
sub humansize {
my ($rawsize) = @_;
my $humansize;
if ($rawsize > 1024*1024*1024) {
$humansize = sprintf("%.1f",$rawsize/1024/1024/1024) . ' GB';
} elsif ($rawsize > 1024*1024) {
$humansize = sprintf("%.1f",$rawsize/1024/1024) . ' MB';
} elsif ($rawsize > 255) {
$humansize = sprintf("%.1f",$rawsize/1024) . ' KB';
} else {
$humansize = $rawsize . ' Bytes';
}
return $humansize;
}
sub getversions {
my ($path, $dataset) = @_;
my @snaps = findsnaps($dataset, $args{'path'});
my $snappath = '.zfs/snapshot';
my $relpath = $path;
$relpath =~ s/^$dataset\///;
my %versions;
foreach my $snap (@snaps) {
my $filename = "$dataset/$snappath/$snap/$relpath";
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# only push to the $versions hash if this size and mtime aren't already present (simple dedupe)
my $duplicate = 0;
foreach my $version (keys %versions) {
if ($versions{$version}{'size'} eq $size && $versions{$version}{'mtime'} eq $mtime) {
$duplicate = 1;
}
}
if (! $duplicate) {
$versions{$filename}{'size'} = $size;
$versions{$filename}{'mtime'} = $mtime;
}
}
return %versions;
}
sub findsnaps {
my ($dataset, $path) = @_;
my $snappath = '.zfs/snapshot';
my $relpath = $path;
$relpath =~ s/^$dataset//;
my @snaps;
opendir (my $dh, "$dataset/$snappath");
while (my $dir=(readdir $dh)) {
if ($dir ne '.' && $dir ne '..') { push @snaps, $dir; }
}
closedir $dh;
return @snaps;
}
sub getdataset {
my ($path) = @_;
open FH, "$zfs list -Ho mountpoint |";
my @datasets = <FH>;
close FH;
my @matchingdatasets;
foreach my $dataset (@datasets) {
chomp $dataset;
if ( $path =~ /^$dataset/ ) { push @matchingdatasets, $dataset; }
}
my $bestmatch = '';
foreach my $dataset (@matchingdatasets) {
if ( length $dataset > length $bestmatch ) { $bestmatch = $dataset; }
}
return $bestmatch;
}
sub getargs {
my @args = @_;
my %args;
my %novaluearg;
my %validarg;
push my @validargs, ('debug','version');
foreach my $item (@validargs) { $validarg{$item} = 1; }
push my @novalueargs, ('debug','version');
foreach my $item (@novalueargs) { $novaluearg{$item} = 1; }
while (my $rawarg = shift(@args)) {
my $arg = $rawarg;
my $argvalue;
if ($rawarg =~ /=/) {
# user specified the value for a CLI argument with =
# instead of with blank space. separate appropriately.
$argvalue = $arg;
$arg =~ s/=.*$//;
$argvalue =~ s/^.*=//;
}
if ($rawarg =~ /^--/) {
# doubledash arg
$arg =~ s/^--//;
if (! $validarg{$arg}) { die "ERROR: don't understand argument $rawarg.\n"; }
if ($novaluearg{$arg}) {
$args{$arg} = 1;
} else {
# if this CLI arg takes a user-specified value and
# we don't already have it, then the user must have
# specified with a space, so pull in the next value
# from the array as this value rather than as the
# next argument.
if ($argvalue eq '') { $argvalue = shift(@args); }
$args{$arg} = $argvalue;
}
} elsif ($arg =~ /^-/) {
# singledash arg
$arg =~ s/^-//;
if (! $validarg{$arg}) { die "ERROR: don't understand argument $rawarg.\n"; }
if ($novaluearg{$arg}) {
$args{$arg} = 1;
} else {
# if this CLI arg takes a user-specified value and
# we don't already have it, then the user must have
# specified with a space, so pull in the next value
# from the array as this value rather than as the
# next argument.
if ($argvalue eq '') { $argvalue = shift(@args); }
$args{$arg} = $argvalue;
}
} else {
# bare arg
$args{'path'} = $arg;
}
}
return %args;
}
|