implemented parsing of provided zfs send/recv options and whitelisting for each use case as needed

This commit is contained in:
Christoph Klaffl 2019-01-31 18:14:35 +01:00
parent 9ec0c0bfb1
commit c5eebbe81d
No known key found for this signature in database
GPG Key ID: FC1C525C2A47CC28
1 changed files with 82 additions and 7 deletions

89
syncoid
View File

@ -27,14 +27,14 @@ GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsn
my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set
my $sendoptions = '';
my @sendoptions = ();
if (length $args{'sendoptions'}) {
$sendoptions = $args{'sendoptions'}
@sendoptions = parsespecialoptions($args{'sendoptions'});
}
my $recvoptions = '';
my @recvoptions = ();
if (length $args{'recvoptions'}) {
$recvoptions = $args{'recvoptions'}
@recvoptions = parsespecialoptions($args{'recvoptions'});
}
@ -352,6 +352,9 @@ sub syncdataset {
# with ZFS on Linux (possibly OpenZFS in general) when setting/unsetting readonly.
#my $originaltargetreadonly;
my $sendoptions = getoptionsline(\@sendoptions, ('D','L','P','R','c','e','p','v','w'));
my $recvoptions = getoptionsline(\@recvoptions, ('o','x','u','v'));
# sync 'em up.
if (! $targetexists) {
# do an initial sync from the oldest source snapshot
@ -470,6 +473,7 @@ sub syncdataset {
# and because this will ony resume the receive to the next
# snapshot, do a normal sync after that
if (defined($receivetoken)) {
$sendoptions = getoptionsline(\@sendoptions, ('P','e','v','w'));
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -t $receivetoken";
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
my $pvsize = getsendsize($sourcehost,"","",$sourceisroot,$receivetoken);
@ -619,9 +623,9 @@ sub syncdataset {
my $pvsize = 0;
my $disp_pvsize = "UNKNOWN";
$sendoptions = getoptionsline(\@sendoptions, ('L','c','e','w'));
if ($nextsnapshot) {
my $nextsnapshotescaped = escapeshellparam($nextsnapshot);
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$nextsnapshotescaped";
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot);
@ -654,6 +658,7 @@ sub syncdataset {
# do a normal replication if bookmarks aren't used or if previous
# bookmark replication was only done to the next oldest snapshot
if (!$bookmark || $nextsnapshot) {
$sendoptions = getoptionsline(\@sendoptions, ('D','L','P','R','c','e','p','v','w'));
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped";
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot);
@ -1398,6 +1403,12 @@ sub getsendsize {
$snaps = "-t $receivetoken";
}
my $sendoptions;
if (defined($receivetoken)) {
$sendoptions = getoptionsline(\@sendoptions, ('e', 'w'));
} else {
$sendoptions = getoptionsline(\@sendoptions, ('D','L','R','c','e','p','v','w'));
}
my $getsendsizecmd = "$sourcessh $mysudocmd $zfscmd send $sendoptions -nP $snaps";
if ($debug) { print "DEBUG: getting estimated transfer size from source $sourcehost using \"$getsendsizecmd 2>&1 |\"...\n"; }
@ -1479,6 +1490,70 @@ sub getreceivetoken() {
return
}
sub parsespecialoptions {
my ($line) = @_;
print("$line\n");
my @options = ();
my @values = split(/ /, $line);
my $optionValue = 0;
my $lastOption;
foreach my $value (@values) {
if ($optionValue ne 0) {
my %item = (
"option" => $lastOption,
"line" => "-$lastOption $value",
);
push @options, \%item;
$optionValue = 0;
next;
}
for my $char (split //, $value) {
if ($optionValue ne 0) {
return undef;
}
if ($char eq 'o' || $char eq 'x') {
$lastOption = $char;
$optionValue = 1;
} else {
my %item = (
"option" => $char,
"line" => "-$char",
);
push @options, \%item;
}
}
}
return @options;
}
sub getoptionsline {
my ($options_ref, @allowed) = @_;
my $line = '';
foreach my $value (@{ $options_ref }) {
if (@allowed) {
if (!grep( /^$$value{'option'}$/, @allowed) ) {
next;
}
}
$line = "$line$$value{'line'} ";
}
return $line;
}
__END__
=head1 NAME
@ -1509,8 +1584,8 @@ Options:
--no-clone-rollback Does not rollback clones on target
--no-rollback Does not rollback clones or snapshots on target (it probably requires a readonly target)
--exclude=REGEX Exclude specific datasets which match the given regular expression. Can be specified multiple times
--sendoptions=OPTIONS DANGER: Inject OPTIONS into zfs send, e.g. syncoid --sendoptions="-Lce" sets zfs send -Lce ...
--recvoptions=OPTIONS DANGER: Inject OPTIONS into zfs received, e.g. syncoid --recvoptions="-x property" sets zfs receive -x property ...
--sendoptions=OPTIONS Use advanced options for zfs send (the arguments are filterd as needed), e.g. syncoid --sendoptions="Lc e" sets zfs send -L -c -e ...
--recvoptions=OPTIONS Use advanced options for zfs receive (the arguments are filterd as needed), e.g. syncoid --recvoptions="ux recordsize o compression=lz4" sets zfs receive -u -x recordsize -o compression=lz4 ...
--sshkey=FILE Specifies a ssh public key to use to connect
--sshport=PORT Connects to remote on a particular port
--sshcipher|c=CIPHER Passes CIPHER to ssh to use a particular cipher set