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
|
#!/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.
$::VERSION = '2.3.0';
use strict;
use warnings;
use Getopt::Long qw(:config auto_version auto_help);
use Pod::Usage;
my $zfs = 'zfs';
my %args = ('path' => '');
GetOptions(\%args, "path=s") or pod2usage(2);
if ($args{'path'} eq '') {
if (scalar(@ARGV) < 1) {
warn "file path missing!\n";
pod2usage(2);
exit 127;
} else {
$args{'path'} = $ARGV[0];
}
}
# resolve given path to a canonical one
$args{'path'} = Cwd::realpath($args{'path'});
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);
if (!defined $size) {
next;
}
# 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;
}
}
my $filename = "$dataset/$relpath";
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
if (defined $size) {
$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 -H -t filesystem -o mountpoint,mounted |";
my @datasets = <FH>;
close FH;
my @matchingdatasets;
foreach my $dataset (@datasets) {
chomp $dataset;
my ($mountpoint, $mounted) = ($dataset =~ m/([^\t]*)\t*(.*)/);
if ($mounted ne "yes") {
next;
}
if ( $path =~ /^$mountpoint/ ) { push @matchingdatasets, $mountpoint; }
}
my $bestmatch = '';
foreach my $dataset (@matchingdatasets) {
if ( length $dataset > length $bestmatch ) { $bestmatch = $dataset; }
}
return $bestmatch;
}
__END__
=head1 NAME
findoid - ZFS file version listing tool
=head1 SYNOPSIS
findoid [options] FILE
FILE local path to file for version listing
Options:
--path=FILE alternative to specify file path to list versions for
--help Prints this helptext
--version Prints the version number
|