---
 src/backend/catalog/storage.c       |   3 +
 src/backend/storage/Makefile        |   5 +
 src/backend/storage/buffer/bufmgr.c |   2 +
 src/backend/storage/map/Makefile    |  19 ++
 src/backend/storage/map/map.c       | 162 +++++++++++++
 src/backend/storage/map/mapsuper.c  | 338 ++++++++++++++++++++++++++++
 src/backend/storage/map/meson.build |   6 +
 src/backend/storage/meson.build     |   3 +
 src/backend/storage/smgr/smgr.c     |  47 ++++
 src/backend/storage/smgr/umbra.c    | 158 ++++++++++++-
 src/include/storage/map.h           |  53 +++++
 src/include/storage/mapsuper.h      | 100 ++++++++
 src/include/storage/smgr.h          |   6 +
 src/include/storage/umbra.h         |   7 +
 14 files changed, 906 insertions(+), 3 deletions(-)
 create mode 100644 src/backend/storage/map/Makefile
 create mode 100644 src/backend/storage/map/map.c
 create mode 100644 src/backend/storage/map/mapsuper.c
 create mode 100644 src/backend/storage/map/meson.build
 create mode 100644 src/include/storage/map.h
 create mode 100644 src/include/storage/mapsuper.h

diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index e443a4993c..6b69329a52 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -150,6 +150,8 @@ RelationCreateStorage(RelFileLocator rlocator, char 
relpersistence,
        srel = smgropen(rlocator, procNumber);
        smgrcreate(srel, MAIN_FORKNUM, false);
 
+       if (needs_wal)
+               smgrcreaterelationmetadata(srel);
        if (needs_wal)
                log_smgrcreate(&srel->smgr_rlocator.locator, MAIN_FORKNUM);
 
@@ -1014,6 +1016,7 @@ smgr_redo(XLogReaderState *record)
                 * log as best we can until the drop is seen.
                 */
                smgrcreate(reln, MAIN_FORKNUM, true);
+               smgrcreaterelationmetadata(reln);
 
                /*
                 * Before we perform the truncation, update minimum recovery 
point to
diff --git a/src/backend/storage/Makefile b/src/backend/storage/Makefile
index 2afb42ca96..b07ba46dbb 100644
--- a/src/backend/storage/Makefile
+++ b/src/backend/storage/Makefile
@@ -20,4 +20,9 @@ SUBDIRS = \
        smgr \
        sync
 
+ifeq ($(with_umbra), yes)
+SUBDIRS += \
+       map
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/buffer/bufmgr.c 
b/src/backend/storage/buffer/bufmgr.c
index 3cc0b0bdd9..540f346d53 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -5505,6 +5505,8 @@ CreateAndCopyRelationData(RelFileLocator src_rlocator,
                                                                                
   permanent);
                }
        }
+
+       smgrcopyrelationmetadata(src_rel, dst_rel, relpersistence);
 }
 
 /* ---------------------------------------------------------------------
diff --git a/src/backend/storage/map/Makefile b/src/backend/storage/map/Makefile
new file mode 100644
index 0000000000..ee9603de14
--- /dev/null
+++ b/src/backend/storage/map/Makefile
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for storage/map (Umbra mapping subsystem)
+#
+# IDENTIFICATION
+#    src/backend/storage/map/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/storage/map
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = \
+       map.o \
+       mapsuper.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/map/map.c b/src/backend/storage/map/map.c
new file mode 100644
index 0000000000..563f38b21a
--- /dev/null
+++ b/src/backend/storage/map/map.c
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * map.c
+ *       Umbra metadata-fork disk layout helpers.
+ *
+ * This file contains address-translation and in-page access routines for the
+ * metadata fork disk layout.
+ *
+ * src/backend/storage/map/map.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/map.h"
+#include "storage/um_defs.h"
+
+void
+MapPageInit(MapPage *page)
+{
+       Assert(page != NULL);
+
+       MemSet(page->pblknos, 0xFF, sizeof(page->pblknos));
+}
+
+BlockNumber
+MapPageGetEntry(const MapPage *page, int entry_idx)
+{
+       Assert(page != NULL);
+
+       if (entry_idx < 0 || entry_idx >= MAP_ENTRIES_PER_PAGE)
+               elog(ERROR, "map entry index %d is out of range", entry_idx);
+
+       return page->pblknos[entry_idx];
+}
+
+void
+MapPageSetEntry(MapPage *page, int entry_idx, BlockNumber pblkno)
+{
+       Assert(page != NULL);
+
+       if (entry_idx < 0 || entry_idx >= MAP_ENTRIES_PER_PAGE)
+               elog(ERROR, "map entry index %d is out of range", entry_idx);
+
+       page->pblknos[entry_idx] = pblkno;
+}
+
+BlockNumber
+MapForkPageIndexToMapBlkno(ForkNumber forknum, BlockNumber fork_page_idx)
+{
+       uint64          group_no;
+       uint64          blkno64;
+
+       if (forknum == UMBRA_METADATA_FORKNUM)
+               elog(ERROR, "Umbra metadata fork cannot be addressed as a map 
target");
+
+       switch (forknum)
+       {
+               case FSM_FORKNUM:
+                       group_no = (uint64) fork_page_idx;
+                       blkno64 = (uint64) MAP_BLOCK_FIRST_GROUP +
+                               group_no * (uint64) MAP_GROUP_TOTAL_PAGES;
+                       break;
+
+               case VISIBILITYMAP_FORKNUM:
+                       group_no = (uint64) fork_page_idx;
+                       blkno64 = (uint64) MAP_BLOCK_FIRST_GROUP +
+                               group_no * (uint64) MAP_GROUP_TOTAL_PAGES +
+                               (uint64) MAP_GROUP_FSM_PAGES;
+                       break;
+
+               case MAIN_FORKNUM:
+               {
+                       uint64          group_page_idx = (uint64) fork_page_idx;
+
+                       group_no = group_page_idx / (uint64) 
MAP_GROUP_MAIN_PAGES;
+                       blkno64 = (uint64) MAP_BLOCK_FIRST_GROUP +
+                               group_no * (uint64) MAP_GROUP_TOTAL_PAGES +
+                               (uint64) MAP_GROUP_FSM_PAGES +
+                               (uint64) MAP_GROUP_VM_PAGES +
+                               (group_page_idx % (uint64) 
MAP_GROUP_MAIN_PAGES);
+                       break;
+               }
+
+               default:
+                       elog(ERROR, "unsupported fork number %d in map layout", 
(int) forknum);
+                       pg_unreachable();
+       }
+
+       if (blkno64 > (uint64) MaxBlockNumber)
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                errmsg("cannot address map page %u for fork 
%d",
+                                               fork_page_idx, forknum)));
+
+       return (BlockNumber) blkno64;
+}
+
+BlockNumber
+MapLblknoToMapBlkno(ForkNumber forknum, BlockNumber lblkno)
+{
+       BlockNumber     fork_page_idx;
+       uint64          entry64;
+
+       fork_page_idx = lblkno / MAP_ENTRIES_PER_PAGE;
+       entry64 = (uint64) MapForkPageIndexToMapBlkno(forknum, fork_page_idx) *
+               (uint64) MAP_ENTRIES_PER_PAGE +
+               (uint64) (lblkno % MAP_ENTRIES_PER_PAGE);
+
+       if (entry64 > (uint64) MaxBlockNumber)
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                errmsg("cannot address logical block %u for 
fork %d in map",
+                                               lblkno, forknum)));
+
+       return (BlockNumber) entry64;
+}
+
+bool
+MapDecodeMapBlkno(BlockNumber map_blkno, ForkNumber *forknum,
+                                 BlockNumber *fork_page_idx)
+{
+       uint64          offset;
+       uint64          group_no;
+       uint64          in_group;
+
+       Assert(forknum != NULL);
+       Assert(fork_page_idx != NULL);
+
+       if (map_blkno == MAP_BLOCK_SUPER || map_blkno < MAP_BLOCK_FIRST_GROUP)
+               return false;
+
+       offset = (uint64) (map_blkno - MAP_BLOCK_FIRST_GROUP);
+       group_no = offset / (uint64) MAP_GROUP_TOTAL_PAGES;
+       in_group = offset % (uint64) MAP_GROUP_TOTAL_PAGES;
+
+       if (in_group < (uint64) MAP_GROUP_FSM_PAGES)
+       {
+               *forknum = FSM_FORKNUM;
+               *fork_page_idx = (BlockNumber) group_no;
+               return true;
+       }
+
+       in_group -= (uint64) MAP_GROUP_FSM_PAGES;
+       if (in_group < (uint64) MAP_GROUP_VM_PAGES)
+       {
+               *forknum = VISIBILITYMAP_FORKNUM;
+               *fork_page_idx = (BlockNumber) group_no;
+               return true;
+       }
+
+       in_group -= (uint64) MAP_GROUP_VM_PAGES;
+       if (in_group < (uint64) MAP_GROUP_MAIN_PAGES)
+       {
+               *forknum = MAIN_FORKNUM;
+               *fork_page_idx = (BlockNumber)
+                       (group_no * (uint64) MAP_GROUP_MAIN_PAGES + in_group);
+               return true;
+       }
+
+       return false;
+}
diff --git a/src/backend/storage/map/mapsuper.c 
b/src/backend/storage/map/mapsuper.c
new file mode 100644
index 0000000000..b376d513fd
--- /dev/null
+++ b/src/backend/storage/map/mapsuper.c
@@ -0,0 +1,338 @@
+/*-------------------------------------------------------------------------
+ *
+ * mapsuper.c
+ *       Umbra metadata superblock helpers.
+ *
+ * This file contains on-disk superblock encoding and direct metadata-file I/O
+ * helpers.
+ *
+ * src/backend/storage/map/mapsuper.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/map.h"
+#include "storage/mapsuper.h"
+#include "storage/umbra.h"
+
+static void MapSBlockReportCorrupt(SMgrRelation reln, const char *reason);
+
+void
+MapSuperblockRefreshCRC(MapSuperblock *super)
+{
+       pg_crc32c       crc;
+
+       Assert(super != NULL);
+
+       INIT_CRC32C(crc);
+       COMP_CRC32C(crc, &super->data, offsetof(MapSuperblockData, crc));
+       FIN_CRC32C(crc);
+       super->data.crc = crc;
+}
+
+bool
+MapSuperblockCheckCRC(const MapSuperblock *super)
+{
+       pg_crc32c       crc;
+
+       Assert(super != NULL);
+
+       INIT_CRC32C(crc);
+       COMP_CRC32C(crc, &super->data, offsetof(MapSuperblockData, crc));
+       FIN_CRC32C(crc);
+
+       return crc == super->data.crc;
+}
+
+void
+MapSuperblockInit(MapSuperblock *super, uint32 flags)
+{
+       Assert(super != NULL);
+
+       MemSet(super, 0, sizeof(*super));
+
+       super->data.magic = MAP_SUPERBLOCK_MAGIC;
+       super->data.version = MAP_SUPERBLOCK_VERSION;
+       super->data.blcksz = BLCKSZ;
+       super->data.flags = flags;
+       super->data.next_free_phys_block_fsm = InvalidBlockNumber;
+       super->data.phys_capacity_fsm = InvalidBlockNumber;
+       super->data.next_free_phys_block_vm = InvalidBlockNumber;
+       super->data.phys_capacity_vm = InvalidBlockNumber;
+       super->data.logical_nblocks_fsm = InvalidBlockNumber;
+       super->data.logical_nblocks_vm = InvalidBlockNumber;
+       super->data.last_updated_lsn = InvalidXLogRecPtr;
+       super->data.crc = 0;
+}
+
+bool
+MapSuperblockHasValidIdentity(const MapSuperblock *super)
+{
+       Assert(super != NULL);
+
+       if (super->data.magic != MAP_SUPERBLOCK_MAGIC)
+               return false;
+       if (super->data.version != MAP_SUPERBLOCK_VERSION)
+               return false;
+       if (super->data.blcksz != BLCKSZ)
+               return false;
+
+       return true;
+}
+
+bool
+MapSuperblockIsValid(const MapSuperblock *super)
+{
+       Assert(super != NULL);
+
+       if (!MapSuperblockHasValidIdentity(super))
+               return false;
+
+       return MapSuperblockCheckCRC(super);
+}
+
+void
+MapSuperblockSetFlags(MapSuperblock *super, uint32 flags)
+{
+       Assert(super != NULL);
+
+       super->data.flags = flags;
+}
+
+uint32
+MapSuperblockGetFlags(const MapSuperblock *super)
+{
+       Assert(super != NULL);
+
+       return super->data.flags;
+}
+
+void
+MapSuperblockSetLastUpdatedLSN(MapSuperblock *super, XLogRecPtr lsn)
+{
+       Assert(super != NULL);
+
+       super->data.last_updated_lsn = lsn;
+}
+
+XLogRecPtr
+MapSuperblockGetLastUpdatedLSN(const MapSuperblock *super)
+{
+       Assert(super != NULL);
+
+       return super->data.last_updated_lsn;
+}
+
+BlockNumber
+MapSuperblockGetNextFreePhysBlock(const MapSuperblock *super, ForkNumber 
forknum)
+{
+       Assert(super != NULL);
+
+       switch (forknum)
+       {
+               case MAIN_FORKNUM:
+                       return super->data.next_free_phys_block_main;
+               case FSM_FORKNUM:
+                       return super->data.next_free_phys_block_fsm;
+               case VISIBILITYMAP_FORKNUM:
+                       return super->data.next_free_phys_block_vm;
+               default:
+                       elog(ERROR, "unsupported fork number for superblock: 
%d", forknum);
+       }
+
+       pg_unreachable();
+}
+
+void
+MapSuperblockSetNextFreePhysBlock(MapSuperblock *super, ForkNumber forknum,
+                                                                 BlockNumber 
blkno)
+{
+       Assert(super != NULL);
+
+       switch (forknum)
+       {
+               case MAIN_FORKNUM:
+                       super->data.next_free_phys_block_main = blkno;
+                       break;
+               case FSM_FORKNUM:
+                       super->data.next_free_phys_block_fsm = blkno;
+                       break;
+               case VISIBILITYMAP_FORKNUM:
+                       super->data.next_free_phys_block_vm = blkno;
+                       break;
+               default:
+                       elog(ERROR, "unsupported fork number for superblock: 
%d", forknum);
+       }
+}
+
+BlockNumber
+MapSuperblockGetPhysCapacity(const MapSuperblock *super, ForkNumber forknum)
+{
+       Assert(super != NULL);
+
+       switch (forknum)
+       {
+               case MAIN_FORKNUM:
+                       return super->data.phys_capacity_main;
+               case FSM_FORKNUM:
+                       return super->data.phys_capacity_fsm;
+               case VISIBILITYMAP_FORKNUM:
+                       return super->data.phys_capacity_vm;
+               default:
+                       elog(ERROR, "unsupported fork number for superblock: 
%d", forknum);
+       }
+
+       pg_unreachable();
+}
+
+void
+MapSuperblockSetPhysCapacity(MapSuperblock *super, ForkNumber forknum,
+                                                        BlockNumber blkno)
+{
+       Assert(super != NULL);
+
+       switch (forknum)
+       {
+               case MAIN_FORKNUM:
+                       super->data.phys_capacity_main = blkno;
+                       break;
+               case FSM_FORKNUM:
+                       super->data.phys_capacity_fsm = blkno;
+                       break;
+               case VISIBILITYMAP_FORKNUM:
+                       super->data.phys_capacity_vm = blkno;
+                       break;
+               default:
+                       elog(ERROR, "unsupported fork number for superblock: 
%d", forknum);
+       }
+}
+
+BlockNumber
+MapSuperblockGetLogicalNblocks(const MapSuperblock *super, ForkNumber forknum)
+{
+       Assert(super != NULL);
+
+       switch (forknum)
+       {
+               case MAIN_FORKNUM:
+                       return super->data.logical_nblocks_main;
+               case FSM_FORKNUM:
+                       return super->data.logical_nblocks_fsm;
+               case VISIBILITYMAP_FORKNUM:
+                       return super->data.logical_nblocks_vm;
+               default:
+                       elog(ERROR, "unsupported fork number for superblock: 
%d", forknum);
+       }
+
+       pg_unreachable();
+}
+
+void
+MapSuperblockSetLogicalNblocks(MapSuperblock *super, ForkNumber forknum,
+                                                          BlockNumber nblocks)
+{
+       Assert(super != NULL);
+
+       switch (forknum)
+       {
+               case MAIN_FORKNUM:
+                       super->data.logical_nblocks_main = nblocks;
+                       break;
+               case FSM_FORKNUM:
+                       super->data.logical_nblocks_fsm = nblocks;
+                       break;
+               case VISIBILITYMAP_FORKNUM:
+                       super->data.logical_nblocks_vm = nblocks;
+                       break;
+               default:
+                       elog(ERROR, "unsupported fork number for superblock: 
%d", forknum);
+       }
+}
+
+void
+MapSuperblockPackPage(const MapSuperblock *super, char page[BLCKSZ])
+{
+       Assert(super != NULL);
+       Assert(page != NULL);
+
+       MemSet(page, 0, BLCKSZ);
+       memcpy(page, super->padding, MAP_SUPERBLOCK_SIZE);
+}
+
+void
+MapSuperblockUnpackPage(MapSuperblock *super, const char page[BLCKSZ])
+{
+       Assert(super != NULL);
+       Assert(page != NULL);
+
+       memcpy(super->padding, page, MAP_SUPERBLOCK_SIZE);
+}
+
+bool
+MapSBlockRead(SMgrRelation reln, MapSuperblock *super)
+{
+       char            page[BLCKSZ];
+
+       Assert(reln != NULL);
+       Assert(super != NULL);
+
+       if (!UmMetadataExists(reln))
+               return false;
+
+       if (UmMetadataNblocks(reln) == 0)
+               return false;
+
+       UmMetadataRead(reln, MAP_BLOCK_SUPER, page);
+       MapSuperblockUnpackPage(super, page);
+
+       if (!MapSuperblockHasValidIdentity(super))
+               MapSBlockReportCorrupt(reln, "invalid identity");
+       if (!MapSuperblockCheckCRC(super))
+               MapSBlockReportCorrupt(reln, "CRC mismatch");
+
+       return true;
+}
+
+void
+MapSBlockWrite(SMgrRelation reln, const MapSuperblock *super, bool skipFsync)
+{
+       MapSuperblock write_super;
+       char            page[BLCKSZ];
+
+       Assert(reln != NULL);
+       Assert(super != NULL);
+
+       write_super = *super;
+       MapSuperblockRefreshCRC(&write_super);
+       MapSuperblockPackPage(&write_super, page);
+
+       if (!UmMetadataOpenOrCreate(reln, false, NULL))
+               elog(ERROR, "could not open Umbra metadata file for superblock 
write");
+
+       if (UmMetadataNblocks(reln) == 0)
+               UmMetadataExtend(reln, MAP_BLOCK_SUPER, page, skipFsync);
+       else
+               UmMetadataWrite(reln, MAP_BLOCK_SUPER, page, skipFsync);
+}
+
+void
+MapSBlockInitNew(SMgrRelation reln, uint32 flags, XLogRecPtr lsn, bool 
skipFsync)
+{
+       MapSuperblock super;
+
+       MapSuperblockInit(&super, flags);
+       MapSuperblockSetLastUpdatedLSN(&super, lsn);
+       MapSBlockWrite(reln, &super, skipFsync);
+}
+
+static void
+MapSBlockReportCorrupt(SMgrRelation reln, const char *reason)
+{
+       RelFileLocator rlocator = reln->smgr_rlocator.locator;
+
+       ereport(ERROR,
+                       (errcode(ERRCODE_DATA_CORRUPTED),
+                        errmsg("Umbra metadata superblock is corrupted for 
relation %u/%u/%u: %s",
+                                       rlocator.spcOid, rlocator.dbOid, 
rlocator.relNumber, reason)));
+}
diff --git a/src/backend/storage/map/meson.build 
b/src/backend/storage/map/meson.build
new file mode 100644
index 0000000000..0f780fe522
--- /dev/null
+++ b/src/backend/storage/map/meson.build
@@ -0,0 +1,6 @@
+# Copyright (c) 2022-2025, PostgreSQL Global Development Group
+
+backend_sources += files(
+  'map.c',
+  'mapsuper.c',
+)
diff --git a/src/backend/storage/meson.build b/src/backend/storage/meson.build
index 05637aa3a4..2f80f3f575 100644
--- a/src/backend/storage/meson.build
+++ b/src/backend/storage/meson.build
@@ -7,6 +7,9 @@ subdir('freespace')
 subdir('ipc')
 subdir('large_object')
 subdir('lmgr')
+if get_option('umbra').enabled()
+  subdir('map')
+endif
 subdir('page')
 subdir('smgr')
 subdir('sync')
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index a7b70d856c..c9a3ef6461 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -127,6 +127,13 @@ typedef struct f_smgr
                                                                  BlockNumber 
old_blocks, BlockNumber nblocks);
        void            (*smgr_immedsync) (SMgrRelation reln, ForkNumber 
forknum);
        void            (*smgr_registersync) (SMgrRelation reln, ForkNumber 
forknum);
+       void            (*smgr_create_relation_metadata) (SMgrRelation reln);
+       void            (*smgr_copy_relation_metadata) (SMgrRelation src,
+                                                                               
                SMgrRelation dst,
+                                                                               
                char relpersistence);
+       void            (*smgr_sync_relation_metadata) (SMgrRelation reln);
+       void            (*smgr_unlink_relation_metadata) (RelFileLocatorBackend 
rlocator,
+                                                                               
                  bool isRedo);
        int                     (*smgr_fd) (SMgrRelation reln, ForkNumber 
forknum, BlockNumber blocknum, uint32 *off);
 } f_smgr;
 
@@ -161,6 +168,10 @@ static const f_smgr smgrsw[] = {
                .smgr_truncate = mdtruncate,
                .smgr_immedsync = mdimmedsync,
                .smgr_registersync = mdregistersync,
+               .smgr_create_relation_metadata = NULL,
+               .smgr_copy_relation_metadata = NULL,
+               .smgr_sync_relation_metadata = NULL,
+               .smgr_unlink_relation_metadata = NULL,
                .smgr_fd = mdfd,
        },
 #ifdef USE_UMBRA
@@ -186,6 +197,10 @@ static const f_smgr smgrsw[] = {
                .smgr_truncate = umtruncate,
                .smgr_immedsync = umimmedsync,
                .smgr_registersync = umregistersync,
+               .smgr_create_relation_metadata = umcreaterelationmetadata,
+               .smgr_copy_relation_metadata = umcopyrelationmetadata,
+               .smgr_sync_relation_metadata = umsyncrelationmetadata,
+               .smgr_unlink_relation_metadata = umunlinkrelationmetadata,
                .smgr_fd = umfd,
        },
 #endif
@@ -529,6 +544,34 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool 
isRedo)
        RESUME_INTERRUPTS();
 }
 
+void
+smgrcreaterelationmetadata(SMgrRelation reln)
+{
+       if (smgrsw[reln->smgr_which].smgr_create_relation_metadata)
+               smgrsw[reln->smgr_which].smgr_create_relation_metadata(reln);
+}
+
+void
+smgrcopyrelationmetadata(SMgrRelation src, SMgrRelation dst, char 
relpersistence)
+{
+       if (smgrsw[dst->smgr_which].smgr_copy_relation_metadata)
+               smgrsw[dst->smgr_which].smgr_copy_relation_metadata(src, dst,
+                                                                               
                                        relpersistence);
+}
+
+void
+smgrsyncrelationmetadata(SMgrRelation reln)
+{
+       if (smgrsw[reln->smgr_which].smgr_sync_relation_metadata)
+               smgrsw[reln->smgr_which].smgr_sync_relation_metadata(reln);
+}
+
+void
+smgrunlinkrelationmetadata(RelFileLocatorBackend rlocator, bool isRedo)
+{
+       if (smgrsw[0].smgr_unlink_relation_metadata)
+               smgrsw[0].smgr_unlink_relation_metadata(rlocator, isRedo);
+}
 /*
  * smgrdosyncall() -- Immediately sync all forks of all given relations
  *
@@ -563,6 +606,8 @@ smgrdosyncall(SMgrRelation *rels, int nrels)
                        if (smgrsw[which].smgr_exists(rels[i], forknum))
                                smgrsw[which].smgr_immedsync(rels[i], forknum);
                }
+
+               smgrsyncrelationmetadata(rels[i]);
        }
 
        RESUME_INTERRUPTS();
@@ -643,6 +688,8 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
 
                for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
                        smgrsw[which].smgr_unlink(rlocators[i], forknum, 
isRedo);
+
+               smgrunlinkrelationmetadata(rlocators[i], isRedo);
        }
 
        pfree(rlocators);
diff --git a/src/backend/storage/smgr/umbra.c b/src/backend/storage/smgr/umbra.c
index 2c08231587..fc6e480276 100644
--- a/src/backend/storage/smgr/umbra.c
+++ b/src/backend/storage/smgr/umbra.c
@@ -4,8 +4,9 @@
  *       Umbra storage manager skeleton.
  *
  * This file establishes Umbra as a separate smgr implementation from md.c.
- * Data-fork operations remain md-backed here, while relation-local metadata
- * file operations go through umfile.
+ * maintains identity mapping state (logical block number == physical block
+ * number) in the relation-local metadata file while using md.c for data-fork
+ * I/O and umfile for metadata-file I/O.
  *
  * src/backend/storage/smgr/umbra.c
  *
@@ -13,7 +14,9 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_class.h"
 #include "storage/md.h"
+#include "storage/mapsuper.h"
 #include "storage/smgr.h"
 #include "storage/umfile.h"
 #include "storage/umbra.h"
@@ -24,7 +27,11 @@ typedef struct UmbraSmgrRelationState
        UmbraFileContext *filectx;
 } UmbraSmgrRelationState;
 
+static bool um_tracks_identity_metadata(ForkNumber forknum);
 static UmbraFileContext *um_relation_filectx(SMgrRelation reln);
+static void um_identity_update_metadata(SMgrRelation reln, ForkNumber forknum,
+                                                                               
BlockNumber nblocks, bool fork_exists,
+                                                                               
bool skipFsync);
 
 bool
 UmMetadataExists(SMgrRelation reln)
@@ -124,7 +131,7 @@ umdestroy(SMgrRelation reln)
 {
        UmbraSmgrRelationState *state = reln->smgr_private;
 
-       umfile_ctx_forget(reln->smgr_rlocator);
+       umfile_ctx_release(reln->smgr_rlocator);
 
        if (state != NULL)
        {
@@ -133,15 +140,94 @@ umdestroy(SMgrRelation reln)
        }
 }
 
+bool
+umisinternalfork(ForkNumber forknum)
+{
+       return forknum == UMBRA_METADATA_FORKNUM;
+}
+
+void
+umcreaterelationmetadata(SMgrRelation reln)
+{
+       bool            created = false;
+
+       if (!UmMetadataOpenOrCreate(reln, false, &created))
+               ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not create Umbra metadata fork 
for relation %u/%u/%u",
+                                               
reln->smgr_rlocator.locator.spcOid,
+                                               
reln->smgr_rlocator.locator.dbOid,
+                                               
reln->smgr_rlocator.locator.relNumber)));
+}
+
+void
+umcopyrelationmetadata(SMgrRelation src, SMgrRelation dst, char relpersistence)
+{
+       BlockNumber src_nblocks;
+       BlockNumber dst_nblocks;
+       PGIOAlignedBlock pagebuf;
+       bool            created = false;
+
+       if (relpersistence != RELPERSISTENCE_PERMANENT)
+               return;
+
+       if (!UmMetadataExists(src))
+               return;
+
+       if (!UmMetadataOpenOrCreate(dst, false, &created))
+               ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not create Umbra metadata fork 
for relation %u/%u/%u",
+                                               
dst->smgr_rlocator.locator.spcOid,
+                                               
dst->smgr_rlocator.locator.dbOid,
+                                               
dst->smgr_rlocator.locator.relNumber)));
+
+       src_nblocks = UmMetadataNblocks(src);
+       dst_nblocks = UmMetadataNblocks(dst);
+
+       for (BlockNumber blkno = 0; blkno < src_nblocks; blkno++)
+       {
+               UmMetadataRead(src, blkno, pagebuf.data);
+               if (blkno < dst_nblocks)
+                       UmMetadataWrite(dst, blkno, pagebuf.data, true);
+               else
+                       UmMetadataExtend(dst, blkno, pagebuf.data, true);
+       }
+
+       UmMetadataImmediateSync(dst);
+}
+
+void
+umsyncrelationmetadata(SMgrRelation reln)
+{
+       if (!UmMetadataExists(reln))
+               return;
+
+       UmMetadataImmediateSync(reln);
+}
+
+void
+umunlinkrelationmetadata(RelFileLocatorBackend rlocator, bool isRedo)
+{
+       umfile_ctx_forget(rlocator);
+       UmMetadataUnlink(rlocator, isRedo);
+}
+
 void
 umcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
 {
        mdcreate(reln, forknum, isRedo);
+
+       if (um_tracks_identity_metadata(forknum))
+               um_identity_update_metadata(reln, forknum, 0, true, true);
 }
 
 bool
 umexists(SMgrRelation reln, ForkNumber forknum)
 {
+       if (forknum == UMBRA_METADATA_FORKNUM)
+               return UmMetadataExists(reln);
+
        return mdexists(reln, forknum);
 }
 
@@ -167,13 +253,30 @@ umextend(SMgrRelation reln, ForkNumber forknum, 
BlockNumber blocknum,
                 const void *buffer, bool skipFsync)
 {
        mdextend(reln, forknum, blocknum, buffer, skipFsync);
+
+       if (um_tracks_identity_metadata(forknum))
+               um_identity_update_metadata(reln, forknum, blocknum + 1, true,
+                                                                       
skipFsync);
 }
 
 void
 umzeroextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
                         int nblocks, bool skipFsync)
 {
+       BlockNumber     target_nblocks;
+
        mdzeroextend(reln, forknum, blocknum, nblocks, skipFsync);
+
+       if (um_tracks_identity_metadata(forknum))
+       {
+               target_nblocks = blocknum + (BlockNumber) nblocks;
+               if (target_nblocks < blocknum)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("Umbra identity mapping block 
count overflow")));
+               um_identity_update_metadata(reln, forknum, target_nblocks, true,
+                                                                       
skipFsync);
+       }
 }
 
 bool
@@ -220,6 +323,11 @@ umwriteback(SMgrRelation reln, ForkNumber forknum,
 BlockNumber
 umnblocks(SMgrRelation reln, ForkNumber forknum)
 {
+       /*
+        * Keep md.c responsible for the physical fork size query. mdtruncate()
+        * relies on a preceding mdnblocks() call to have opened all active
+        * segments.
+        */
        return mdnblocks(reln, forknum);
 }
 
@@ -228,12 +336,18 @@ umtruncate(SMgrRelation reln, ForkNumber forknum,
                   BlockNumber old_blocks, BlockNumber nblocks)
 {
        mdtruncate(reln, forknum, old_blocks, nblocks);
+
+       if (um_tracks_identity_metadata(forknum))
+               um_identity_update_metadata(reln, forknum, nblocks, true, 
false);
 }
 
 void
 umimmedsync(SMgrRelation reln, ForkNumber forknum)
 {
        mdimmedsync(reln, forknum);
+
+       if (um_tracks_identity_metadata(forknum) && UmMetadataExists(reln))
+               UmMetadataImmediateSync(reln);
 }
 
 void
@@ -261,3 +375,41 @@ um_relation_filectx(SMgrRelation reln)
 
        return state->filectx;
 }
+
+static bool
+um_tracks_identity_metadata(ForkNumber forknum)
+{
+       return forknum == MAIN_FORKNUM ||
+               forknum == FSM_FORKNUM ||
+               forknum == VISIBILITYMAP_FORKNUM;
+}
+
+static void
+um_identity_update_metadata(SMgrRelation reln, ForkNumber forknum,
+                                                       BlockNumber nblocks, 
bool fork_exists,
+                                                       bool skipFsync)
+{
+       MapSuperblock super;
+
+       Assert(reln != NULL);
+       Assert(um_tracks_identity_metadata(forknum));
+
+       if (!MapSBlockRead(reln, &super))
+               MapSuperblockInit(&super, 0);
+
+       if (!fork_exists && forknum != MAIN_FORKNUM)
+       {
+               MapSuperblockSetLogicalNblocks(&super, forknum, 
InvalidBlockNumber);
+               MapSuperblockSetNextFreePhysBlock(&super, forknum, 
InvalidBlockNumber);
+               MapSuperblockSetPhysCapacity(&super, forknum, 
InvalidBlockNumber);
+       }
+       else
+       {
+               MapSuperblockSetLogicalNblocks(&super, forknum, nblocks);
+               MapSuperblockSetNextFreePhysBlock(&super, forknum, nblocks);
+               MapSuperblockSetPhysCapacity(&super, forknum, nblocks);
+       }
+
+       MapSuperblockSetLastUpdatedLSN(&super, InvalidXLogRecPtr);
+       MapSBlockWrite(reln, &super, skipFsync);
+}
diff --git a/src/include/storage/map.h b/src/include/storage/map.h
new file mode 100644
index 0000000000..b0887794c3
--- /dev/null
+++ b/src/include/storage/map.h
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * map.h
+ *       Umbra metadata-fork disk layout helpers.
+ *
+ * This header defines the stable on-disk page layout and address translation
+ * helpers for Umbra's relation-local metadata file.
+ *
+ * src/include/storage/map.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MAP_H
+#define MAP_H
+
+#include "storage/block.h"
+#include "storage/relfilelocator.h"
+
+#define MAP_ENTRIES_PER_PAGE (BLCKSZ / sizeof(uint32))
+
+/*
+ * Umbra metadata file page layout:
+ * - block 0: superblock payload
+ * - blocks 1..: repeated proportional groups
+ *
+ * Each group reserves one FSM map page, one VM map page, and 8192 MAIN map
+ * pages. That keeps the mapping formula stable while leaving room for the
+ * auxiliary forks to grow alongside MAIN.
+ */
+#define MAP_BLOCK_SUPER                0
+#define MAP_BLOCK_FIRST_GROUP  1
+#define MAP_GROUP_FSM_PAGES    1
+#define MAP_GROUP_VM_PAGES     1
+#define MAP_GROUP_MAIN_PAGES   8192
+#define MAP_GROUP_TOTAL_PAGES \
+       (MAP_GROUP_FSM_PAGES + MAP_GROUP_VM_PAGES + MAP_GROUP_MAIN_PAGES)
+
+typedef struct MapPage
+{
+       uint32          pblknos[MAP_ENTRIES_PER_PAGE];
+} MapPage;
+
+extern void MapPageInit(MapPage *page);
+extern BlockNumber MapPageGetEntry(const MapPage *page, int entry_idx);
+extern void MapPageSetEntry(MapPage *page, int entry_idx, BlockNumber pblkno);
+
+extern BlockNumber MapForkPageIndexToMapBlkno(ForkNumber forknum,
+                                                                               
          BlockNumber fork_page_idx);
