Compare commits

...

6 Commits

Author SHA1 Message Date
Пётр 66fbfe06b4
Merge f70a961d79 into a6728e48de 2025-08-24 11:11:41 -04:00
Jim Salter a6728e48de
Merge pull request #996 from jjakob/fix_directtimeout
syncoid: fix directtimeout in directmbuffer mode
2025-08-24 10:56:44 -04:00
Jim Salter 393a4672e5
Merge pull request #1023 from phreaker0/fix-file-handle-conflict
fixed file handle conflict
2025-08-24 10:50:58 -04:00
Christoph Klaffl efd52f416d
fixed file handle conflict 2025-08-12 14:19:47 +02:00
Peter f70a961d79 Implement --restore-properties and --target-properties 2025-04-10 15:05:01 +02:00
Jernej Jakob 749490830f
syncoid: fix directtimeout in directmbuffer mode
If --insecure-direct-connection contained 4 parts (including the
',mbuffer' at the end), the 3rd part (timeout) was silently ignored
and left at the default 60s.

Do not ignore the timeout part even in directmbuffer mode.
2025-03-31 14:54:46 +02:00
2 changed files with 104 additions and 17 deletions

15
sanoid
View File

@ -1084,11 +1084,9 @@ sub init {
@datasets = getchilddatasets($config{$section}{'path'});
DATASETS: foreach my $dataset(@datasets) {
if (! @cachedatasets) {
push (@updatedatasets, $dataset);
push (@updatedatasets, "$dataset\n");
}
chomp $dataset;
if ($zfsRecursive) {
# don't try to take the snapshot ourself, recursive zfs snapshot will take care of that
$config{$dataset}{'autosnap'} = 0;
@ -1691,7 +1689,7 @@ sub getchilddatasets {
my $getchildrencmd = "$mysudocmd $zfs list -o name -t filesystem,volume -Hr $fs |";
if ($args{'debug'}) { print "DEBUG: getting list of child datasets on $fs using $getchildrencmd...\n"; }
open FH, $getchildrencmd;
my @children = <FH>;
chomp( my @children = <FH> );
close FH;
# parent dataset is the first element
@ -1781,25 +1779,26 @@ sub addcachedsnapshots {
copy($cache, "$cache.tmp") or die "Could not copy to $cache.tmp!\n";
open FH, ">> $cache.tmp" or die "Could not write to $cache.tmp!\n";
open my $fh, ">> $cache.tmp" or die "Could not write to $cache.tmp!\n";
while((my $snap, my $details) = each(%taken)) {
my @parts = split("@", $snap, 2);
my $suffix = $parts[1] . "\tcreation\t" . $details->{time} . "\t-";
my $dataset = $parts[0];
print FH "${dataset}\@${suffix}\n";
print $fh "${dataset}\@${suffix}\n";
if ($details->{recursive}) {
my @datasets = getchilddatasets($dataset);
foreach my $dataset(@datasets) {
print FH "${dataset}\@${suffix}\n";
print "${dataset}\@${suffix}\n";
print $fh "${dataset}\@${suffix}\n";
}
}
}
close FH;
close $fh;
# preserve mtime of cache for expire check
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($cache);

106
syncoid
View File

@ -27,7 +27,7 @@ GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsn
"no-clone-handling", "no-privilege-elevation", "force-delete", "no-rollback", "create-bookmark", "use-hold",
"pv-options=s" => \$pvoptions, "keep-sync-snap", "preserve-recordsize", "mbuffer-size=s" => \$mbuffer_size,
"delete-target-snapshots", "insecure-direct-connection=s", "preserve-properties",
"include-snaps=s@", "exclude-snaps=s@", "exclude-datasets=s@")
"include-snaps=s@", "exclude-snaps=s@", "exclude-datasets=s@", "target-properties=s@", "restore-properties")
or pod2usage(2);
my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set
@ -42,6 +42,12 @@ if (defined($args{'exclude'})) {
}
}
if (defined $args{'restore-properties'} && defined $args{'preserve-properties'}) {
writelog('WARN', "invalid argument combination, --restore-properties and --preserve-properties aren't compatible!");
pod2usage(2);
exit 127;
}
my @sendoptions = ();
if (length $args{'sendoptions'}) {
@sendoptions = parsespecialoptions($args{'sendoptions'});
@ -178,7 +184,7 @@ if (length $args{'insecure-direct-connection'}) {
$directlisten = $args{'insecure-direct-connection'};
}
if (scalar @parts == 3) {
if (scalar @parts >= 3) {
$directtimeout = $parts[2];
}
@ -912,14 +918,57 @@ sub runsynccmd {
# if no rollbacks are allowed, disable forced receive
if (!defined $args{'no-rollback'}) { $recvoptions .= ' -F'; }
if (defined $args{'preserve-properties'}) {
my %properties = getlocalzfsvalues($sourcehost,$sourcefs,$sourceisroot);
my %properties = getlocalzfsvalues($sourcehost,$sourcefs,$sourceisroot);
if (defined $args{'restore-properties'}) {
foreach my $key (keys %properties) {
my $value = $properties{$key};
writelog('DEBUG', "will set $key to $value ...");
my $pair = escapeshellparam("$key=$value");
$recvoptions .= " -o $pair";
my ($skey, $ikey) = split(/:/, $key);
if (!length($ikey)) {
my $skeypresent = 0;
foreach my $key1 (keys %properties) {
my ($skey1, $ikey1) = split(/:/, $key1);
if (length($ikey1)) {
if($ikey1 eq $key) {
$skeypresent = 1;
}
}
}
if (!$skeypresent) {
if(!istargetpropertypresent($key)) {
writelog('DEBUG', "will set $key to $value ...");
my $pair = escapeshellparam("$key=$value");
$recvoptions .= " -o $pair";
}
}
} else {
if(!istargetpropertypresent($ikey)) {
if($value eq "*default*") {
writelog('DEBUG', "will unset $ikey ...");
$recvoptions .= " -x $ikey";
} else {
writelog('DEBUG', "will set $ikey to $value ...");
my $pair = escapeshellparam("$ikey=$value");
$recvoptions .= " -o $pair";
}
}
}
}
}
if (defined $args{'preserve-properties'}) {
foreach my $key (keys %properties) {
my $value = $properties{$key};
if(!istargetpropertypresent($key)) {
writelog('DEBUG', "will set $key to $value ...");
my $pair = escapeshellparam("$key=$value");
$recvoptions .= " -o $pair";
} else {
my $newkey .= "syncoid:$key";
writelog('DEBUG', "will set $newkey to $value ...");
my $pair = escapeshellparam("$newkey=$value");
$recvoptions .= " -o $pair";
}
}
} elsif (defined $args{'preserve-recordsize'}) {
my $type = getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'type');
@ -929,6 +978,26 @@ sub runsynccmd {
}
}
if (defined $args{'target-properties'}) {
my $props = $args{'target-properties'};
foreach (@$props) {
my ($tkey, $tvalue) = split(/=/);
my $tpair = escapeshellparam("$tkey=$tvalue");
$recvoptions .= " -o $tpair";
my $localpresent = 0;
foreach my $key (keys %properties) {
my $value = $properties{$key};
if ($tkey eq $key) {
$localpresent = 1;
}
}
if(!$localpresent && defined $args{'preserve-properties'}) {
$tpair = escapeshellparam("syncoid:$tkey=*default*");
$recvoptions .= " -o $tpair";
}
}
}
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $sendsource";
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $targetfsescaped 2>&1";
@ -2331,6 +2400,23 @@ sub writelog {
}
}
sub istargetpropertypresent {
my ($propname) = @_;
if (defined $args{'target-properties'}) {
my $props = $args{'target-properties'};
foreach (@$props) {
my ($tkey, $tvalue) = split(/=/);
if ($propname =~ /$tkey/) {
writelog('DEBUG', "source property $propname will be overwritten by value set in target-properties /$_/");
return 1;
}
}
}
return 0;
}
sub snapisincluded {
my ($snapname) = @_;
@ -2414,7 +2500,9 @@ Options:
--create-bookmark Creates a zfs bookmark for the newest snapshot on the source after replication succeeds (only works with --no-sync-snap)
--use-hold Adds a hold to the newest snapshot on the source and target after replication succeeds and removes the hold after the next successful replication. The hold name includes the identifier if set. This allows for separate holds in case of multiple targets
--preserve-recordsize Preserves the recordsize on initial sends to the target
--preserve-properties Preserves locally set dataset properties similar to the zfs send -p flag but this one will also work for encrypted datasets in non raw sends
--preserve-properties Preserves locally set dataset properties similar to the zfs send -p flag but this one will also work for encrypted datasets in non raw sends. If --target-properties overrides locally set property, it will be saved with 'syncoid:' prefix on target dataset.
--restore-properties Works like --preserve-properties but also sets properties present locally with 'syncoid:' prefix on target dataset (without this prefix).
--target-properties Set properties on target dataset, for example --target-properties=canmount=noauto. Can be specified multiple times. The properties defined by this parameter override the properties set by --preserve-properties and --restore-properties.
--no-rollback Does not rollback snapshots on target (it probably requires a readonly target)
--delete-target-snapshots With this argument snapshots which are missing on the source will be destroyed on the target. Use this if you only want to handle snapshots on the source.
--exclude=REGEX DEPRECATED. Equivalent to --exclude-datasets, but will be removed in a future release. Ignored if --exclude-datasets is also provided.