diff --git a/contrib/dbsize/dbsize.c b/contrib/dbsize/dbsize.c index 0037c14e706da940d266d5dd4499d0e07584a051..7b976822371ff94fddaa55fd8b465edf086cf8aa 100644 --- a/contrib/dbsize/dbsize.c +++ b/contrib/dbsize/dbsize.c @@ -1,16 +1,15 @@ #include "postgres.h" #include <sys/types.h> -#include <dirent.h> #include <sys/stat.h> #include <unistd.h> -#include <errno.h> #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/namespace.h" #include "commands/dbcommands.h" #include "fmgr.h" +#include "storage/fd.h" #include "utils/builtins.h" @@ -58,7 +57,7 @@ database_size(PG_FUNCTION_ARGS) dbpath = GetDatabasePath(dbid); - dirdesc = opendir(dbpath); + dirdesc = AllocateDir(dbpath); if (!dirdesc) ereport(ERROR, (errcode_for_file_access(), @@ -93,7 +92,7 @@ database_size(PG_FUNCTION_ARGS) pfree(fullname); } - closedir(dirdesc); + FreeDir(dirdesc); PG_RETURN_INT64(totalsize); } diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index aac93a60a3f7dbd1bcbb58fa8d6a38a5e659dba3..a12fe88556346acb4726b91a438add4b48387765 100644 --- a/src/backend/access/transam/slru.c +++ b/src/backend/access/transam/slru.c @@ -6,15 +6,13 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * 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 <fcntl.h> -#include <dirent.h> -#include <errno.h> #include <sys/stat.h> #include <unistd.h> @@ -888,7 +886,7 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) int segpage; char path[MAXPGPATH]; - cldir = opendir(ctl->Dir); + cldir = AllocateDir(ctl->Dir); if (cldir == NULL) ereport(ERROR, (errcode_for_file_access(), @@ -927,7 +925,7 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read directory \"%s\": %m", ctl->Dir))); - closedir(cldir); + FreeDir(cldir); return found; } diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index dc6fea9b5bb9ebc85bf9ba57344345997e36dc30..821b504cbf7a2f4fe6f7ed3286a7ddc8c53ce4cd 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * 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 @@ #include <fcntl.h> #include <signal.h> #include <unistd.h> -#include <errno.h> #include <sys/stat.h> #include <sys/time.h> -#include <dirent.h> #include "access/clog.h" #include "access/transam.h" @@ -1753,9 +1751,9 @@ MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr) XLByteToPrevSeg(endptr, endlogId, endlogSeg); - xldir = opendir(XLogDir); + xldir = AllocateDir(XLogDir); if (xldir == NULL) - ereport(PANIC, + ereport(ERROR, (errcode_for_file_access(), errmsg("could not open transaction log directory \"%s\": %m", XLogDir))); @@ -1812,11 +1810,11 @@ MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr) errno = 0; #endif if (errno) - ereport(PANIC, + ereport(ERROR, (errcode_for_file_access(), errmsg("could not read transaction log directory \"%s\": %m", XLogDir))); - closedir(xldir); + FreeDir(xldir); } /* diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index 2446f97b653f9ed9517e3eb2ef00be59bf71d1ce..5ef12de949518be73314b6341308886733f6b730 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * 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: * @@ -43,8 +43,6 @@ #include <sys/file.h> #include <sys/param.h> #include <sys/stat.h> -#include <dirent.h> -#include <errno.h> #include <unistd.h> #include <fcntl.h> @@ -87,11 +85,11 @@ int max_files_per_process = 1000; /* * Maximum number of file descriptors to open for either VFD entries or - * AllocateFile files. This is initialized to a conservative value, and - * remains that way indefinitely in bootstrap or standalone-backend cases. - * In normal postmaster operation, the postmaster calls set_max_safe_fds() - * late in initialization to update the value, and that value is then - * inherited by forked subprocesses. + * AllocateFile/AllocateDir operations. This is initialized to a conservative + * value, and remains that way indefinitely in bootstrap or standalone-backend + * cases. In normal postmaster operation, the postmaster calls + * set_max_safe_fds() late in initialization to update the value, and that + * value is then inherited by forked subprocesses. * * Note: the value of max_files_per_process is taken into account while * setting this variable, and so need not be tested separately. @@ -159,6 +157,17 @@ static int nfile = 0; static int numAllocatedFiles = 0; 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; * this is used in generation of tempfile names. @@ -489,7 +498,7 @@ LruInsert(File file) if (FileIsNotOpen(file)) { - while (nfile + numAllocatedFiles >= max_safe_fds) + while (nfile + numAllocatedFiles + numAllocatedDirs >= max_safe_fds) { if (!ReleaseLruFile()) break; @@ -748,7 +757,7 @@ fileNameOpenFile(FileName fileName, file = AllocateVfd(); vfdP = &VfdCache[file]; - while (nfile + numAllocatedFiles >= max_safe_fds) + while (nfile + numAllocatedFiles + numAllocatedDirs >= max_safe_fds) { if (!ReleaseLruFile()) break; @@ -1099,8 +1108,8 @@ AllocateFile(char *name, char *mode) * looping. */ if (numAllocatedFiles >= MAX_ALLOCATED_FILES || - numAllocatedFiles >= max_safe_fds - 1) - elog(ERROR, "too many private FDs demanded"); + numAllocatedFiles + numAllocatedDirs >= max_safe_fds - 1) + elog(ERROR, "too many private files demanded"); TryAgain: if ((file = fopen(name, mode)) != NULL) @@ -1155,6 +1164,86 @@ FreeFile(FILE *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 * @@ -1211,7 +1300,7 @@ AtProcExit_Files(int code, Datum arg) * 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 * 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 CleanupTempFiles(bool isProcExit) @@ -1240,6 +1329,9 @@ CleanupTempFiles(bool isProcExit) while (numAllocatedFiles > 0) FreeFile(allocatedFiles[0]); + + while (numAllocatedDirs > 0) + FreeDir(allocatedDirs[0]); } @@ -1271,7 +1363,7 @@ RemovePgTempFiles(void) * files. */ 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) { @@ -1287,7 +1379,7 @@ RemovePgTempFiles(void) "%s/%s/%s", db_path, db_de->d_name, 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) { @@ -1310,9 +1402,9 @@ RemovePgTempFiles(void) "unexpected file found in temporary-files directory: \"%s\"", rm_path); } - closedir(temp_dir); + FreeDir(temp_dir); } } - closedir(db_dir); + FreeDir(db_dir); } } diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h index feca2b92b197c79e38501c7d9b0c5653b7b9c531..177925cf3e80776dbc34dd543ca8e36b5fcbea76 100644 --- a/src/include/storage/fd.h +++ b/src/include/storage/fd.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * 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 @@ * 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 * 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 #define FD_H +#include <dirent.h> + + /* * FileSeek uses the standard UNIX lseek(2) flags. */ @@ -65,7 +71,11 @@ extern int FileTruncate(File file, long offset); /* Operations that allow use of regular stdio --- USE WITH CAUTION */ 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 */ extern int BasicOpenFile(FileName fileName, int fileFlags, int fileMode); diff --git a/src/port/copydir.c b/src/port/copydir.c index 64a8407a7f83d7ea082307e7d2d4f111e495541d..7d33d14c82bd1ea772866ae1a7018448f5282cc3 100644 --- a/src/port/copydir.c +++ b/src/port/copydir.c @@ -11,18 +11,18 @@ * as a service. * * 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 "storage/fd.h" + #undef mkdir /* no reason to use that macro because we * ignore the 2nd arg */ -#include <dirent.h> - /* * copydir: copy a directory (we only need to go one level deep) @@ -47,7 +47,7 @@ copydir(char *fromdir, char *todir) errmsg("could not create directory \"%s\": %m", todir))); return -1; } - xldir = opendir(fromdir); + xldir = AllocateDir(fromdir); if (xldir == NULL) { ereport(WARNING, @@ -65,11 +65,11 @@ copydir(char *fromdir, char *todir) ereport(WARNING, (errcode_for_file_access(), errmsg("could not copy file \"%s\": %m", fromfl))); - closedir(xldir); + FreeDir(xldir); return -1; } } - closedir(xldir); + FreeDir(xldir); return 0; }