+extern BlockNumber MapLblknoToMapBlkno(ForkNumber forknum, BlockNumber lblkno);
+extern bool MapDecodeMapBlkno(BlockNumber map_blkno, ForkNumber *forknum,
+                                                         BlockNumber 
*fork_page_idx);
+
+#endif                                                 /* MAP_H */
diff --git a/src/include/storage/mapsuper.h b/src/include/storage/mapsuper.h
new file mode 100644
index 0000000000..1f6a5dca5a
--- /dev/null
+++ b/src/include/storage/mapsuper.h
@@ -0,0 +1,100 @@
+/*-------------------------------------------------------------------------
+ *
+ * mapsuper.h
+ *       Umbra metadata superblock helpers.
+ *
+ * The superblock is stored in metadata block 0. Its first 512 bytes contain a
+ * versioned payload plus CRC, and the remainder of the block is reserved.
+ *
+ * src/include/storage/mapsuper.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MAPSUPER_H
+#define MAPSUPER_H
+
+#include "access/xlogdefs.h"
+#include "port/pg_crc32c.h"
+#include "storage/block.h"
+#include "storage/smgr.h"
+
+#define MAP_SUPERBLOCK_MAGIC           0x554D4252U     /* "UMBR" */
+#define MAP_SUPERBLOCK_VERSION         1U
+#define MAP_SUPERBLOCK_SIZE                    512
+#define MAP_SUPERBLOCK_PAYLOAD_SIZE 64
+
+#define MAP_SUPERBLOCK_FLAG_SKIP_WAL_PENDING 0x00000001U
+
+typedef struct pg_attribute_packed() MapSuperblockData
+{
+       uint32          magic;
+       uint32          version;
+       uint32          blcksz;
+       uint32          flags;
+
+       BlockNumber next_free_phys_block_main;
+       BlockNumber phys_capacity_main;
+       BlockNumber next_free_phys_block_fsm;
+       BlockNumber phys_capacity_fsm;
+       BlockNumber next_free_phys_block_vm;
+       BlockNumber phys_capacity_vm;
+
+       BlockNumber logical_nblocks_main;
+       BlockNumber logical_nblocks_fsm;
+       BlockNumber logical_nblocks_vm;
+
+       XLogRecPtr      last_updated_lsn;
+       pg_crc32c       crc;
+} MapSuperblockData;
+
+typedef union MapSuperblock
+{
+       MapSuperblockData data;
+       char            padding[MAP_SUPERBLOCK_SIZE];
+} MapSuperblock;
+
+typedef char MapSuperblockDataSizeCheck
+[(sizeof(MapSuperblockData) == MAP_SUPERBLOCK_PAYLOAD_SIZE) ? 1 : -1];
+typedef char MapSuperblockDataCRCOffsetCheck
+[(offsetof(MapSuperblockData, crc) == 60) ? 1 : -1];
+typedef char MapSuperblockSizeCheck
+[(sizeof(MapSuperblock) == MAP_SUPERBLOCK_SIZE) ? 1 : -1];
+
+extern void MapSuperblockInit(MapSuperblock *super, uint32 flags);
+extern bool MapSuperblockHasValidIdentity(const MapSuperblock *super);
+extern bool MapSuperblockIsValid(const MapSuperblock *super);
+extern bool MapSuperblockCheckCRC(const MapSuperblock *super);
+extern void MapSuperblockRefreshCRC(MapSuperblock *super);
+
+extern void MapSuperblockSetFlags(MapSuperblock *super, uint32 flags);
+extern uint32 MapSuperblockGetFlags(const MapSuperblock *super);
+
+extern void MapSuperblockSetLastUpdatedLSN(MapSuperblock *super, XLogRecPtr 
lsn);
+extern XLogRecPtr MapSuperblockGetLastUpdatedLSN(const MapSuperblock *super);
+
+extern BlockNumber MapSuperblockGetNextFreePhysBlock(const MapSuperblock 
*super,
+                                                                               
                         ForkNumber forknum);
