diff --git a/tests/test_encryption.py b/tests/test_encryption.py index 267b318..dbcb3a9 100644 --- a/tests/test_encryption.py +++ b/tests/test_encryption.py @@ -191,3 +191,36 @@ test_target1/test_source2/fs2 encryptionroot - test_target1/test_source2/fs2/sub encryptionroot - - """) + + + + def test_raw_invalid_snapshot(self): + """in raw mode, its not allowed to have any newer snaphots on target, #219""" + + self.prepare_encrypted_dataset("11111111", "test_source1/fs1/encryptedsource") + + with patch('time.strftime', return_value="test-20101111000000"): + self.assertFalse(ZfsAutobackup("test test_target1 --verbose --no-progress".split(" ")).run()) + + #this is invalid in raw mode + shelltest("zfs snapshot test_target1/test_source1/fs1/encryptedsource@incompatible") + + with patch('time.strftime', return_value="test-20101111000001"): + #should fail because of incompatble snapshot + self.assertEqual(ZfsAutobackup("test test_target1 --verbose --no-progress --allow-empty".split(" ")).run(),1) + #should destroy incompatible and continue + self.assertFalse(ZfsAutobackup("test test_target1 --verbose --no-progress --no-snapshot --destroy-incompatible".split(" ")).run()) + + + r = shelltest("zfs get -r -t filesystem encryptionroot test_target1") + self.assertMultiLineEqual(r,""" +NAME PROPERTY VALUE SOURCE +test_target1 encryptionroot - - +test_target1/test_source1 encryptionroot - - +test_target1/test_source1/fs1 encryptionroot - - +test_target1/test_source1/fs1/encryptedsource encryptionroot test_target1/test_source1/fs1/encryptedsource - +test_target1/test_source1/fs1/sub encryptionroot - - +test_target1/test_source2 encryptionroot - - +test_target1/test_source2/fs2 encryptionroot - - +test_target1/test_source2/fs2/sub encryptionroot - - +""") diff --git a/tests/test_zfsautobackup32.py b/tests/test_zfsautobackup32.py index 70317eb..f59d6e3 100644 --- a/tests/test_zfsautobackup32.py +++ b/tests/test_zfsautobackup32.py @@ -105,3 +105,4 @@ test_target1/test_source2/fs2/sub@test-20101111000000 test_target1/test_source2/fs2/sub@test-20101111000001 """) + diff --git a/zfs_autobackup/ZfsDataset.py b/zfs_autobackup/ZfsDataset.py index d250429..c32debd 100644 --- a/zfs_autobackup/ZfsDataset.py +++ b/zfs_autobackup/ZfsDataset.py @@ -866,13 +866,16 @@ class ZfsDataset: return start_snapshot - def find_incompatible_snapshots(self, common_snapshot): + def find_incompatible_snapshots(self, common_snapshot, raw): """returns a list of snapshots that is incompatible for a zfs recv onto the common_snapshot. all direct followup snapshots with written=0 are compatible. + in raw-mode nothing is compatible. issue #219 + Args: :type common_snapshot: ZfsDataset + :type raw: bool """ ret = [] @@ -880,7 +883,7 @@ class ZfsDataset: if common_snapshot and self.snapshots: followup = True for snapshot in self.snapshots[self.find_snapshot_index(common_snapshot) + 1:]: - if not followup or int(snapshot.properties['written']) != 0: + if raw or not followup or int(snapshot.properties['written']) != 0: followup = False ret.append(snapshot) @@ -983,7 +986,7 @@ class ZfsDataset: else: return resume_token - def _plan_sync(self, target_dataset, also_other_snapshots, guid_check): + def _plan_sync(self, target_dataset, also_other_snapshots, guid_check, raw): """plan where to start syncing and what to sync and what to keep Args: @@ -991,13 +994,14 @@ class ZfsDataset: :type target_dataset: ZfsDataset :type also_other_snapshots: bool :type guid_check: bool + :type raw: bool """ # determine common and start snapshot target_dataset.debug("Determining start snapshot") common_snapshot = self.find_common_snapshot(target_dataset, guid_check=guid_check) start_snapshot = self.find_start_snapshot(common_snapshot, also_other_snapshots) - incompatible_target_snapshots = target_dataset.find_incompatible_snapshots(common_snapshot) + incompatible_target_snapshots = target_dataset.find_incompatible_snapshots(common_snapshot, raw) # let thinner decide whats obsolete on source source_obsoletes = [] @@ -1058,11 +1062,26 @@ class ZfsDataset: :type guid_check: bool """ - self.verbose("sending to {}".format(target_dataset)) + # self.verbose("-> {}".format(target_dataset)) + + #defaults for these settings if there is no encryption stuff going on: + send_properties = True + raw = False + write_embedded = True + + # source dataset encrypted? + if self.properties.get('encryption', 'off')!='off': + # user wants to send it over decrypted? + if decrypt: + # when decrypting, zfs cant send properties + send_properties=False + else: + # keep data encrypted by sending it raw (including properties) + raw=True (common_snapshot, start_snapshot, source_obsoletes, target_obsoletes, target_keeps, incompatible_target_snapshots) = \ - self._plan_sync(target_dataset=target_dataset, also_other_snapshots=also_other_snapshots, guid_check=guid_check) + self._plan_sync(target_dataset=target_dataset, also_other_snapshots=also_other_snapshots, guid_check=guid_check, raw=raw) # NOTE: we do this because we dont want filesystems to fillup when backups keep failing. # Also usefull with no_send to still cleanup stuff. @@ -1084,23 +1103,9 @@ class ZfsDataset: if rollback: target_dataset.rollback() - #defaults for these settings if there is no encryption stuff going on: - send_properties = True - raw = False - write_embedded = True (active_filter_properties, active_set_properties) = self.get_allowed_properties(filter_properties, set_properties) - # source dataset encrypted? - if self.properties.get('encryption', 'off')!='off': - # user wants to send it over decrypted? - if decrypt: - # when decrypting, zfs cant send properties - send_properties=False - else: - # keep data encrypted by sending it raw (including properties) - raw=True - # encrypt at target? if encrypt and not raw: # filter out encryption properties to let encryption on the target take place