Skip to content

Commit 261592b

Browse files
author
Kent Overstreet
committed
bcachefs: Fix snapshotting a subvolume, then renaming it
Subvolume roots and the dirents that point to them are special; they don't obey the normal snapshot versioning rules because they cross snapshot boundaries. We don't keep around older versions of subvolume dirents on rename - we don't need to, because subvolume dirents are only visible in the parent subvolume, and we wouldn't be able to match up the different dirent and inode versions due to crossing the snapshot ID boundary. That means that when we rename a subvolume, that's been snapshotted, the older version of the subvolume root will become dangling - it won't have a dirent that points to it. That's expected, we just need to tell fsck that this is ok. Fixes: koverstreet/bcachefs#856 Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
1 parent 8dd3804 commit 261592b

File tree

1 file changed

+43
-1
lines changed

1 file changed

+43
-1
lines changed

fs/bcachefs/fsck.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,31 @@ static inline bool inode_should_reattach(struct bch_inode_unpacked *inode)
321321
inode->bi_subvol == BCACHEFS_ROOT_SUBVOL)
322322
return false;
323323

324+
/*
325+
* Subvolume roots are special: older versions of subvolume roots may be
326+
* disconnected, it's only the newest version that matters.
327+
*
328+
* We only keep a single dirent pointing to a subvolume root, i.e.
329+
* older versions of snapshots will not have a different dirent pointing
330+
* to the same subvolume root.
331+
*
332+
* This is because dirents that point to subvolumes are only visible in
333+
* the parent subvolume - versioning is not needed - and keeping them
334+
* around would break fsck, because when we're crossing subvolumes we
335+
* don't have a consistent snapshot ID to do check the inode <-> dirent
336+
* relationships.
337+
*
338+
* Thus, a subvolume root that's been renamed after a snapshot will have
339+
* a disconnected older version - that's expected.
340+
*
341+
* Note that taking a snapshot always updates the root inode (to update
342+
* the dirent backpointer), so a subvolume root inode with
343+
* BCH_INODE_has_child_snapshot is never visible.
344+
*/
345+
if (inode->bi_subvol &&
346+
(inode->bi_flags & BCH_INODE_has_child_snapshot))
347+
return false;
348+
324349
return !inode->bi_dir && !(inode->bi_flags & BCH_INODE_unlinked);
325350
}
326351

@@ -1007,6 +1032,23 @@ static int check_inode_dirent_inode(struct btree_trans *trans,
10071032
if (ret && !bch2_err_matches(ret, ENOENT))
10081033
return ret;
10091034

1035+
if ((ret || dirent_points_to_inode_nowarn(d, inode)) &&
1036+
inode->bi_subvol &&
1037+
(inode->bi_flags & BCH_INODE_has_child_snapshot)) {
1038+
/* Older version of a renamed subvolume root: we won't have a
1039+
* correct dirent for it. That's expected, see
1040+
* inode_should_reattach().
1041+
*
1042+
* We don't clear the backpointer field when doing the rename
1043+
* because there might be arbitrarily many versions in older
1044+
* snapshots.
1045+
*/
1046+
inode->bi_dir = 0;
1047+
inode->bi_dir_offset = 0;
1048+
*write_inode = true;
1049+
goto out;
1050+
}
1051+
10101052
if (fsck_err_on(ret,
10111053
trans, inode_points_to_missing_dirent,
10121054
"inode points to missing dirent\n%s",
@@ -1027,7 +1069,7 @@ static int check_inode_dirent_inode(struct btree_trans *trans,
10271069
inode->bi_dir_offset = 0;
10281070
*write_inode = true;
10291071
}
1030-
1072+
out:
10311073
ret = 0;
10321074
fsck_err:
10331075
bch2_trans_iter_exit(trans, &dirent_iter);

0 commit comments

Comments
 (0)