+extern void MapSuperblockSetNextFreePhysBlock(MapSuperblock *super,
+                                                                               
          ForkNumber forknum,
+                                                                               
          BlockNumber blkno);
+
+extern BlockNumber MapSuperblockGetPhysCapacity(const MapSuperblock *super,
+                                                                               
                ForkNumber forknum);
+extern void MapSuperblockSetPhysCapacity(MapSuperblock *super, ForkNumber 
forknum,
+                                                                               
 BlockNumber blkno);
+
+extern BlockNumber MapSuperblockGetLogicalNblocks(const MapSuperblock *super,
+                                                                               
                  ForkNumber forknum);
+extern void MapSuperblockSetLogicalNblocks(MapSuperblock *super, ForkNumber 
forknum,
+                                                                               
   BlockNumber nblocks);
+
+extern void MapSuperblockPackPage(const MapSuperblock *super, char 
page[BLCKSZ]);
+extern void MapSuperblockUnpackPage(MapSuperblock *super, const char 
page[BLCKSZ]);
+
+extern bool MapSBlockRead(SMgrRelation reln, MapSuperblock *super);
+extern void MapSBlockWrite(SMgrRelation reln, const MapSuperblock *super,
+                                                  bool skipFsync);
+extern void MapSBlockInitNew(SMgrRelation reln, uint32 flags, XLogRecPtr lsn,
+                                                        bool skipFsync);
+
+#endif                                                 /* MAPSUPER_H */
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 1076717b92..8d06d69b51 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -113,6 +113,12 @@ extern void smgrwriteback(SMgrRelation reln, ForkNumber 
forknum,
                                                  BlockNumber blocknum, 
BlockNumber nblocks);
 extern BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum);
 extern BlockNumber smgrnblocks_cached(SMgrRelation reln, ForkNumber forknum);
