fix: prevent blob storage leak on folder deletion

- Add PG trigger trg_files_decrement_blob_ref (AFTER DELETE ON storage.files)
  that auto-decrements storage.blobs.ref_count for every deleted file row.
  Covers all paths: explicit DELETE, ON DELETE CASCADE, trash emptying.

- Remove manual remove_reference() call from delete_file() in
  file_blob_write_repository — trigger is now the single source of truth.

- Fix double-decrement bug in FileManagementService::delete_with_cleanup:
  was decrementing ref_count on trash (soft-delete) when the file row still
  existed, causing premature blob GC and potential data corruption on restore.

- Remove dead fields (file_read, dedup_service) from FileManagementService
  and simplify constructors — ref_count fully handled by PG trigger.
This commit is contained in:
Diocrafts
2026-02-22 22:07:46 +01:00
parent 35cfbba335
commit 2dd3dc0b54
5 changed files with 867 additions and 91 deletions
+25
View File
@@ -470,6 +470,31 @@ CREATE OR REPLACE VIEW storage.trash_items AS
SELECT 1 FROM storage.folders p
WHERE p.id = fo.parent_id AND p.is_trashed = TRUE));
-- ── Trigger: auto-decrement blob ref_count when a file row is deleted ──
--
-- This is the single source of truth for ref_count bookkeeping on deletion.
-- It fires for ALL delete paths: explicit DELETE, ON DELETE CASCADE from
-- folders, trash emptying, and any future code that removes file rows.
-- Rust code must NOT call remove_reference() after deleting a file row
-- to avoid double-decrementing.
CREATE OR REPLACE FUNCTION storage.decrement_blob_ref()
RETURNS trigger AS $$
BEGIN
UPDATE storage.blobs
SET ref_count = GREATEST(ref_count - 1, 0)
WHERE hash = OLD.blob_hash;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_files_decrement_blob_ref ON storage.files;
CREATE TRIGGER trg_files_decrement_blob_ref
AFTER DELETE ON storage.files
FOR EACH ROW
EXECUTE FUNCTION storage.decrement_blob_ref();
COMMENT ON FUNCTION storage.decrement_blob_ref() IS 'Auto-decrement blob ref_count when a file referencing it is deleted';
COMMENT ON TABLE storage.folders IS 'Virtual folder hierarchy with ltree — no physical directories on disk';
COMMENT ON TABLE storage.files IS 'File metadata pointing to content-addressable blobs';
COMMENT ON VIEW storage.trash_items IS 'Unified view of all trashed files and folders';