diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index a2f75b23b8..af5b329f8a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -11,6 +11,7 @@
  *			- Add a pgstat config column to pg_database, so this
  *			  entire thing can be enabled/disabled on a per db basis.
  *
+ *	Portions Copyright (c) 2019-2021, CYBERTEC PostgreSQL International GmbH
  *	Copyright (c) 2001-2021, PostgreSQL Global Development Group
  *
  *	src/backend/postmaster/pgstat.c
@@ -54,6 +55,7 @@
 #include "replication/slot.h"
 #include "replication/walsender.h"
 #include "storage/backendid.h"
+#include "storage/buffile.h"
 #include "storage/dsm.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
@@ -308,10 +310,16 @@ NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]) pg_attribute_no
 static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
 static PgStat_StatTabEntry *pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry,
 												 Oid tableoid, bool create);
+static void pgstat_tweak_base(bool permanent, bool global, Oid database,
+							  char tweak_base[TWEAK_BASE_SIZE]);
 static void pgstat_write_statsfiles(bool permanent, bool allDbs);
 static void pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent);
+static void pgstat_write_bytes(TransientBufFile *file, void *ptr, size_t size,
+							   bool *failed);
 static HTAB *pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep);
 static void pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash, bool permanent);
+static void pgstat_read_bytes(TransientBufFile *file, void *ptr, size_t size,
+							  bool *failed);
 static void backend_read_statsfile(void);
 
 static bool pgstat_write_statsfile_needed(void);
@@ -3202,6 +3210,9 @@ PgstatCollectorMain(int argc, char *argv[])
 	MyBackendType = B_STATS_COLLECTOR;
 	init_ps_display(NULL);
 
+	/* BufFileOpenTransient() and friends do use VFD. */
+	InitFileAccess();
+
 	/*
 	 * Read in existing stats files or initialize the stats to zero.
 	 */
@@ -3595,6 +3606,26 @@ pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create)
 	return result;
 }
 
+/*
+ * Initialize the common part of the encryption tweak.
+ */
+static void
+pgstat_tweak_base(bool permanent, bool global, Oid database,
+				  char tweak_base[TWEAK_BASE_SIZE])
+{
+	char	*c = tweak_base;
+
+	StaticAssertStmt(3 + sizeof(Oid) <= TWEAK_BASE_SIZE,
+					 "tweak components do not fit into TWEAK_BASE_SIZE");
+	memset(tweak_base, 0, TWEAK_BASE_SIZE);
+	*c = TRANS_BUF_FILE_PGSTATS;
+	c++;
+	*c = permanent;
+	c++;
+	*c = global;
+	c++;
+	memcpy(c, &database, sizeof(Oid));
+}
 
 /* ----------
  * pgstat_write_statsfiles() -
@@ -3615,18 +3646,24 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 {
 	HASH_SEQ_STATUS hstat;
 	PgStat_StatDBEntry *dbentry;
-	FILE	   *fpout;
+	TransientBufFile	   *fpout;
+	File	vfd;
 	int32		format_id;
 	const char *tmpfile = permanent ? PGSTAT_STAT_PERMANENT_TMPFILE : pgstat_stat_tmpname;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
-	int			rc;
+	bool	failed = false;
+	char tweak_base[TWEAK_BASE_SIZE];
 
 	elog(DEBUG2, "writing stats file \"%s\"", statfile);
 
 	/*
 	 * Open the statistics temp file to write out the current values.
 	 */