+extern void smgrcreaterelationmetadata(SMgrRelation reln);
+extern void smgrcopyrelationmetadata(SMgrRelation src, SMgrRelation dst,
+                                                                        char 
relpersistence);
+extern void smgrsyncrelationmetadata(SMgrRelation reln);
+extern void smgrunlinkrelationmetadata(RelFileLocatorBackend rlocator,
+                                                                          bool 
isRedo);
 extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks,
                                                 BlockNumber *old_nblocks,
                                                 BlockNumber *nblocks);
diff --git a/src/include/storage/umbra.h b/src/include/storage/umbra.h
index 30e033fcf0..2fb3c2f75e 100644
--- a/src/include/storage/umbra.h
+++ b/src/include/storage/umbra.h
@@ -34,6 +34,13 @@ extern void uminit(void);
 extern void umopen(SMgrRelation reln);
 extern void umclose(SMgrRelation reln, ForkNumber forknum);
 extern void umdestroy(SMgrRelation reln);
+extern bool umisinternalfork(ForkNumber forknum);
+extern void umcreaterelationmetadata(SMgrRelation reln);
+extern void umcopyrelationmetadata(SMgrRelation src, SMgrRelation dst,
+                                                                  char 
relpersistence);
+extern void umsyncrelationmetadata(SMgrRelation reln);
+extern void umunlinkrelationmetadata(RelFileLocatorBackend rlocator,
+                                                                        bool 
isRedo);
 extern void umcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
 extern bool umexists(SMgrRelation reln, ForkNumber forknum);
 extern void umunlink(RelFileLocatorBackend rlocator, ForkNumber forknum, bool 
isRedo);
-- 
2.50.1 (Apple Git-155)



Reply via email to