)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"76f17b2fae92a73aa8c09ac090c9a37f9f91d4a1","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":6,"id":"7db6cbcb_e1610aa2","updated":"2026-08-31 21:02:56.000000000","message":"recheck","commit_id":"e563d96844af9288a9461cd728ea2751c154997e"},{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"919421413a0077d03516425a5509da410403e5e8","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":6,"id":"fd911366_44244f3e","updated":"2026-08-31 23:34:33.000000000","message":"recheck","commit_id":"e563d96844af9288a9461cd728ea2751c154997e"},{"author":{"_account_id":36686,"name":"Inyong Hong","display_name":"hongp","email":"inyong.hong@samsung.com","username":"hong-p"},"change_message_id":"58aa98d69b94ef46d41b1de798687ab830bbe6ab","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":9,"id":"17c4cc0d_9d2bb35a","updated":"2026-10-11 03:12:36.000000000","message":"I really appreciate your work. I just left a comment about a potential issue, so please take a look.","commit_id":"aab03497d362fc52aa3f3edef8295de0b1c6c84d"}],"cinder/backup/api.py":[{"author":{"_account_id":36686,"name":"Inyong Hong","display_name":"hongp","email":"inyong.hong@samsung.com","username":"hong-p"},"change_message_id":"58aa98d69b94ef46d41b1de798687ab830bbe6ab","unresolved":true,"context_lines":[{"line_number":677,"context_line":"            for key, value in (backup.metadata or {}).items()"},{"line_number":678,"context_line":"            if key in constants.RESERVED_BACKUP_METADATA_KEYS"},{"line_number":679,"context_line":"        }"},{"line_number":680,"context_line":"        if not reserved:"},{"line_number":681,"context_line":"            return metadata"},{"line_number":682,"context_line":"        overridden \u003d sorted(set(metadata) \u0026 set(reserved))"},{"line_number":683,"context_line":"        if overridden:"},{"line_number":684,"context_line":"            LOG.info(\u0027Ignoring user-supplied values for reserved backup \u0027"}],"source_content_type":"text/x-python","patch_set":9,"id":"b58315c7_c03d43c7","line":681,"range":{"start_line":680,"start_character":8,"end_line":681,"end_character":27},"updated":"2026-10-11 03:12:36.000000000","message":"This still allows users to inject `cbt_metadata` when the key is absent, potentially causing backup deletion to remove a snapshot that does not belong to the backup. It would be safer to reject reserved keys on both create and update while preserving existing Cinder-managed values.","commit_id":"aab03497d362fc52aa3f3edef8295de0b1c6c84d"},{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"a75677033e4c56ec9bc4fdc4f91a562a3642becc","unresolved":false,"context_lines":[{"line_number":677,"context_line":"            for key, value in (backup.metadata or {}).items()"},{"line_number":678,"context_line":"            if key in constants.RESERVED_BACKUP_METADATA_KEYS"},{"line_number":679,"context_line":"        }"},{"line_number":680,"context_line":"        if not reserved:"},{"line_number":681,"context_line":"            return metadata"},{"line_number":682,"context_line":"        overridden \u003d sorted(set(metadata) \u0026 set(reserved))"},{"line_number":683,"context_line":"        if overridden:"},{"line_number":684,"context_line":"            LOG.info(\u0027Ignoring user-supplied values for reserved backup \u0027"}],"source_content_type":"text/x-python","patch_set":9,"id":"9096c22a_5192f399","line":681,"range":{"start_line":680,"start_character":8,"end_line":681,"end_character":27},"in_reply_to":"b58315c7_c03d43c7","updated":"2026-10-11 04:17:28.000000000","message":"Good catch, thanks — and it\u0027s wider than the delete path: the parent\u0027s `cbt_metadata` also feeds `_cleanup_cbt_snapshots` after a successful incremental, so a planted value is reachable without deleting anything. Next PS will reject reserved keys on create and on update (an update may only echo the stored value, which keeps GET-modify-PUT working), verify in the manager that an adopted snapshot belongs to the backup\u0027s volume before deleting it, and tighten the driver contract so `delete_cbt_snapshot` must be scoped to volume — RBD already does this, the FlashArray and LVM changes will get the same guard.","commit_id":"aab03497d362fc52aa3f3edef8295de0b1c6c84d"}],"cinder/backup/chunkeddriver.py":[{"author":{"_account_id":36171,"name":"jayaanand borra","display_name":"jayaanand borra","email":"jayaanand.borra@netapp.com","username":"jayaanan","status":"netapp"},"change_message_id":"1487deaaf365a30bed253ed0a48c99144b0aa0ae","unresolved":true,"context_lines":[{"line_number":547,"context_line":"            cbt_meta[\u0027reference_snapshot_cinder_id\u0027] \u003d ("},{"line_number":548,"context_line":"                reference_cinder_snapshot_id)"},{"line_number":549,"context_line":"        meta \u003d dict(backup.metadata) if backup.metadata else {}"},{"line_number":550,"context_line":"        meta[\u0027cbt_metadata\u0027] \u003d json.dumps(cbt_meta, sort_keys\u003dTrue)"},{"line_number":551,"context_line":"        backup.metadata \u003d meta"},{"line_number":552,"context_line":"        backup.save()"},{"line_number":553,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"32d372ac_102a0acf","line":550,"updated":"2026-09-17 15:32:36.000000000","message":"Backup.metadata is returned by GET (cinder/api/v3/views/backups.py:32) and replaced by PATCH (cinder/api/v3/backups.py:57 → Backup.save() → backup_metadata_update(..., delete\u003dTrue) at cinder/objects/backup.py:179). Keys omitted from the PATCH are deleted (cinder/db/sqlalchemy/api.py:6709).\n\nIf the user updates metadata without cbt_metadata, it is gone. The parent then looks like a SHA backup. Promote-to-full at line 663 only runs when cbt_metadata is still present, so the next incremental calls _read_sha256file on a CBT parent with no SHA file and fails.","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"62cd71e53faecc3ef0b7fd21e59866898887db55","unresolved":false,"context_lines":[{"line_number":547,"context_line":"            cbt_meta[\u0027reference_snapshot_cinder_id\u0027] \u003d ("},{"line_number":548,"context_line":"                reference_cinder_snapshot_id)"},{"line_number":549,"context_line":"        meta \u003d dict(backup.metadata) if backup.metadata else {}"},{"line_number":550,"context_line":"        meta[\u0027cbt_metadata\u0027] \u003d json.dumps(cbt_meta, sort_keys\u003dTrue)"},{"line_number":551,"context_line":"        backup.metadata \u003d meta"},{"line_number":552,"context_line":"        backup.save()"},{"line_number":553,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"c54940a9_a4f58036","line":550,"in_reply_to":"32d372ac_102a0acf","updated":"2026-09-17 18:41:36.000000000","message":"Marked as resolved.","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":36171,"name":"jayaanand borra","display_name":"jayaanand borra","email":"jayaanand.borra@netapp.com","username":"jayaanan","status":"netapp"},"change_message_id":"1487deaaf365a30bed253ed0a48c99144b0aa0ae","unresolved":true,"context_lines":[{"line_number":588,"context_line":"            while remaining \u003e 0:"},{"line_number":589,"context_line":"                read_bytes \u003d min(self.chunk_size_bytes, remaining)"},{"line_number":590,"context_line":"                data \u003d volume_file.read(read_bytes)"},{"line_number":591,"context_line":"                if not data:"},{"line_number":592,"context_line":"                    break"},{"line_number":593,"context_line":"                self._backup_chunk(backup, container, data, offset,"},{"line_number":594,"context_line":"                                   object_meta, extra_metadata)"}],"source_content_type":"text/x-python","patch_set":8,"id":"60d4c097_7cfddf8e","line":591,"updated":"2026-09-17 15:32:36.000000000","message":"Empty data while remaining \u003e 0 means EOF before the extent is done. This break only leaves the inner loop; the backup still finalizes and becomes available with missing bytes.\n\nA short non-empty read is fine (remaining -\u003d len(data)). Empty read with remaining \u003e 0 must raise. Also reject offset \u003c 0 or offset + length \u003e volume_size_bytes (volume_size_bytes is passed into _backup_cbt and unused).","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"62cd71e53faecc3ef0b7fd21e59866898887db55","unresolved":false,"context_lines":[{"line_number":588,"context_line":"            while remaining \u003e 0:"},{"line_number":589,"context_line":"                read_bytes \u003d min(self.chunk_size_bytes, remaining)"},{"line_number":590,"context_line":"                data \u003d volume_file.read(read_bytes)"},{"line_number":591,"context_line":"                if not data:"},{"line_number":592,"context_line":"                    break"},{"line_number":593,"context_line":"                self._backup_chunk(backup, container, data, offset,"},{"line_number":594,"context_line":"                                   object_meta, extra_metadata)"}],"source_content_type":"text/x-python","patch_set":8,"id":"dc27ee46_c3387be8","line":591,"in_reply_to":"60d4c097_7cfddf8e","updated":"2026-09-17 18:41:36.000000000","message":"Extents now have to satisfy ``0 \u003c\u003d offset`` and ``offset + length \u003c\u003d volume_size_bytes``; anything else raises ``InvalidBackup`` before we seek. This is where the previously-unused ``volume_size_bytes`` argument earns its keep.","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":36171,"name":"jayaanand borra","display_name":"jayaanand borra","email":"jayaanand.borra@netapp.com","username":"jayaanan","status":"netapp"},"change_message_id":"1487deaaf365a30bed253ed0a48c99144b0aa0ae","unresolved":true,"context_lines":[{"line_number":655,"context_line":"        # SHA-comparison entirely so we skip the sha256 file load."},{"line_number":656,"context_line":"        parent_backup_shafile \u003d None"},{"line_number":657,"context_line":"        parent_backup \u003d None"},{"line_number":658,"context_line":"        if backup.parent_id and not cbt_enabled:"},{"line_number":659,"context_line":"            # Backup.get_by_id always loads the metadata optional field, so"},{"line_number":660,"context_line":"            # parent_backup.metadata is populated for the CBT check below."},{"line_number":661,"context_line":"            parent_backup \u003d objects.Backup.get_by_id(self.context,"}],"source_content_type":"text/x-python","patch_set":8,"id":"f980b59b_31b4654f","line":658,"updated":"2026-09-17 15:32:36.000000000","message":"The backup.size \u003e parent_backup.size check is inside this block (line 686). backup.size is the volume size at create (cinder/backup/api.py:344). SHA incrementals refuse a larger volume. CBT incrementals skip the whole block.\n\nNew capacity is usually unallocated, so it is not in exists\u003dTrue extents. Restore applies parent then this incremental; the extra region is not written. Sparse-full zero-fill (line 974) uses backup[\u0027size\u0027] of that backup (the parent), not the dest size.","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"62cd71e53faecc3ef0b7fd21e59866898887db55","unresolved":false,"context_lines":[{"line_number":655,"context_line":"        # SHA-comparison entirely so we skip the sha256 file load."},{"line_number":656,"context_line":"        parent_backup_shafile \u003d None"},{"line_number":657,"context_line":"        parent_backup \u003d None"},{"line_number":658,"context_line":"        if backup.parent_id and not cbt_enabled:"},{"line_number":659,"context_line":"            # Backup.get_by_id always loads the metadata optional field, so"},{"line_number":660,"context_line":"            # parent_backup.metadata is populated for the CBT check below."},{"line_number":661,"context_line":"            parent_backup \u003d objects.Backup.get_by_id(self.context,"}],"source_content_type":"text/x-python","patch_set":8,"id":"6ef19b4a_5ddc16e4","line":658,"in_reply_to":"f980b59b_31b4654f","updated":"2026-09-17 18:41:36.000000000","message":"``parent_backup`` is now loaded for any ``parent_id``, on both paths, so the size guard below can run on CBT incrementals. ``sha_diff_parent`` is a separate variable for \"a SHA256 diff is actually possible\" — it used to be ``parent_backup``\u0027s truthiness, and hoisting the load alone made a CBT bootstrap crash in the full-scan loop.","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"}],"cinder/backup/manager.py":[{"author":{"_account_id":36171,"name":"jayaanand borra","display_name":"jayaanand borra","email":"jayaanand.borra@netapp.com","username":"jayaanan","status":"netapp"},"change_message_id":"1487deaaf365a30bed253ed0a48c99144b0aa0ae","unresolved":true,"context_lines":[{"line_number":939,"context_line":"        except Exception as e:"},{"line_number":940,"context_line":"            LOG.warning(\u0027CBT reference snapshot cleanup failed for backup \u0027"},{"line_number":941,"context_line":"                        \u0027%(backup)s (volume %(vol)s): %(err)s\u0027,"},{"line_number":942,"context_line":"                        {\u0027backup\u0027: backup.id, \u0027vol\u0027: volume.id, \u0027err\u0027: e})"},{"line_number":943,"context_line":""},{"line_number":944,"context_line":"    def _release_cbt_reference(self, context, backup):"},{"line_number":945,"context_line":"        \"\"\"Delete the CBT reference snapshot recorded on ``backup``."}],"source_content_type":"text/x-python","patch_set":8,"id":"8c0fde70_15a8ec2e","line":942,"updated":"2026-09-17 15:32:36.000000000","message":"_release_cbt_reference sets volume \u003d None on VolumeNotFound (line 966). If cinder_id is set and delete_snapshot raises anything other than SnapshotNotFound, this log formats volume.id and raises AttributeError. That will contradict \" \"Best-effort delete of a CBT reference snapshot; never raises\"","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"62cd71e53faecc3ef0b7fd21e59866898887db55","unresolved":false,"context_lines":[{"line_number":939,"context_line":"        except Exception as e:"},{"line_number":940,"context_line":"            LOG.warning(\u0027CBT reference snapshot cleanup failed for backup \u0027"},{"line_number":941,"context_line":"                        \u0027%(backup)s (volume %(vol)s): %(err)s\u0027,"},{"line_number":942,"context_line":"                        {\u0027backup\u0027: backup.id, \u0027vol\u0027: volume.id, \u0027err\u0027: e})"},{"line_number":943,"context_line":""},{"line_number":944,"context_line":"    def _release_cbt_reference(self, context, backup):"},{"line_number":945,"context_line":"        \"\"\"Delete the CBT reference snapshot recorded on ``backup``."}],"source_content_type":"text/x-python","patch_set":8,"id":"91876fad_bbff39b6","line":942,"in_reply_to":"8c0fde70_15a8ec2e","updated":"2026-09-17 18:41:36.000000000","message":"Done","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":36171,"name":"jayaanand borra","display_name":"jayaanand borra","email":"jayaanand.borra@netapp.com","username":"jayaanan","status":"netapp"},"change_message_id":"1487deaaf365a30bed253ed0a48c99144b0aa0ae","unresolved":true,"context_lines":[{"line_number":961,"context_line":"        if not backend_id and not cinder_id:"},{"line_number":962,"context_line":"            return"},{"line_number":963,"context_line":"        try:"},{"line_number":964,"context_line":"            volume \u003d objects.Volume.get_by_id(context, backup.volume_id)"},{"line_number":965,"context_line":"        except exception.VolumeNotFound:"},{"line_number":966,"context_line":"            volume \u003d None"},{"line_number":967,"context_line":"        self._delete_reference_snapshot(context, volume, backend_id,"}],"source_content_type":"text/x-python","patch_set":8,"id":"aaeceee6_e0add2d8","line":964,"updated":"2026-09-17 15:32:36.000000000","message":"volume_get(context, volume_id) (cinder/db/sqlalchemy/api.py:2767) does not take read_deleted. Passing read_deleted\u003d\u0027yes\u0027 into Volume.get_by_id is a TypeError.\n\nCinder allows deleting a volume while backups remain. Available-volume CBT refs are backend-only (cinder_id is None). Then backup-delete: VolumeNotFound → volume \u003d None → line 934 elif backend_id and volume is not None is skipped → array snapshot never deleted.","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"},{"author":{"_account_id":13425,"name":"Simon Dodsley","email":"simon@everpuredata.com","username":"sdodsley"},"change_message_id":"62cd71e53faecc3ef0b7fd21e59866898887db55","unresolved":false,"context_lines":[{"line_number":961,"context_line":"        if not backend_id and not cinder_id:"},{"line_number":962,"context_line":"            return"},{"line_number":963,"context_line":"        try:"},{"line_number":964,"context_line":"            volume \u003d objects.Volume.get_by_id(context, backup.volume_id)"},{"line_number":965,"context_line":"        except exception.VolumeNotFound:"},{"line_number":966,"context_line":"            volume \u003d None"},{"line_number":967,"context_line":"        self._delete_reference_snapshot(context, volume, backend_id,"}],"source_content_type":"text/x-python","patch_set":8,"id":"4bb1e479_f6ee4474","line":964,"in_reply_to":"aaeceee6_e0add2d8","updated":"2026-09-17 18:41:36.000000000","message":"Done","commit_id":"91919c6ff6b5dd4cb3818651d6b405216fbe6c59"}]}