-	fpout = AllocateFile(tmpfile, PG_BINARY_W);
+	if (data_encrypted)
+		pgstat_tweak_base(permanent, true, InvalidOid, tweak_base);
+	fpout = BufFileOpenTransient(tmpfile,
+								 O_CREAT | O_WRONLY | O_APPEND | PG_BINARY,
+								 tweak_base);
 	if (fpout == NULL)
 	{
 		ereport(LOG,
@@ -3645,32 +3682,27 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	 * Write the file header --- currently just a format ID.
 	 */
 	format_id = PGSTAT_FILE_FORMAT_ID;
-	rc = fwrite(&format_id, sizeof(format_id), 1, fpout);
-	(void) rc;					/* we'll check for error with ferror */
+	pgstat_write_bytes(fpout, &format_id, sizeof(format_id), &failed);
 
 	/*
 	 * Write global stats struct
 	 */
-	rc = fwrite(&globalStats, sizeof(globalStats), 1, fpout);
-	(void) rc;					/* we'll check for error with ferror */
+	pgstat_write_bytes(fpout, &globalStats, sizeof(globalStats), &failed);
 
 	/*
 	 * Write archiver stats struct
 	 */
-	rc = fwrite(&archiverStats, sizeof(archiverStats), 1, fpout);
-	(void) rc;					/* we'll check for error with ferror */
+	pgstat_write_bytes(fpout, &archiverStats, sizeof(archiverStats), &failed);
 
 	/*
 	 * Write WAL stats struct
 	 */
-	rc = fwrite(&walStats, sizeof(walStats), 1, fpout);
-	(void) rc;					/* we'll check for error with ferror */
+	pgstat_write_bytes(fpout, &walStats, sizeof(walStats), &failed);
 
 	/*
 	 * Write SLRU stats struct
 	 */
-	rc = fwrite(slruStats, sizeof(slruStats), 1, fpout);
-	(void) rc;					/* we'll check for error with ferror */
+	pgstat_write_bytes(fpout, slruStats, sizeof(slruStats), &failed);
 
 	/*
 	 * Walk through the database table.
@@ -3694,9 +3726,11 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 		 * Write out the DB entry. We don't write the tables or functions
 		 * pointers, since they're of no use to any other process.
 		 */
-		fputc('D', fpout);
-		rc = fwrite(dbentry, offsetof(PgStat_StatDBEntry, tables), 1, fpout);
-		(void) rc;				/* we'll check for error with ferror */
+		pgstat_write_bytes(fpout, "D", 1, &failed);
+		pgstat_write_bytes(fpout,
+						   dbentry,
+						   offsetof(PgStat_StatDBEntry, tables),
+						   &failed);
 	}
 
 	/*
@@ -3709,29 +3743,36 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 		hash_seq_init(&hstat, replSlotStatHash);
 		while ((slotent = (PgStat_StatReplSlotEntry *) hash_seq_search(&hstat)) != NULL)
 		{
-			fputc('R', fpout);
-			rc = fwrite(slotent, sizeof(PgStat_StatReplSlotEntry), 1, fpout);
-			(void) rc;			/* we'll check for error with ferror */
+			pgstat_write_bytes(fpout, "R", 1, &failed);
+			pgstat_write_bytes(fpout, slotent, sizeof(PgStat_StatReplSlotEntry),
+							   &failed);
 		}
 	}
 
 	/*
 	 * No more output to be done. Close the temp file and replace the old
-	 * pgstat.stat with it.  The ferror() check replaces testing for error
-	 * after each individual fputc or fwrite above.
+	 * pgstat.stat with it.
 	 */
-	fputc('E', fpout);
+	pgstat_write_bytes(fpout, "E", 1, &failed);
 
-	if (ferror(fpout))
+	if (failed)
 	{
 		ereport(LOG,
 				(errcode_for_file_access(),
 				 errmsg("could not write temporary statistics file \"%s\": %m",
 						tmpfile)));
-		FreeFile(fpout);
+		BufFileCloseTransient(fpout);
 		unlink(tmpfile);
+		return;
 	}
-	else if (FreeFile(fpout) < 0)
+
+	/*
+	 * XXX This might PANIC, see FileClose(). Don't we need special behaviour
+	 * for statistics?
+	 */
+	vfd = BufFileTransientGetVfd(fpout);
+	BufFileCloseTransient(fpout);
+	if (!FileIsClosed(vfd))
 	{
 		ereport(LOG,
 				(errcode_for_file_access(),
@@ -3796,12 +3837,14 @@ pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
 	HASH_SEQ_STATUS fstat;
 	PgStat_StatTabEntry *tabentry;
 	PgStat_StatFuncEntry *funcentry;
-	FILE	   *fpout;
+	TransientBufFile	   *fpout;
+	File	vfd;
 	int32		format_id;
 	Oid			dbid = dbentry->databaseid;
-	int			rc;
 	char		tmpfile[MAXPGPATH];
 	char		statfile[MAXPGPATH];
+	bool	failed = false;
+	char tweak_base[TWEAK_BASE_SIZE];
 
 	get_dbstat_filename(permanent, true, dbid, tmpfile, MAXPGPATH);
 	get_dbstat_filename(permanent, false, dbid, statfile, MAXPGPATH);
@@ -3811,7 +3854,11 @@ pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
 	/*
 	 * Open the statistics temp file to write out the current values.
 	 */
-	fpout = AllocateFile(tmpfile, PG_BINARY_W);
+	if (data_encrypted)
+		pgstat_tweak_base(permanent, false, dbid, tweak_base);
+	fpout = BufFileOpenTransient(tmpfile,
+								 O_CREAT | O_WRONLY | O_APPEND | PG_BINARY,
+								 tweak_base);
 	if (fpout == NULL)
 	{
 		ereport(LOG,
@@ -3825,8 +3872,7 @@ pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
 	 * Write the file header --- currently just a format ID.
 	 */
 	format_id = PGSTAT_FILE_FORMAT_ID;
-	rc = fwrite(&format_id, sizeof(format_id), 1, fpout);
-	(void) rc;					/* we'll check for error with ferror */
+	pgstat_write_bytes(fpout, &format_id, sizeof(format_id), &failed);
 
 	/*
 	 * Walk through the database's access stats per table.
@@ -3834,9 +3880,14 @@ pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
 	hash_seq_init(&tstat, dbentry->tables);
 	while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&tstat)) != NULL)
 	{
-		fputc('T', fpout);
-		rc = fwrite(tabentry, sizeof(PgStat_StatTabEntry), 1, fpout);
-		(void) rc;				/* we'll check for error with ferror */
+		pgstat_write_bytes(fpout, "T", 1, &failed);
+		if (failed)
+			break;
+
+		pgstat_write_bytes(fpout, tabentry, sizeof(PgStat_StatTabEntry),
+						   &failed);
+		if (failed)
+			break;
 	}
 
 	/*
@@ -3845,28 +3896,42 @@ pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
 	hash_seq_init(&fstat, dbentry->functions);
 	while ((funcentry = (PgStat_StatFuncEntry *) hash_seq_search(&fstat)) != NULL)
 	{
-		fputc('F', fpout);
-		rc = fwrite(funcentry, sizeof(PgStat_StatFuncEntry), 1, fpout);
-		(void) rc;				/* we'll check for error with ferror */
+		pgstat_write_bytes(fpout, "F", 1, &failed);
+		if (failed)
+			break;
+
+		pgstat_write_bytes(fpout,
+						   funcentry,
+						   sizeof(PgStat_StatFuncEntry),
+						   &failed);
+		if (failed)
+			break;
 	}
 
 	/*
 	 * No more output to be done. Close the temp file and replace the old
-	 * pgstat.stat with it.  The ferror() check replaces testing for error
-	 * after each individual fputc or fwrite above.
+	 * pgstat.stat with it.
 	 */
-	fputc('E', fpout);
+	pgstat_write_bytes(fpout, "E", 1, &failed);
 
-	if (ferror(fpout))
+	if (failed)
 	{
 		ereport(LOG,
 				(errcode_for_file_access(),
 				 errmsg("could not write temporary statistics file \"%s\": %m",
 						tmpfile)));
-		FreeFile(fpout);
+		BufFileCloseTransient(fpout);
 		unlink(tmpfile);
+		return;
 	}
-	else if (FreeFile(fpout) < 0)
+
+	/*
+	 * XXX This might PANIC, see FileClose(). Don't we need special behaviour
+	 * for statistics?
+	 */
+	vfd = BufFileTransientGetVfd(fpout);
+	BufFileCloseTransient(fpout);
+	if (!FileIsClosed(vfd))
 	{
 		ereport(LOG,
 				(errcode_for_file_access(),
@@ -3892,6 +3957,25 @@ pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
 	}
 }
 
+/*
+ * Convenience routine to write data to file and check for errors.
+ */
+static void
+pgstat_write_bytes(TransientBufFile *file, void *ptr, size_t size,
+	bool *failed)
+{
+	/* Do nothing if any previous write failed. */
+	if (*failed)
+		return;
+
+	/*
+	 * Use BufFileWriteTransient() because it handles encryption
+	 * transparently.
+	 */
+	if (BufFileWriteTransient(file, ptr, size) != size)
+		*failed = true;
+}
+
 /* ----------
  * pgstat_read_statsfiles() -
  *
@@ -3919,10 +4003,12 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	PgStat_StatDBEntry dbbuf;
 	HASHCTL		hash_ctl;
 	HTAB	   *dbhash;
-	FILE	   *fpin;
+	TransientBufFile *fpin;
 	int32		format_id;
 	bool		found;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
+	bool	failed = false;
+	char tweak_base[TWEAK_BASE_SIZE];
 	int			i;
 
 	/*
@@ -3971,7 +4057,11 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 * not yet written the stats file the first time.  Any other failure
 	 * condition is suspicious.
 	 */
-	if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
+	if (data_encrypted)
+		pgstat_tweak_base(permanent, true, InvalidOid, tweak_base);
+	if ((fpin = BufFileOpenTransient(statfile,
+									 O_RDONLY | PG_BINARY,
+									 tweak_base)) == NULL)
 	{
 		if (errno != ENOENT)
 			ereport(pgStatRunningInCollector ? LOG : WARNING,
@@ -3984,8 +4074,8 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	/*
 	 * Verify it's of the expected format.
 	 */
-	if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id) ||
-		format_id != PGSTAT_FILE_FORMAT_ID)
+	pgstat_read_bytes(fpin, &format_id, sizeof(format_id), &failed);
+	if (failed || format_id != PGSTAT_FILE_FORMAT_ID)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
@@ -3995,7 +4085,8 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	/*
 	 * Read global stats struct
 	 */
-	if (fread(&globalStats, 1, sizeof(globalStats), fpin) != sizeof(globalStats))
+	pgstat_read_bytes(fpin, &globalStats, sizeof(globalStats), &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
@@ -4016,7 +4107,8 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	/*
 	 * Read archiver stats struct
 	 */
-	if (fread(&archiverStats, 1, sizeof(archiverStats), fpin) != sizeof(archiverStats))
+	pgstat_read_bytes(fpin, &archiverStats, sizeof(archiverStats), &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
@@ -4027,7 +4119,8 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	/*
 	 * Read WAL stats struct
 	 */
-	if (fread(&walStats, 1, sizeof(walStats), fpin) != sizeof(walStats))
+	pgstat_read_bytes(fpin, &walStats, sizeof(walStats), &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
@@ -4038,7 +4131,8 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	/*
 	 * Read SLRU stats struct
 	 */
-	if (fread(slruStats, 1, sizeof(slruStats), fpin) != sizeof(slruStats))
+	pgstat_read_bytes(fpin, &slruStats, sizeof(slruStats), &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
@@ -4052,15 +4146,22 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	for (;;)
 	{
-		switch (fgetc(fpin))
+		char	c;
+
+		pgstat_read_bytes(fpin, &c, 1, &failed);
+
+		switch (c)
 		{
 				/*
 				 * 'D'	A PgStat_StatDBEntry struct describing a database
 				 * follows.
 				 */
 			case 'D':
-				if (fread(&dbbuf, 1, offsetof(PgStat_StatDBEntry, tables),
-						  fpin) != offsetof(PgStat_StatDBEntry, tables))
+				pgstat_read_bytes(fpin,
+								  &dbbuf,
+								  offsetof(PgStat_StatDBEntry, tables),
+								  &failed);
+				if (failed)
 				{
 					ereport(pgStatRunningInCollector ? LOG : WARNING,
 							(errmsg("corrupted statistics file \"%s\"",
@@ -4144,8 +4245,10 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 					PgStat_StatReplSlotEntry slotbuf;
 					PgStat_StatReplSlotEntry *slotent;
 
-					if (fread(&slotbuf, 1, sizeof(PgStat_StatReplSlotEntry), fpin)
-						!= sizeof(PgStat_StatReplSlotEntry))
+					pgstat_read_bytes(fpin, &slotbuf,
+									  sizeof(PgStat_StatReplSlotEntry),
+									  &failed);
+					if (failed)
 					{
 						ereport(pgStatRunningInCollector ? LOG : WARNING,
 								(errmsg("corrupted statistics file \"%s\"",
@@ -4186,7 +4289,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 done:
-	FreeFile(fpin);
+	BufFileCloseTransient(fpin);
 
 	/* If requested to read the permanent file, also get rid of it. */
 	if (permanent)
@@ -4221,10 +4324,12 @@ pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash,
 	PgStat_StatTabEntry tabbuf;
 	PgStat_StatFuncEntry funcbuf;
 	PgStat_StatFuncEntry *funcentry;
-	FILE	   *fpin;
+	TransientBufFile	   *fpin;
 	int32		format_id;
 	bool		found;
 	char		statfile[MAXPGPATH];
+	bool	failed = false;
+	char tweak_base[TWEAK_BASE_SIZE];
 
 	get_dbstat_filename(permanent, false, databaseid, statfile, MAXPGPATH);
 
@@ -4237,7 +4342,11 @@ pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash,
 	 * not yet written the stats file the first time.  Any other failure
 	 * condition is suspicious.
 	 */
-	if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
+	if (data_encrypted)
+		pgstat_tweak_base(permanent, false, databaseid, tweak_base);
+	if ((fpin = BufFileOpenTransient(statfile,
+									 O_RDONLY | PG_BINARY,
+									 tweak_base)) == NULL)
 	{
 		if (errno != ENOENT)
 			ereport(pgStatRunningInCollector ? LOG : WARNING,
@@ -4250,8 +4359,8 @@ pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash,
 	/*
 	 * Verify it's of the expected format.
 	 */
-	if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id) ||
-		format_id != PGSTAT_FILE_FORMAT_ID)
+	pgstat_read_bytes(fpin, &format_id, sizeof(format_id), &failed);
+	if (failed || format_id != PGSTAT_FILE_FORMAT_ID)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
@@ -4264,14 +4373,19 @@ pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash,
 	 */
 	for (;;)
 	{
-		switch (fgetc(fpin))
+		char	c;
+
+		pgstat_read_bytes(fpin, &c, 1, &failed);
+
+		switch (c)
 		{
 				/*
 				 * 'T'	A PgStat_StatTabEntry follows.
 				 */
 			case 'T':
-				if (fread(&tabbuf, 1, sizeof(PgStat_StatTabEntry),
-						  fpin) != sizeof(PgStat_StatTabEntry))
+				pgstat_read_bytes(fpin, &tabbuf, sizeof(PgStat_StatTabEntry),
+								  &failed);
+				if (failed)
 				{
 					ereport(pgStatRunningInCollector ? LOG : WARNING,
 							(errmsg("corrupted statistics file \"%s\"",
@@ -4304,8 +4418,11 @@ pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash,
 				 * 'F'	A PgStat_StatFuncEntry follows.
 				 */
 			case 'F':
-				if (fread(&funcbuf, 1, sizeof(PgStat_StatFuncEntry),
-						  fpin) != sizeof(PgStat_StatFuncEntry))
+				pgstat_read_bytes(fpin,
+								  &funcbuf,
+								  sizeof(PgStat_StatFuncEntry),
+								  &failed);
+				if (failed)
 				{
 					ereport(pgStatRunningInCollector ? LOG : WARNING,
 							(errmsg("corrupted statistics file \"%s\"",
@@ -4349,7 +4466,7 @@ pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash,
 	}
 
 done:
-	FreeFile(fpin);
+	BufFileCloseTransient(fpin);
 
 	if (permanent)
 	{
@@ -4386,15 +4503,21 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_WalStats myWalStats;
 	PgStat_SLRUStats mySLRUStats[SLRU_NUM_ELEMENTS];
 	PgStat_StatReplSlotEntry myReplSlotStats;
-	FILE	   *fpin;
+	TransientBufFile	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
+	bool	failed = false;
+	char tweak_base[TWEAK_BASE_SIZE];
 
 	/*
 	 * Try to open the stats file.  As above, anything but ENOENT is worthy of
 	 * complaining about.
 	 */
-	if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
+	if (data_encrypted)
+		pgstat_tweak_base(permanent, true, InvalidOid, tweak_base);
+	if ((fpin = BufFileOpenTransient(statfile,
+									 O_RDONLY | PG_BINARY,
+									 tweak_base)) == NULL)
 	{
 		if (errno != ENOENT)
 			ereport(pgStatRunningInCollector ? LOG : WARNING,
@@ -4407,58 +4530,62 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	/*
 	 * Verify it's of the expected format.
 	 */
-	if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id) ||
-		format_id != PGSTAT_FILE_FORMAT_ID)
+	pgstat_read_bytes(fpin, &format_id, sizeof(format_id), &failed);
+	if (failed || format_id != PGSTAT_FILE_FORMAT_ID)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
-		FreeFile(fpin);
+		BufFileCloseTransient(fpin);
 		return false;
 	}
 
 	/*
 	 * Read global stats struct
 	 */
-	if (fread(&myGlobalStats, 1, sizeof(myGlobalStats),
-			  fpin) != sizeof(myGlobalStats))
+	pgstat_read_bytes(fpin, &myGlobalStats, sizeof(myGlobalStats), &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
-		FreeFile(fpin);
+		BufFileCloseTransient(fpin);
 		return false;
 	}
 
 	/*
 	 * Read archiver stats struct
 	 */
-	if (fread(&myArchiverStats, 1, sizeof(myArchiverStats),
-			  fpin) != sizeof(myArchiverStats))
+	pgstat_read_bytes(fpin, &myArchiverStats, sizeof(myArchiverStats),
+					  &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
-		FreeFile(fpin);
+		BufFileCloseTransient(fpin);
 		return false;
 	}
 
 	/*
 	 * Read WAL stats struct
 	 */
-	if (fread(&myWalStats, 1, sizeof(myWalStats), fpin) != sizeof(myWalStats))
+	pgstat_read_bytes(fpin, &myWalStats, sizeof(myWalStats), &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
-		FreeFile(fpin);
+		BufFileCloseTransient(fpin);
 		return false;
 	}
 
 	/*
 	 * Read SLRU stats struct
 	 */
-	if (fread(mySLRUStats, 1, sizeof(mySLRUStats), fpin) != sizeof(mySLRUStats))
+	pgstat_read_bytes(fpin, mySLRUStats, sizeof(mySLRUStats),
+					  &failed);
+	if (failed)
 	{
 		ereport(pgStatRunningInCollector ? LOG : WARNING,
 				(errmsg("corrupted statistics file \"%s\"", statfile)));
-		FreeFile(fpin);
+		BufFileCloseTransient(fpin);
 		return false;
 	}
 
@@ -4471,20 +4598,27 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	 */
 	for (;;)
 	{
-		switch (fgetc(fpin))
+		char	c;
+
+		pgstat_read_bytes(fpin, &c, 1, &failed);
+
+		switch (c)
 		{
 				/*
 				 * 'D'	A PgStat_StatDBEntry struct describing a database
 				 * follows.
 				 */
 			case 'D':
-				if (fread(&dbentry, 1, offsetof(PgStat_StatDBEntry, tables),
-						  fpin) != offsetof(PgStat_StatDBEntry, tables))
+				pgstat_read_bytes(fpin,
+								  &dbentry,
+								  offsetof(PgStat_StatDBEntry, tables),
+								  &failed);
+				if (failed)
 				{
 					ereport(pgStatRunningInCollector ? LOG : WARNING,
 							(errmsg("corrupted statistics file \"%s\"",
 									statfile)));
-					FreeFile(fpin);
+					BufFileCloseTransient(fpin);
 					return false;
 				}
 
@@ -4505,13 +4639,14 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 				 * replication slot follows.
 				 */
 			case 'R':
-				if (fread(&myReplSlotStats, 1, sizeof(PgStat_StatReplSlotEntry), fpin)
-					!= sizeof(PgStat_StatReplSlotEntry))
+				pgstat_read_bytes(fpin, &myReplSlotStats,
+								  sizeof(PgStat_StatReplSlotEntry), &failed);
+				if (failed)
 				{
 					ereport(pgStatRunningInCollector ? LOG : WARNING,
 							(errmsg("corrupted statistics file \"%s\"",
 									statfile)));
-					FreeFile(fpin);
+					BufFileCloseTransient(fpin);
 					return false;
 				}
 				break;
@@ -4524,17 +4659,35 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 					ereport(pgStatRunningInCollector ? LOG : WARNING,
 							(errmsg("corrupted statistics file \"%s\"",
 									statfile)));
-					FreeFile(fpin);
+					BufFileCloseTransient(fpin);
 					return false;
 				}
 		}
 	}
 
 done:
-	FreeFile(fpin);
+	BufFileCloseTransient(fpin);
 	return true;
 }
 
+/*
+ * Convenience routine to read data from file and check for errors.
+ */
+static void
+pgstat_read_bytes(TransientBufFile *file, void *ptr, size_t size,
+	bool *failed)
+{
+	/* Do nothing if any previous read failed. */
+	if (*failed)
+		return;
+
+	/*
+	 * Use BufFileReadTransient() because it handles encryption transparently.
+	 */
+	if (BufFileReadTransient(file, ptr, size) != size)
+		*failed = true;
+}
+
 /*
  * If not already done, read the statistics collector stats file into
  * some hash tables.  The results will be kept until pgstat_clear_snapshot()
