Skip to content
This repository was archived by the owner on Jun 15, 2025. It is now read-only.

Commit 99ee152

Browse files
committed
Fix bug causing invalid parents to be used
Issues have come up where backup failed with different error messages such as: > ERROR: rename o258-8-0 -> bin failed. Directory not empty Investigation has shown that these are the result of unrelated existing snapshots of different subvolumes being used as clone sources. The scenario when such unrelated snapshots are taken into consideration is when the path of the subvolume to backup is a prefix of existing other subvolumes of which snapshots are located in the same repository. In particular, when backing up a btrfs subvolume that is located in the root ('/') of the file system hierarchy, all other existing snapshots were used as parents. This change fixes the problem by taking a trailing separator between the path encoded in a snapshot's name and its time into consideration when comparing snapshot names. This trailing separator, which cannot be contained in a snapshot's name, acts as a suffix making comparisons based on the base name safe.
1 parent a1d58a9 commit 99ee152

File tree

2 files changed

+35
-4
lines changed

2 files changed

+35
-4
lines changed

btrfs-backup/src/deso/btrfs/repository.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def _snapshotFiles(directory, extension, repository):
182182
# Mind the asterisk between the time and the extension. It is required
183183
# because we can have snapshots with equal time stamps which are then
184184
# numbered in an increasing fashion.
185-
expression = regex(r"^%s-.*-%s.*%s$" % (base, time, extension))
185+
expression = regex(r"^%s.*-%s.*%s$" % (base, time, extension))
186186

187187
# We have to rely on the '-v' parameter here to get a properly sorted
188188
# list and '-1' to display one file per line. We should not have to
@@ -292,9 +292,9 @@ def _snapshotBaseName(subvolume):
292292
# the ones in the middle with underscores to make names look less
293293
# confusing.
294294
path = subvolume.strip(sep).replace(sep, "_")
295-
return "%s-%s" % (name, path)
295+
return "%s-%s-" % (name, path)
296296
else:
297-
return name
297+
return "%s-" % name
298298

299299

300300
def _ensureUniqueName(snapshot, snapshots):
@@ -334,7 +334,7 @@ def _snapshotName(subvolume, snapshots):
334334
"""Retrieve a fully qualified, unique snapshot name."""
335335
name = _snapshotBaseName(subvolume)
336336
time = datetime.strftime(datetime.now(), _TIME_FORMAT)
337-
snapshot = "%s-%s" % (name, time)
337+
snapshot = "%s%s" % (name, time)
338338

339339
return _ensureUniqueName(snapshot, snapshots)
340340

btrfs-backup/src/deso/btrfs/test/testRepository.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,37 @@ def testRepositoryMultipleSubvolumeSync(self):
604604
self.assertTrue(isfile(dst.path(root["path"], ".ssh", "key.pub")))
605605

606606

607+
def testRepositorySubsumingSubvolumesAreTreatedProperly(self):
608+
"""Verify that subvolumes with names subsuming that of another one are backed up correctly."""
609+
with alias(self._mount) as m:
610+
snaps = make(m, "snapshots")
611+
backup = make(m, "backup")
612+
613+
src = Repository(snaps)
614+
dst = Repository(backup)
615+
616+
make(m, "root", subvol=True)
617+
make(m, "root", "file", data=b"a")
618+
619+
subvols = [m.path("root")]
620+
for i in range(2):
621+
# Create subvolumes with names that are a prefix of the
622+
# previously created one.
623+
make(m, "root%d" % i, subvol=True)
624+
make(m, "root%d" % i, "file", data=bytes("%d" % i, "utf-8"))
625+
626+
subvols += [m.path("root%d" % i)]
627+
628+
# Create backups of all the subvolumes. After this operation we
629+
# also have snapshots if them.
630+
syncRepos(subvols, src, dst)
631+
632+
src_snaps = src.snapshots()
633+
dst_snaps = dst.snapshots()
634+
parents = dst.parents(None, m.path("root"), src_snaps, dst_snaps)
635+
self.assertEqual(len(parents), 1)
636+
637+
607638
def testRepositoryUniqueSnapshotName(self):
608639
"""Verify that subvolume paths are correctly canonicalized for snapshot names."""
609640
with alias(self._mount) as m:

0 commit comments

Comments
 (0)