Skip to content

Commit c6c6750

Browse files
committed
Fix error handling in temp-file deletion with log_temp_files active.
The original coding in FileClose() reset the file-is-temp flag before unlinking the file, so that if control came back through due to an error, it wouldn't try to unlink the file twice. This was correct when written, but when the log_temp_files feature was added, the logging action was put in between those two steps. An error occurring during the logging action --- such as a query cancel --- would result in the unlink not getting done at all, as in recent report from Michael Glaesemann. To fix this, make sure that we do both the stat and the unlink before doing anything that could conceivably CHECK_FOR_INTERRUPTS. There is a judgment call here, which is which log message to emit first: if you can see only one, which should it be? I chose to log unlink failure at the risk of losing the log_temp_files log message --- after all, if the unlink does fail, the temp file is still there for you to see. Back-patch to all versions that have log_temp_files. The code was OK before that.
1 parent 0f02949 commit c6c6750

File tree

1 file changed

+33
-6
lines changed
  • src/backend/storage/file

1 file changed

+33
-6
lines changed

src/backend/storage/file/fd.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,6 @@ void
10291029
FileClose(File file)
10301030
{
10311031
Vfd *vfdP;
1032-
struct stat filestats;
10331032

10341033
Assert(FileIsValid(file));
10351034

@@ -1052,15 +1051,36 @@ FileClose(File file)
10521051
}
10531052

10541053
/*
1055-
* Delete the file if it was temporary
1054+
* Delete the file if it was temporary, and make a log entry if wanted
10561055
*/
10571056
if (vfdP->fdstate & FD_TEMPORARY)
10581057
{
1059-
/* reset flag so that die() interrupt won't cause problems */
1058+
/*
1059+
* If we get an error, as could happen within the ereport/elog calls,
1060+
* we'll come right back here during transaction abort. Reset the
1061+
* flag to ensure that we can't get into an infinite loop. This code
1062+
* is arranged to ensure that the worst-case consequence is failing
1063+
* to emit log message(s), not failing to attempt the unlink.
1064+
*/
10601065
vfdP->fdstate &= ~FD_TEMPORARY;
1066+
10611067
if (log_temp_files >= 0)
10621068
{
1063-
if (stat(vfdP->fileName, &filestats) == 0)
1069+
struct stat filestats;
1070+
int stat_errno;
1071+
1072+
/* first try the stat() */
1073+
if (stat(vfdP->fileName, &filestats))
1074+
stat_errno = errno;
1075+
else
1076+
stat_errno = 0;
1077+
1078+
/* in any case do the unlink */
1079+
if (unlink(vfdP->fileName))
1080+
elog(LOG, "could not unlink file \"%s\": %m", vfdP->fileName);
1081+
1082+
/* and last report the stat results */
1083+
if (stat_errno == 0)
10641084
{
10651085
if ((filestats.st_size / 1024) >= log_temp_files)
10661086
ereport(LOG,
@@ -1069,10 +1089,17 @@ FileClose(File file)
10691089
(unsigned long) filestats.st_size)));
10701090
}
10711091
else
1092+
{
1093+
errno = stat_errno;
10721094
elog(LOG, "could not stat file \"%s\": %m", vfdP->fileName);
1095+
}
1096+
}
1097+
else
1098+
{
1099+
/* easy case, just do the unlink */
1100+
if (unlink(vfdP->fileName))
1101+
elog(LOG, "could not unlink file \"%s\": %m", vfdP->fileName);
10731102
}
1074-
if (unlink(vfdP->fileName))
1075-
elog(LOG, "could not unlink file \"%s\": %m", vfdP->fileName);
10761103
}
10771104

10781105
/* Unregister it from the resource owner */

0 commit comments

Comments
 (0)