Commit 7a57a672 authored by Tom Lane's avatar Tom Lane

Replace opendir/closedir calls throughout the backend with AllocateDir

and FreeDir routines modeled on the existing AllocateFile/FreeFile.
Like the latter, these routines will avoid failing on EMFILE/ENFILE
conditions whenever possible, and will prevent leakage of directory
descriptors if an elog() occurs while one is open.
Also, reduce PANIC to ERROR in MoveOfflineLogs() --- this is not
critical code and there is no reason to force a DB restart on failure.
All per recent trouble report from Olivier Hubaut.
parent 4f571319
#include "postgres.h" #include "postgres.h"
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "fmgr.h" #include "fmgr.h"
#include "storage/fd.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -58,7 +57,7 @@ database_size(PG_FUNCTION_ARGS) ...@@ -58,7 +57,7 @@ database_size(PG_FUNCTION_ARGS)
dbpath = GetDatabasePath(dbid); dbpath = GetDatabasePath(dbid);
dirdesc = opendir(dbpath); dirdesc = AllocateDir(dbpath);
if (!dirdesc) if (!dirdesc)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
...@@ -93,7 +92,7 @@ database_size(PG_FUNCTION_ARGS) ...@@ -93,7 +92,7 @@ database_size(PG_FUNCTION_ARGS)
pfree(fullname); pfree(fullname);
} }
closedir(dirdesc); FreeDir(dirdesc);
PG_RETURN_INT64(totalsize); PG_RETURN_INT64(totalsize);
} }
......
...@@ -6,15 +6,13 @@ ...@@ -6,15 +6,13 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.12 2004/02/17 03:45:17 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.13 2004/02/23 23:03:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
...@@ -888,7 +886,7 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) ...@@ -888,7 +886,7 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions)
int segpage; int segpage;
char path[MAXPGPATH]; char path[MAXPGPATH];
cldir = opendir(ctl->Dir); cldir = AllocateDir(ctl->Dir);
if (cldir == NULL) if (cldir == NULL)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
...@@ -927,7 +925,7 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) ...@@ -927,7 +925,7 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not read directory \"%s\": %m", ctl->Dir))); errmsg("could not read directory \"%s\": %m", ctl->Dir)));
closedir(cldir); FreeDir(cldir);
return found; return found;
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.136 2004/02/17 03:45:17 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.137 2004/02/23 23:03:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,10 +17,8 @@ ...@@ -17,10 +17,8 @@
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <dirent.h>
#include "access/clog.h" #include "access/clog.h"
#include "access/transam.h" #include "access/transam.h"
...@@ -1753,9 +1751,9 @@ MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr) ...@@ -1753,9 +1751,9 @@ MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr)
XLByteToPrevSeg(endptr, endlogId, endlogSeg); XLByteToPrevSeg(endptr, endlogId, endlogSeg);
xldir = opendir(XLogDir); xldir = AllocateDir(XLogDir);
if (xldir == NULL) if (xldir == NULL)
ereport(PANIC, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not open transaction log directory \"%s\": %m", errmsg("could not open transaction log directory \"%s\": %m",
XLogDir))); XLogDir)));
...@@ -1812,11 +1810,11 @@ MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr) ...@@ -1812,11 +1810,11 @@ MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr)
errno = 0; errno = 0;
#endif #endif
if (errno) if (errno)
ereport(PANIC, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not read transaction log directory \"%s\": %m", errmsg("could not read transaction log directory \"%s\": %m",
XLogDir))); XLogDir)));
closedir(xldir); FreeDir(xldir);
} }
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.107 2004/02/23 20:45:59 tgl Exp $ * $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.108 2004/02/23 23:03:10 tgl Exp $
* *
* NOTES: * NOTES:
* *
...@@ -43,8 +43,6 @@ ...@@ -43,8 +43,6 @@
#include <sys/file.h> #include <sys/file.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -87,11 +85,11 @@ int max_files_per_process = 1000; ...@@ -87,11 +85,11 @@ int max_files_per_process = 1000;
/* /*
* Maximum number of file descriptors to open for either VFD entries or * Maximum number of file descriptors to open for either VFD entries or
* AllocateFile files. This is initialized to a conservative value, and * AllocateFile/AllocateDir operations. This is initialized to a conservative
* remains that way indefinitely in bootstrap or standalone-backend cases. * value, and remains that way indefinitely in bootstrap or standalone-backend
* In normal postmaster operation, the postmaster calls set_max_safe_fds() * cases. In normal postmaster operation, the postmaster calls
* late in initialization to update the value, and that value is then * set_max_safe_fds() late in initialization to update the value, and that
* inherited by forked subprocesses. * value is then inherited by forked subprocesses.
* *
* Note: the value of max_files_per_process is taken into account while * Note: the value of max_files_per_process is taken into account while
* setting this variable, and so need not be tested separately. * setting this variable, and so need not be tested separately.
...@@ -159,6 +157,17 @@ static int nfile = 0; ...@@ -159,6 +157,17 @@ static int nfile = 0;
static int numAllocatedFiles = 0; static int numAllocatedFiles = 0;
static FILE *allocatedFiles[MAX_ALLOCATED_FILES]; static FILE *allocatedFiles[MAX_ALLOCATED_FILES];
/*
* List of <dirent.h> DIRs opened with AllocateDir.
*
* Since we don't have heavy use of AllocateDir, it seems OK to put a pretty
* small maximum limit on the number of simultaneously allocated dirs.
*/
#define MAX_ALLOCATED_DIRS 10
static int numAllocatedDirs = 0;
static DIR *allocatedDirs[MAX_ALLOCATED_DIRS];
/* /*
* Number of temporary files opened during the current session; * Number of temporary files opened during the current session;
* this is used in generation of tempfile names. * this is used in generation of tempfile names.
...@@ -489,7 +498,7 @@ LruInsert(File file) ...@@ -489,7 +498,7 @@ LruInsert(File file)
if (FileIsNotOpen(file)) if (FileIsNotOpen(file))
{ {
while (nfile + numAllocatedFiles >= max_safe_fds) while (nfile + numAllocatedFiles + numAllocatedDirs >= max_safe_fds)
{ {
if (!ReleaseLruFile()) if (!ReleaseLruFile())
break; break;
...@@ -748,7 +757,7 @@ fileNameOpenFile(FileName fileName, ...@@ -748,7 +757,7 @@ fileNameOpenFile(FileName fileName,
file = AllocateVfd(); file = AllocateVfd();
vfdP = &VfdCache[file]; vfdP = &VfdCache[file];
while (nfile + numAllocatedFiles >= max_safe_fds) while (nfile + numAllocatedFiles + numAllocatedDirs >= max_safe_fds)
{ {
if (!ReleaseLruFile()) if (!ReleaseLruFile())
break; break;
...@@ -1099,8 +1108,8 @@ AllocateFile(char *name, char *mode) ...@@ -1099,8 +1108,8 @@ AllocateFile(char *name, char *mode)
* looping. * looping.
*/ */
if (numAllocatedFiles >= MAX_ALLOCATED_FILES || if (numAllocatedFiles >= MAX_ALLOCATED_FILES ||
numAllocatedFiles >= max_safe_fds - 1) numAllocatedFiles + numAllocatedDirs >= max_safe_fds - 1)
elog(ERROR, "too many private FDs demanded"); elog(ERROR, "too many private files demanded");
TryAgain: TryAgain:
if ((file = fopen(name, mode)) != NULL) if ((file = fopen(name, mode)) != NULL)
...@@ -1155,6 +1164,86 @@ FreeFile(FILE *file) ...@@ -1155,6 +1164,86 @@ FreeFile(FILE *file)
return fclose(file); return fclose(file);
} }
/*
* Routines that want to use <dirent.h> (ie, DIR*) should use AllocateDir
* rather than plain opendir(). This lets fd.c deal with freeing FDs if
* necessary to open the directory, and with closing it after an elog.
* When done, call FreeDir rather than closedir.
*
* Ideally this should be the *only* direct call of opendir() in the backend.
*/
DIR *
AllocateDir(const char *dirname)
{
DIR *dir;
DO_DB(elog(LOG, "AllocateDir: Allocated %d", numAllocatedDirs));
/*
* The test against MAX_ALLOCATED_DIRS prevents us from overflowing
* allocatedDirs[]; the test against max_safe_fds prevents AllocateDir
* from hogging every one of the available FDs, which'd lead to infinite
* looping.
*/
if (numAllocatedDirs >= MAX_ALLOCATED_DIRS ||
numAllocatedDirs + numAllocatedFiles >= max_safe_fds - 1)
elog(ERROR, "too many private dirs demanded");
TryAgain:
if ((dir = opendir(dirname)) != NULL)
{
allocatedDirs[numAllocatedDirs] = dir;
numAllocatedDirs++;
return dir;
}
if (errno == EMFILE || errno == ENFILE)
{
int save_errno = errno;
ereport(LOG,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("out of file descriptors: %m; release and retry")));
errno = 0;
if (ReleaseLruFile())
goto TryAgain;
errno = save_errno;
}
return NULL;
}
/*
* Close a directory opened with AllocateDir.
*
* Note we do not check closedir's return value --- it is up to the caller
* to handle close errors.
*/
int
FreeDir(DIR *dir)
{
int i;
DO_DB(elog(LOG, "FreeDir: Allocated %d", numAllocatedDirs));
/* Remove dir from list of allocated dirs, if it's present */
for (i = numAllocatedDirs; --i >= 0;)
{
if (allocatedDirs[i] == dir)
{
numAllocatedDirs--;
allocatedDirs[i] = allocatedDirs[numAllocatedDirs];
break;
}
}
if (i < 0)
elog(WARNING, "dir passed to FreeDir was not obtained from AllocateDir");
return closedir(dir);
}
/* /*
* closeAllVfds * closeAllVfds
* *
...@@ -1211,7 +1300,7 @@ AtProcExit_Files(int code, Datum arg) ...@@ -1211,7 +1300,7 @@ AtProcExit_Files(int code, Datum arg)
* exiting. If that's the case, we should remove all temporary files; if * exiting. If that's the case, we should remove all temporary files; if
* that's not the case, we are being called for transaction commit/abort * that's not the case, we are being called for transaction commit/abort
* and should only remove transaction-local temp files. In either case, * and should only remove transaction-local temp files. In either case,
* also clean up "allocated" stdio files. * also clean up "allocated" stdio files and dirs.
*/ */
static void static void
CleanupTempFiles(bool isProcExit) CleanupTempFiles(bool isProcExit)
...@@ -1240,6 +1329,9 @@ CleanupTempFiles(bool isProcExit) ...@@ -1240,6 +1329,9 @@ CleanupTempFiles(bool isProcExit)
while (numAllocatedFiles > 0) while (numAllocatedFiles > 0)
FreeFile(allocatedFiles[0]); FreeFile(allocatedFiles[0]);
while (numAllocatedDirs > 0)
FreeDir(allocatedDirs[0]);
} }
...@@ -1271,7 +1363,7 @@ RemovePgTempFiles(void) ...@@ -1271,7 +1363,7 @@ RemovePgTempFiles(void)
* files. * files.
*/ */
snprintf(db_path, sizeof(db_path), "%s/base", DataDir); snprintf(db_path, sizeof(db_path), "%s/base", DataDir);
if ((db_dir = opendir(db_path)) != NULL) if ((db_dir = AllocateDir(db_path)) != NULL)
{ {
while ((db_de = readdir(db_dir)) != NULL) while ((db_de = readdir(db_dir)) != NULL)
{ {
...@@ -1287,7 +1379,7 @@ RemovePgTempFiles(void) ...@@ -1287,7 +1379,7 @@ RemovePgTempFiles(void)
"%s/%s/%s", "%s/%s/%s",
db_path, db_de->d_name, db_path, db_de->d_name,
PG_TEMP_FILES_DIR); PG_TEMP_FILES_DIR);
if ((temp_dir = opendir(temp_path)) != NULL) if ((temp_dir = AllocateDir(temp_path)) != NULL)
{ {
while ((temp_de = readdir(temp_dir)) != NULL) while ((temp_de = readdir(temp_dir)) != NULL)
{ {
...@@ -1310,9 +1402,9 @@ RemovePgTempFiles(void) ...@@ -1310,9 +1402,9 @@ RemovePgTempFiles(void)
"unexpected file found in temporary-files directory: \"%s\"", "unexpected file found in temporary-files directory: \"%s\"",
rm_path); rm_path);
} }
closedir(temp_dir); FreeDir(temp_dir);
} }
} }
closedir(db_dir); FreeDir(db_dir);
} }
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.43 2004/02/23 20:45:59 tgl Exp $ * $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.44 2004/02/23 23:03:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -31,10 +31,16 @@ ...@@ -31,10 +31,16 @@
* use FreeFile, not fclose, to close it. AVOID using stdio for files * use FreeFile, not fclose, to close it. AVOID using stdio for files
* that you intend to hold open for any length of time, since there is * that you intend to hold open for any length of time, since there is
* no way for them to share kernel file descriptors with other files. * no way for them to share kernel file descriptors with other files.
*
* Likewise, use AllocateDir/FreeDir, not opendir/closedir, to allocate
* open directories (DIR*).
*/ */
#ifndef FD_H #ifndef FD_H
#define FD_H #define FD_H
#include <dirent.h>
/* /*
* FileSeek uses the standard UNIX lseek(2) flags. * FileSeek uses the standard UNIX lseek(2) flags.
*/ */
...@@ -65,7 +71,11 @@ extern int FileTruncate(File file, long offset); ...@@ -65,7 +71,11 @@ extern int FileTruncate(File file, long offset);
/* Operations that allow use of regular stdio --- USE WITH CAUTION */ /* Operations that allow use of regular stdio --- USE WITH CAUTION */
extern FILE *AllocateFile(char *name, char *mode); extern FILE *AllocateFile(char *name, char *mode);
extern int FreeFile(FILE *); extern int FreeFile(FILE *file);
/* Operations to allow use of the <dirent.h> library routines */
extern DIR *AllocateDir(const char *dirname);
extern int FreeDir(DIR *dir);
/* If you've really really gotta have a plain kernel FD, use this */ /* If you've really really gotta have a plain kernel FD, use this */
extern int BasicOpenFile(FileName fileName, int fileFlags, int fileMode); extern int BasicOpenFile(FileName fileName, int fileFlags, int fileMode);
......
...@@ -11,18 +11,18 @@ ...@@ -11,18 +11,18 @@
* as a service. * as a service.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/port/copydir.c,v 1.7 2003/11/29 19:52:13 pgsql Exp $ * $PostgreSQL: pgsql/src/port/copydir.c,v 1.8 2004/02/23 23:03:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "storage/fd.h"
#undef mkdir /* no reason to use that macro because we #undef mkdir /* no reason to use that macro because we
* ignore the 2nd arg */ * ignore the 2nd arg */
#include <dirent.h>
/* /*
* copydir: copy a directory (we only need to go one level deep) * copydir: copy a directory (we only need to go one level deep)
...@@ -47,7 +47,7 @@ copydir(char *fromdir, char *todir) ...@@ -47,7 +47,7 @@ copydir(char *fromdir, char *todir)
errmsg("could not create directory \"%s\": %m", todir))); errmsg("could not create directory \"%s\": %m", todir)));
return -1; return -1;
} }
xldir = opendir(fromdir); xldir = AllocateDir(fromdir);
if (xldir == NULL) if (xldir == NULL)
{ {
ereport(WARNING, ereport(WARNING,
...@@ -65,11 +65,11 @@ copydir(char *fromdir, char *todir) ...@@ -65,11 +65,11 @@ copydir(char *fromdir, char *todir)
ereport(WARNING, ereport(WARNING,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not copy file \"%s\": %m", fromfl))); errmsg("could not copy file \"%s\": %m", fromfl)));
closedir(xldir); FreeDir(xldir);
return -1; return -1;
} }
} }
closedir(xldir); FreeDir(xldir);
return 0; return 0;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment