Skip to content

Commit 97ed3fc

Browse files
committed
librbd: avoid data corruption on flatten when object map is inconsistent
By making flatten skip copyup in case the object is marked OBJECT_EXISTS or OBJECT_EXISTS_CLEAN, commit 40af4f8 ("librbd: flatten operation should use object map") introduced a critical regression. If the object map becomes inconsistent (e.g. because flatten gets interrupted by killing "rbd flatten" process or a client running on the clone crashes after updating the object map but before writing to the image), the following attempt to flatten would corrupt the clone if the copyup is actually still needed. By design, it's impossible to tell whether the object is "known to exist" based on the object map -- only telling whether the object is "known to NOT exist" is possible (i.e. only OBJECT_NONEXISTENT state is reliable). Negating OBJECT_NONEXISTENT tells that the object "may exist", not that the object is "known to exist". This is reflected in the name of object_may_exist() helper that was introduced together with the object map implementation. Something like object_may_not_exist() simply can't be constructed given the rest of librbd. This effectively reverts commits 4c86bcc ("librbd: add object_may_not_exist helper") and 40af4f8 ("librbd: flatten operation should use object map"). Fixes: https://tracker.ceph.com/issues/68998 Signed-off-by: Ilya Dryomov <[email protected]>
1 parent 892af8c commit 97ed3fc

File tree

3 files changed

+0
-36
lines changed

3 files changed

+0
-36
lines changed

src/librbd/ObjectMap.cc

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -106,32 +106,6 @@ bool ObjectMap<I>::object_may_exist(uint64_t object_no) const
106106
return exists;
107107
}
108108

109-
template <typename I>
110-
bool ObjectMap<I>::object_may_not_exist(uint64_t object_no) const
111-
{
112-
ceph_assert(ceph_mutex_is_locked(m_image_ctx.image_lock));
113-
114-
// Fall back to default logic if object map is disabled or invalid
115-
if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP,
116-
m_image_ctx.image_lock)) {
117-
return true;
118-
}
119-
120-
bool flags_set;
121-
int r = m_image_ctx.test_flags(m_image_ctx.snap_id,
122-
RBD_FLAG_OBJECT_MAP_INVALID,
123-
m_image_ctx.image_lock, &flags_set);
124-
if (r < 0 || flags_set) {
125-
return true;
126-
}
127-
128-
uint8_t state = (*this)[object_no];
129-
bool nonexistent = (state != OBJECT_EXISTS && state != OBJECT_EXISTS_CLEAN);
130-
ldout(m_image_ctx.cct, 20) << "object_no=" << object_no << " r="
131-
<< nonexistent << dendl;
132-
return nonexistent;
133-
}
134-
135109
template <typename I>
136110
bool ObjectMap<I>::update_required(const ceph::BitVector<2>::Iterator& it,
137111
uint8_t new_state) {

src/librbd/ObjectMap.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ class ObjectMap : public RefCountedObject {
6565
void close(Context *on_finish);
6666
bool set_object_map(ceph::BitVector<2> &target_object_map);
6767
bool object_may_exist(uint64_t object_no) const;
68-
bool object_may_not_exist(uint64_t object_no) const;
6968

7069
void aio_save(Context *on_finish);
7170
void aio_resize(uint64_t new_size, uint8_t default_object_state,

src/librbd/operation/FlattenRequest.cc

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,6 @@ class C_FlattenObject : public C_AsyncObjectThrottle<I> {
4949
return -ERESTART;
5050
}
5151

52-
{
53-
std::shared_lock image_lock{image_ctx.image_lock};
54-
if (image_ctx.object_map != nullptr &&
55-
!image_ctx.object_map->object_may_not_exist(m_object_no)) {
56-
// can skip because the object already exists
57-
return 1;
58-
}
59-
}
60-
6152
if (!io::util::trigger_copyup(
6253
&image_ctx, m_object_no, m_io_context, this)) {
6354
// stop early if the parent went away - it just means

0 commit comments

Comments
 (0)