public inbox for [email protected]
help / color / mirror / Atom feedRe: Check some unchecked fclose() results
3+ messages / 2 participants
[nested] [flat]
* Re: Check some unchecked fclose() results
@ 2026-03-23 02:22 Chao Li <[email protected]>
2026-04-02 01:16 ` Re: Check some unchecked fclose() results Andreas Karlsson <[email protected]>
0 siblings, 1 reply; 3+ messages in thread
From: Chao Li @ 2026-03-23 02:22 UTC (permalink / raw)
To: PostgreSQL Hackers <[email protected]>
> On Mar 23, 2026, at 10:04, Chao Li <[email protected]> wrote:
>
> Hi,
>
> This morning, while reading through recent commits, I noticed that 69c57466a added a check for fclose(), with the explanation that “write errors (like ENOSPC) may not be reported until close time.” More generally, an fclose() failure can also reflect an earlier buffered write or flush failure that is only reported when the stream is closed. So it seems worth checking these calls in other file-writing paths as well.
>
> So I did a quick search through the source tree, and I found several other fclose() calls whose results are not checked. This patch fixes some of them.
>
> My criteria for including cases in this patch were basically:
>
> * only output file descriptors
> * code paths where the logic is relatively clear and easy to handle
>
> If this patch gets processed, I would be happy to spend more time handling the remaining cases. Or, if committers think all remaining cases should be included in this patch, I would also be happy to expand it.
>
Forgot to attach the patch file. Here comes it.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v1-0001-Check-fclose-failures-in-more-places.patch (5.8K, 2-v1-0001-Check-fclose-failures-in-more-places.patch)
download | inline diff:
From 9173313ee399247384abd9724c64fdf378dd49e5 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 23 Mar 2026 09:49:59 +0800
Subject: [PATCH v1] Check fclose() failures in more places
Commit 860359ea0 fixed archive_waldump.c to check fclose(), noting that
write errors such as ENOSPC may not be reported until close time.
Do the same in several other file-writing paths that currently ignore
fclose() failures.
In postmaster.c, if writing the external PID file fails at fclose()
time, report the error, avoid chmod() on the incomplete file, and
unlink it so that a partial file is not left behind.
Author: Chao Li <[email protected]>
Reviewed-by:
Discussion:
---
src/backend/postmaster/postmaster.c | 19 +++++++++++++------
src/bin/pg_basebackup/pg_createsubscriber.c | 3 ++-
src/bin/pg_dump/pg_dumpall.c | 11 ++++++++---
src/bin/pg_upgrade/pg_upgrade.c | 3 ++-
src/fe_utils/astreamer_file.c | 3 ++-
src/fe_utils/recovery_gen.c | 6 ++++--
6 files changed, 31 insertions(+), 14 deletions(-)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3fac46c402b..95f20f35f31 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1300,12 +1300,19 @@ PostmasterMain(int argc, char *argv[])
if (fpidfile)
{
fprintf(fpidfile, "%d\n", MyProcPid);
- fclose(fpidfile);
-
- /* Make PID file world readable */
- if (chmod(external_pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0)
- write_stderr("%s: could not change permissions of external PID file \"%s\": %m\n",
+ if (fclose(fpidfile) != 0)
+ {
+ write_stderr("%s: could not write external PID file \"%s\": %m\n",
progname, external_pid_file);
+ unlink(external_pid_file);
+ }
+ else
+ {
+ /* Make PID file world readable */
+ if (chmod(external_pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0)
+ write_stderr("%s: could not change permissions of external PID file \"%s\": %m\n",
+ progname, external_pid_file);
+ }
}
else
write_stderr("%s: could not write external PID file \"%s\": %m\n",
@@ -4117,7 +4124,7 @@ CreateOptsFile(int argc, char *argv[], char *fullprogname)
fprintf(fp, " \"%s\"", argv[i]);
fputs("\n", fp);
- if (fclose(fp))
+ if (fclose(fp) != 0)
{
ereport(LOG,
(errcode_for_file_access(),
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..27d2c830d4d 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -1390,7 +1390,8 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
if (fwrite(recoveryconfcontents->data, recoveryconfcontents->len, 1, fd) != 1)
pg_fatal("could not write to file \"%s\": %m", conf_filename);
- fclose(fd);
+ if (fclose(fd) != 0)
+ pg_fatal("could not close file \"%s\": %m", conf_filename);
recovery_params_set = true;
/* Include conditionally the recovery parameters. */
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 20cdd2d92f0..ba4831181c5 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -842,7 +842,8 @@ main(int argc, char *argv[])
if (filename)
{
- fclose(OPF);
+ if (fclose(OPF) != 0)
+ pg_fatal("could not close output file \"%s\": %m", filename);
/* sync the resulting file, errors are not fatal */
if (dosync)
@@ -2099,7 +2100,8 @@ dumpDatabases(PGconn *conn)
create_opts = "--create";
if (filename && archDumpFormat == archNull)
- fclose(OPF);
+ if (fclose(OPF) != 0)
+ pg_fatal("could not close output file \"%s\": %m", filename);
/*
* If this is not a plain format dump, then append dboid and dbname to
@@ -2133,7 +2135,10 @@ dumpDatabases(PGconn *conn)
/* Close map file */
if (archDumpFormat != archNull)
- fclose(map_file);
+ {
+ if (fclose(map_file) != 0)
+ pg_fatal("could not close map file: %m");
+ }
PQclear(res);
}
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..ee5dd654e25 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -353,7 +353,8 @@ make_outputdirs(char *pgdata)
" pg_upgrade run on %s"
"-----------------------------------------------------------------\n\n",
ctime(&run_time));
- fclose(fp);
+ if (fclose(fp) != 0)
+ pg_fatal("could not close log file \"%s\": %m", filename_path);
}
}
diff --git a/src/fe_utils/astreamer_file.c b/src/fe_utils/astreamer_file.c
index 6e63a41af0d..33da0bc980b 100644
--- a/src/fe_utils/astreamer_file.c
+++ b/src/fe_utils/astreamer_file.c
@@ -266,7 +266,8 @@ astreamer_extractor_content(astreamer *streamer, astreamer_member *member,
case ASTREAMER_MEMBER_TRAILER:
if (mystreamer->file == NULL)
break;
- fclose(mystreamer->file);
+ if (fclose(mystreamer->file) != 0)
+ pg_fatal("could not close file \"%s\": %m", mystreamer->filename);
mystreamer->file = NULL;
break;
diff --git a/src/fe_utils/recovery_gen.c b/src/fe_utils/recovery_gen.c
index f352652c785..f6da9e0d152 100644
--- a/src/fe_utils/recovery_gen.c
+++ b/src/fe_utils/recovery_gen.c
@@ -143,7 +143,8 @@ WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents
if (fwrite(contents->data, contents->len, 1, cf) != 1)
pg_fatal("could not write to file \"%s\": %m", filename);
- fclose(cf);
+ if (fclose(cf) != 0)
+ pg_fatal("could not close file \"%s\": %m", filename);
if (!use_recovery_conf)
{
@@ -152,7 +153,8 @@ WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents
if (cf == NULL)
pg_fatal("could not create file \"%s\": %m", filename);
- fclose(cf);
+ if (fclose(cf) != 0)
+ pg_fatal("could not close file \"%s\": %m", filename);
}
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 3+ messages in thread
* Re: Check some unchecked fclose() results
2026-03-23 02:22 Re: Check some unchecked fclose() results Chao Li <[email protected]>
@ 2026-04-02 01:16 ` Andreas Karlsson <[email protected]>
2026-04-02 04:15 ` Re: Check some unchecked fclose() results Chao Li <[email protected]>
0 siblings, 1 reply; 3+ messages in thread
From: Andreas Karlsson @ 2026-04-02 01:16 UTC (permalink / raw)
To: Chao Li <[email protected]>; PostgreSQL Hackers <[email protected]>
On 3/23/26 3:22 AM, Chao Li wrote:
>> My criteria for including cases in this patch were basically:
>>
>> * only output file descriptors
>> * code paths where the logic is relatively clear and easy to handle
Those criteria are not enough as can be evidenced from some of the cases
which you patched. I do not see why you would want to error out in the
following two cases:
1. When writing to e.g. a log file and we do not call pg_fatal() if a
write fails. Then it makes no sense to die on failed fclose().
2. When creating an empty file. I could be wrong here but in that case
fclose() cannot fail. Arguably maybe we should use open() and close()
then instead of fopen() and fclose() but handling an error which can
never happen does not add any value.
Please review your patches a bit careful before submitting. You are
doing some good work with finding bugs and reviewing patches but it is
clear you do not spend enough time per mail making sure it is not a
false positive.
--
Andreas Karlsson
Percona
^ permalink raw reply [nested|flat] 3+ messages in thread
* Re: Check some unchecked fclose() results
2026-03-23 02:22 Re: Check some unchecked fclose() results Chao Li <[email protected]>
2026-04-02 01:16 ` Re: Check some unchecked fclose() results Andreas Karlsson <[email protected]>
@ 2026-04-02 04:15 ` Chao Li <[email protected]>
0 siblings, 0 replies; 3+ messages in thread
From: Chao Li @ 2026-04-02 04:15 UTC (permalink / raw)
To: Andreas Karlsson <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>
> On Apr 2, 2026, at 09:16, Andreas Karlsson <[email protected]> wrote:
>
> On 3/23/26 3:22 AM, Chao Li wrote:
>>> My criteria for including cases in this patch were basically:
>>>
>>> * only output file descriptors
>>> * code paths where the logic is relatively clear and easy to handle
>
> Those criteria are not enough as can be evidenced from some of the cases which you patched. I do not see why you would want to error out in the following two cases:
>
Thanks for noticing this patch.
> 1. When writing to e.g. a log file and we do not call pg_fatal() if a write fails. Then it makes no sense to die on failed fclose().
>
That’s a good point. I think you referred to the change in pg_dumpall.c and postmaster.c. I didn’t consider that point when I wrote the patch. But I think it’s arguable. Like in pg_dumpall.c, there are a lot of fprintf, check results of every fprintf might be redundant, checking a single fclose() might be cleaner. Anyway, I don’t want to argue hard for that.
> 2. When creating an empty file. I could be wrong here but in that case fclose() cannot fail. Arguably maybe we should use open() and close() then instead of fopen() and fclose() but handling an error which can never happen does not add any value.
>
Even if creating an empty file, fclose() could still possible fail. For example, creating an empty on a network storage, while fclose(), the network connection might be broken, then fclose() could fail.
> Please review your patches a bit careful before submitting. You are doing some good work with finding bugs and reviewing patches but it is clear you do not spend enough time per mail making sure it is not a false positive.
>
I might have missed some points while working on the patch, but I’m not sure how you concluded that I didn’t spend enough time on it. I did make some “too quick” mistakes in my first few months working on the hacker work. Since then, I have made it a rule to do self-review, build, and testing for every patch before sending it out. Of course, I am still human, I cannot guarantee that I will never make mistakes.
But your comments made me realize that a cleanup patch covering all fclose() is not a good idea. Each case needs to be evaluated separately, so a broad cleanup patch would be hard to get processed. For that reason, I am withdrawing this patch.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 3+ messages in thread
end of thread, other threads:[~2026-04-02 04:15 UTC | newest]
Thread overview: 3+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-03-23 02:22 Re: Check some unchecked fclose() results Chao Li <[email protected]>
2026-04-02 01:16 ` Andreas Karlsson <[email protected]>
2026-04-02 04:15 ` Chao Li <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox